Explorar el Código

More Omnipod pump manager related fixes and improvements (#429)

+ Fix implementation bug in PodInfoPulseLogPlus limiting pod time to 9 bits
+ Fix pod pump managers not to crash if an incorrect response type is returned
+ Add safeguards for possible edge cases to prevent 0x31 faults before pod is set up
+ Adjust activationTimeString to better match pulseLogPlusString's output
Joe Moran hace 2 años
padre
commit
16692b9d14

+ 3 - 3
Dependencies/OmniBLE/OmniBLE/OmnipodCommon/MessageBlocks/PodInfoActivationTime.swift

@@ -36,7 +36,7 @@ public struct PodInfoActivationTime : PodInfo {
         self.day    = Int(encodedData[13])
         self.hour   = Int(encodedData[15])
         self.minute = Int(encodedData[16])
-        self.data = Data(encodedData)
+        self.data   = Data(encodedData)
     }
 }
 
@@ -51,8 +51,8 @@ func activationTimeString(podInfoActivationTime: PodInfoActivationTime) -> Strin
     result.append(String(format: "Minute: %u", podInfoActivationTime.minute))
 
     // pod fault info
-    result.append(String(format: "\n%@", String(describing: podInfoActivationTime.faultEventCode)))
-    result.append(String(format: "Fault Time: %@", podInfoActivationTime.faultTime.timeIntervalStr))
+    result.append(String(format: "\nFault Time: %@", podInfoActivationTime.faultTime.timeIntervalStr))
+    result.append(String(describing: podInfoActivationTime.faultEventCode))
 
     return result.joined(separator: "\n")
 }

+ 2 - 2
Dependencies/OmniBLE/OmniBLE/OmnipodCommon/MessageBlocks/PodInfoPulseLogPlus.swift

@@ -44,8 +44,8 @@ public struct PodInfoPulseLogPlus : PodInfo {
         self.nEntries = nEntries
         self.maxEntries = maxEntries
         self.faultEventCode = FaultEventCode(rawValue: encodedData[1])
-        self.timeFaultEvent = TimeInterval(minutes: Double((Int(encodedData[2] & 0b1) << 8) + Int(encodedData[3])))
-        self.timeActivation = TimeInterval(minutes: Double((Int(encodedData[4] & 0b1) << 8) + Int(encodedData[5])))
+        self.timeFaultEvent = TimeInterval(minutes: Double((Int(encodedData[2]) << 8) + Int(encodedData[3])))
+        self.timeActivation = TimeInterval(minutes: Double((Int(encodedData[4]) << 8) + Int(encodedData[5])))
         self.pulseLog = createPulseLog(encodedData: encodedData, logStartByteOffset: logStartByteOffset, nEntries: self.nEntries)
         self.data = encodedData
     }

+ 36 - 8
Dependencies/OmniBLE/OmniBLE/PumpManager/OmniBLEPumpManager.swift

@@ -32,6 +32,7 @@ public enum OmniBLEPumpManagerError: Error {
     case insulinTypeNotConfigured
     case notReadyForCannulaInsertion
     case invalidSetting
+    case setupNotComplete
     case communication(Error)
     case state(Error)
 }
@@ -47,6 +48,10 @@ extension OmniBLEPumpManagerError: LocalizedError {
             return LocalizedString("Insulin type not configured", comment: "Error description for OmniBLEPumpManagerError.insulinTypeNotConfigured")
         case .notReadyForCannulaInsertion:
             return LocalizedString("Pod is not in a state ready for cannula insertion.", comment: "Error message when cannula insertion fails because the pod is in an unexpected state")
+        case .invalidSetting:
+            return LocalizedString("Invalid Setting", comment: "Error description for invalid setting")
+        case .setupNotComplete:
+            return LocalizedString("Pod setup is not complete", comment: "Error description when pod setup is not complete")
         case .communication(let error):
             if let error = error as? LocalizedError {
                 return error.errorDescription
@@ -59,8 +64,6 @@ extension OmniBLEPumpManagerError: LocalizedError {
             } else {
                 return String(describing: error)
             }
-        case .invalidSetting:
-            return LocalizedString("Invalid Setting", comment: "Error description for OmniBLEPumpManagerError.invalidSetting")
         }
     }
 
@@ -1079,6 +1082,12 @@ extension OmniBLEPumpManager {
             return
         }
 
+        guard state.podState?.setupProgress == .completed else {
+            // A cancel delivery command before pod setup is complete will fault the pod
+            completion(.state(OmniBLEPumpManagerError.setupNotComplete))
+            return
+        }
+
         guard state.podState?.unfinalizedBolus?.isFinished() != false else {
             completion(.state(PodCommsError.unfinalizedBolus))
             return
@@ -1113,6 +1122,11 @@ extension OmniBLEPumpManager {
                 return .success(false)
             }
 
+            guard state.podState?.setupProgress == .completed else {
+                // A cancel delivery command before pod setup is complete will fault the pod
+                return .failure(PumpManagerError.deviceState(OmniBLEPumpManagerError.setupNotComplete))
+            }
+
             guard state.podState?.unfinalizedBolus?.isFinished() != false else {
                 return .failure(.deviceState(PodCommsError.unfinalizedBolus))
             }
@@ -1250,9 +1264,8 @@ extension OmniBLEPumpManager {
                     let beepBlock = self.beepMessageBlock(beepType: .bipBeeeeep)
                     let podInfoResponse = try session.readPodInfo(podInfoResponseSubType: .pulseLogRecent, beepBlock: beepBlock)
                     guard let podInfoPulseLogRecent = podInfoResponse.podInfo as? PodInfoPulseLogRecent else {
-                        self.log.error("Unable to decode PulseLogRecent: %s", String(describing: podInfoResponse))
-                        completion(.failure(PodCommsError.unexpectedResponse(response: .podInfoResponse)))
-                        return
+                        self.log.error("Unable to decode Pulse Log: %s", String(describing: podInfoResponse))
+                        throw PodCommsError.unexpectedResponse(response: .podInfoResponse)
                     }
                     let lastPulseNumber = Int(podInfoPulseLogRecent.indexLastEntry)
                     let str = pulseLogString(pulseLogEntries: podInfoPulseLogRecent.pulseLog, lastPulseNumber: lastPulseNumber)
@@ -1285,7 +1298,10 @@ extension OmniBLEPumpManager {
                 case .success(let session):
                     let beepBlock = self.beepMessageBlock(beepType: .bipBeeeeep)
                     let podInfoResponse = try session.readPodInfo(podInfoResponseSubType: .pulseLogPlus, beepBlock: beepBlock)
-                    let podInfoPulseLogPlus = podInfoResponse.podInfo as! PodInfoPulseLogPlus
+                    guard let podInfoPulseLogPlus = podInfoResponse.podInfo as? PodInfoPulseLogPlus else {
+                        self.log.error("Unable to decode Pulse Log Plus: %s", String(describing: podInfoResponse))
+                        throw PodCommsError.unexpectedResponse(response: .podInfoResponse)
+                    }
                     let str = pulseLogPlusString(podInfoPulseLogPlus: podInfoPulseLogPlus)
                     completion(.success(str))
                 case .failure(let error):
@@ -1310,7 +1326,10 @@ extension OmniBLEPumpManager {
                 case .success(let session):
                     let beepBlock = self.beepMessageBlock(beepType: .beepBeep)
                     let podInfoResponse = try session.readPodInfo(podInfoResponseSubType: .activationTime, beepBlock: beepBlock)
-                    let podInfoActivationTime = podInfoResponse.podInfo as! PodInfoActivationTime
+                    guard let podInfoActivationTime = podInfoResponse.podInfo as? PodInfoActivationTime else {
+                        self.log.error("Unable to decode Activation Time: %s", String(describing: podInfoResponse))
+                        throw PodCommsError.unexpectedResponse(response: .podInfoResponse)
+                    }
                     let str = activationTimeString(podInfoActivationTime: podInfoActivationTime)
                     completion(.success(str))
                 case .failure(let error):
@@ -1335,7 +1354,10 @@ extension OmniBLEPumpManager {
                 case .success(let session):
                     let beepBlock = self.beepMessageBlock(beepType: .beepBeep)
                     let podInfoResponse = try session.readPodInfo(podInfoResponseSubType: .triggeredAlerts, beepBlock: beepBlock)
-                    let podInfoTriggeredAlerts = podInfoResponse.podInfo as! PodInfoTriggeredAlerts
+                    guard let podInfoTriggeredAlerts = podInfoResponse.podInfo as? PodInfoTriggeredAlerts else {
+                        self.log.error("Unable to decode Read Triggered Alerts: %s", String(describing: podInfoResponse))
+                        throw PodCommsError.unexpectedResponse(response: .podInfoResponse)
+                    }
                     let str = triggeredAlertsString(podInfoTriggeredAlerts: podInfoTriggeredAlerts)
                     completion(.success(str))
                 case .failure(let error):
@@ -1795,6 +1817,12 @@ extension OmniBLEPumpManager: PumpManager {
             return
         }
 
+        guard state.podState?.setupProgress == .completed else {
+            // A cancel delivery command before pod setup is complete will fault the pod
+            completion(.failure(PumpManagerError.deviceState(OmniBLEPumpManagerError.setupNotComplete)))
+            return
+        }
+
         self.podComms.runSession(withName: "Cancel Bolus") { (result) in
 
             let session: PodCommsSession

+ 3 - 3
Dependencies/OmniKit/OmniKit/OmnipodCommon/MessageBlocks/PodInfoActivationTime.swift

@@ -35,7 +35,7 @@ public struct PodInfoActivationTime : PodInfo {
         self.day    = Int(encodedData[13])
         self.hour   = Int(encodedData[15])
         self.minute = Int(encodedData[16])
-        self.data = Data(encodedData)
+        self.data   = Data(encodedData)
     }
 }
 
@@ -50,8 +50,8 @@ func activationTimeString(podInfoActivationTime: PodInfoActivationTime) -> Strin
     result.append(String(format: "Minute: %u", podInfoActivationTime.minute))
 
     // pod fault info
-    result.append(String(format: "\n%@", String(describing: podInfoActivationTime.faultEventCode)))
-    result.append(String(format: "Fault Time: %@", podInfoActivationTime.faultTime.timeIntervalStr))
+    result.append(String(format: "\nFault Time: %@", podInfoActivationTime.faultTime.timeIntervalStr))
+    result.append(String(describing: podInfoActivationTime.faultEventCode))
 
     return result.joined(separator: "\n")
 }

+ 2 - 2
Dependencies/OmniKit/OmniKit/OmnipodCommon/MessageBlocks/PodInfoPulseLogPlus.swift

@@ -43,8 +43,8 @@ public struct PodInfoPulseLogPlus : PodInfo {
         self.nEntries = nEntries
         self.maxEntries = maxEntries
         self.faultEventCode = FaultEventCode(rawValue: encodedData[1])
-        self.timeFaultEvent = TimeInterval(minutes: Double((Int(encodedData[2] & 0b1) << 8) + Int(encodedData[3])))
-        self.timeActivation = TimeInterval(minutes: Double((Int(encodedData[4] & 0b1) << 8) + Int(encodedData[5])))
+        self.timeFaultEvent = TimeInterval(minutes: Double((Int(encodedData[2]) << 8) + Int(encodedData[3])))
+        self.timeActivation = TimeInterval(minutes: Double((Int(encodedData[4]) << 8) + Int(encodedData[5])))
         self.pulseLog = createPulseLog(encodedData: encodedData, logStartByteOffset: logStartByteOffset, nEntries: self.nEntries)
         self.data = encodedData
     }

+ 28 - 2
Dependencies/OmniKit/OmniKit/PumpManager/OmnipodPumpManager.swift

@@ -44,6 +44,7 @@ public enum OmnipodPumpManagerError: Error {
     case insulinTypeNotConfigured
     case notReadyForCannulaInsertion
     case invalidSetting
+    case setupNotComplete
     case communication(Error)
     case state(Error)
 }
@@ -59,6 +60,10 @@ extension OmnipodPumpManagerError: LocalizedError {
             return LocalizedString("Insulin type not configured", comment: "Error description for OmniBLEPumpManagerError.insulinTypeNotConfigured")
         case .notReadyForCannulaInsertion:
             return LocalizedString("Pod is not in a state ready for cannula insertion.", comment: "Error message when cannula insertion fails because the pod is in an unexpected state")
+        case .invalidSetting:
+            return LocalizedString("Invalid Setting", comment: "Error description for invalid setting")
+        case .setupNotComplete:
+            return LocalizedString("Pod setup is not complete", comment: "Error description when pod setup is not complete")
         case .communication(let error):
             if let error = error as? LocalizedError {
                 return error.errorDescription
@@ -71,8 +76,6 @@ extension OmnipodPumpManagerError: LocalizedError {
             } else {
                 return String(describing: error)
             }
-        case .invalidSetting:
-            return LocalizedString("Invalid Setting", comment: "Error description for OmniBLEPumpManagerError.invalidSetting")
         }
     }
 
@@ -1021,6 +1024,12 @@ extension OmnipodPumpManager {
             return
         }
 
+        guard state.podState?.setupProgress == .completed else {
+            // A cancel delivery command before pod setup is complete will fault the pod
+            completion(.state(OmnipodPumpManagerError.setupNotComplete))
+            return
+        }
+
         guard state.podState?.unfinalizedBolus?.isFinished() != false else {
             completion(.state(PodCommsError.unfinalizedBolus))
             return
@@ -1055,6 +1064,11 @@ extension OmnipodPumpManager {
                 return .success(false)
             }
 
+            guard state.podState?.setupProgress == .completed else {
+                // A cancel delivery command before pod setup is complete will fault the pod
+                return .failure(PumpManagerError.deviceState(OmnipodPumpManagerError.setupNotComplete))
+            }
+
             guard state.podState?.unfinalizedBolus?.isFinished() != false else {
                 return .failure(PumpManagerError.deviceState(PodCommsError.unfinalizedBolus))
             }
@@ -1799,6 +1813,12 @@ extension OmnipodPumpManager: PumpManager {
             return
         }
 
+        guard state.podState?.setupProgress == .completed else {
+            // A cancel delivery command before pod setup is complete will fault the pod
+            completion(.failure(PumpManagerError.deviceState(OmnipodPumpManagerError.setupNotComplete)))
+            return
+        }
+
         let rileyLinkSelector = self.rileyLinkDeviceProvider.firstConnectedDevice
         self.podComms.runSession(withName: "Cancel Bolus", using: rileyLinkSelector) { (result) in
 
@@ -1864,6 +1884,12 @@ extension OmnipodPumpManager: PumpManager {
             return
         }
 
+        guard state.podState?.setupProgress == .completed else {
+            // A cancel delivery command before pod setup is complete will fault the pod
+            completion(.deviceState(OmnipodPumpManagerError.setupNotComplete))
+            return
+        }
+
         // Legal duration values are [virtual] zero (to cancel current temp basal) or between 30 min and 12 hours
         guard duration < .ulpOfOne || (duration >= .minutes(30) && duration <= .hours(12)) else {
             completion(.deviceState(OmnipodPumpManagerError.invalidSetting))