From f1e15929ad18598723f0ff49a8cf93ed5695edee Mon Sep 17 00:00:00 2001 From: Pete Schwamb Date: Tue, 1 Sep 2020 13:11:28 -0500 Subject: [PATCH] Update DIY PumpManagers to new PumpManager protocol changes (#24) * Update DIY PumpManagers to new PumpManager protocol changes * Update RileyLink app to protocol changes --- .../PumpManager/MinimedPumpManager.swift | 97 +++----- .../PumpManager/MinimedPumpManagerError.swift | 26 +-- MinimedKitUI/MinimedPumpManager+UI.swift | 9 +- OmniKit/PumpManager/OmnipodPumpManager.swift | 207 +++++++++--------- OmniKit/PumpManager/PodCommsSession.swift | 2 + .../PumpManager/OmniPodPumpManager+UI.swift | 9 + RileyLink/DeviceDataManager.swift | 5 +- RileyLinkKit/PumpOpsSession.swift | 66 ++---- 8 files changed, 191 insertions(+), 230 deletions(-) diff --git a/MinimedKit/PumpManager/MinimedPumpManager.swift b/MinimedKit/PumpManager/MinimedPumpManager.swift index 64b18120f..bdf80d3ff 100644 --- a/MinimedKit/PumpManager/MinimedPumpManager.swift +++ b/MinimedKit/PumpManager/MinimedPumpManager.swift @@ -261,6 +261,7 @@ extension MinimedPumpManager { } } + /// - Throws: `PumpCommandError` specifying the failure sequence private func runSuspendResumeOnSession(suspendResumeState: SuspendResumeMessageBody.SuspendResumeState, session: PumpOpsSession) throws { defer { self.recents.suspendEngageState = .stable } self.recents.suspendEngageState = suspendResumeState == .suspend ? .engaging : .disengaging @@ -291,10 +292,10 @@ extension MinimedPumpManager { } } - private func setSuspendResumeState(state: SuspendResumeMessageBody.SuspendResumeState, completion: @escaping (Error?) -> Void) { + private func setSuspendResumeState(state: SuspendResumeMessageBody.SuspendResumeState, completion: @escaping (MinimedPumpManagerError?) -> Void) { rileyLinkDeviceProvider.getDevices { (devices) in guard let device = devices.firstConnected else { - completion(PumpManagerError.connection(MinimedPumpManagerError.noRileyLink)) + completion(MinimedPumpManagerError.noRileyLink) return } @@ -315,7 +316,7 @@ extension MinimedPumpManager { }) } catch let error { self.troubleshootPumpComms(using: device) - completion(PumpManagerError.communication(error as? LocalizedError)) + completion(MinimedPumpManagerError.commsError(error as! PumpCommandError)) } } } @@ -417,7 +418,7 @@ extension MinimedPumpManager { } /// Called on an unknown queue by the delegate - private func pumpManagerDelegateDidProcessReservoirValue(_ result: PumpManagerResult<(newValue: ReservoirValue, lastValue: ReservoirValue?, areStoredValuesContinuous: Bool)>) { + private func pumpManagerDelegateDidProcessReservoirValue(_ result: Result<(newValue: ReservoirValue, lastValue: ReservoirValue?, areStoredValuesContinuous: Bool), Error>) { switch result { case .failure: break @@ -574,7 +575,7 @@ extension MinimedPumpManager { } } - private func storePendingPumpEvents(_ completion: @escaping (_ error: Error?) -> Void) { + private func storePendingPumpEvents(_ completion: @escaping (_ error: MinimedPumpManagerError?) -> Void) { // Must be called from the sessionQueue let events = (self.state.pendingDoses + [self.state.unfinalizedBolus, self.state.unfinalizedTempBasal]).compactMap({ $0?.newPumpEvent }) @@ -587,7 +588,8 @@ extension MinimedPumpManager { delegate.pumpManager(self, hasNewPumpEvents: events, lastReconciliation: self.lastReconciliation, completion: { (error) in // Called on an unknown queue by the delegate - completion(error) + self.log.error("Pump event storage failed: %{public}@", String(describing: error)) + completion(MinimedPumpManagerError.storageFailure) }) }) @@ -869,8 +871,8 @@ extension MinimedPumpManager: PumpManager { } } } - - public func enactBolus(units: Double, at startDate: Date, willRequest: @escaping (_ dose: DoseEntry) -> Void, completion: @escaping (PumpManagerResult) -> Void) { + + public func enactBolus(units: Double, at startDate: Date, completion: @escaping (PumpManagerResult) -> Void) { let enactUnits = roundToSupportedBolusVolume(units: units) guard enactUnits > 0 else { @@ -882,13 +884,13 @@ extension MinimedPumpManager: PumpManager { pumpOps.runSession(withName: "Bolus", using: rileyLinkDeviceProvider.firstConnectedDevice) { (session) in guard let session = session else { - completion(.failure(SetBolusError.certain(PumpManagerError.connection(MinimedPumpManagerError.noRileyLink)))) + completion(.failure(PumpManagerError.connection(MinimedPumpManagerError.noRileyLink))) return } if let unfinalizedBolus = self.state.unfinalizedBolus { guard unfinalizedBolus.isFinished else { - completion(.failure(SetBolusError.certain(PumpManagerError.deviceState(MinimedPumpManagerError.bolusInProgress)))) + completion(.failure(PumpManagerError.deviceState(MinimedPumpManagerError.bolusInProgress))) return } @@ -910,55 +912,28 @@ extension MinimedPumpManager: PumpManager { // Ignore result } }) - } catch let error as PumpOpsError { - self.log.error("Failed to fetch pump status: %{public}@", String(describing: error)) - completion(.failure(SetBolusError.certain(error))) - return - } catch let error as PumpCommandError { - self.log.error("Failed to fetch pump status: %{public}@", String(describing: error)) - switch error { - case .arguments(let error): - completion(.failure(SetBolusError.certain(error))) - case .command(let error): - completion(.failure(SetBolusError.certain(error))) - } - return } catch let error { self.recents.bolusEngageState = .stable - completion(.failure(error)) + self.log.error("Failed to fetch pump status: %{public}@", String(describing: error)) + completion(.failure(PumpManagerError.communication(error as? LocalizedError))) return } } - do { - if case .suspended = self.state.suspendState { - do { - try self.runSuspendResumeOnSession(suspendResumeState: .resume, session: session) - } catch let error as PumpOpsError { - self.log.error("Failed to resume pump for bolus: %{public}@", String(describing: error)) - completion(.failure(SetBolusError.certain(error))) - return - } catch let error as PumpCommandError { - self.log.error("Failed to resume pump for bolus: %{public}@", String(describing: error)) - switch error { - case .arguments(let error): - completion(.failure(SetBolusError.certain(error))) - case .command(let error): - completion(.failure(SetBolusError.certain(error))) - } - return - } catch let error { - self.recents.bolusEngageState = .stable - completion(.failure(error)) - return - } + if case .suspended = self.state.suspendState { + do { + try self.runSuspendResumeOnSession(suspendResumeState: .resume, session: session) + } catch let error { + self.recents.bolusEngageState = .stable + self.log.error("Failed to resume pump for bolus: %{public}@", String(describing: error)) + completion(.failure(PumpManagerError.communication(error as? LocalizedError))) + return } + } - let date = Date() - let deliveryTime = self.state.pumpModel.bolusDeliveryTime(units: enactUnits) - let requestedDose = UnfinalizedDose(bolusAmount: enactUnits, startTime: date, duration: deliveryTime) - willRequest(DoseEntry(requestedDose)) + let deliveryTime = self.state.pumpModel.bolusDeliveryTime(units: enactUnits) + do { try session.setNormalBolus(units: enactUnits) // Between bluetooth and the radio and firmware, about 2s on average passes before we start tracking @@ -977,7 +952,7 @@ extension MinimedPumpManager: PumpManager { } catch let error { self.log.error("Failed to bolus: %{public}@", String(describing: error)) self.recents.bolusEngageState = .stable - completion(.failure(error)) + completion(.failure(PumpManagerError.communication(error as? LocalizedError))) } } } @@ -987,14 +962,13 @@ extension MinimedPumpManager: PumpManager { setSuspendResumeState(state: .suspend) { (error) in self.recents.bolusEngageState = .stable if let error = error { - completion(.failure(error)) + completion(.failure(PumpManagerError.communication(error))) } else { completion(.success(nil)) } } } - - + public func enactTempBasal(unitsPerHour: Double, for duration: TimeInterval, completion: @escaping (PumpManagerResult) -> Void) { pumpOps.runSession(withName: "Set Temp Basal", using: rileyLinkDeviceProvider.firstConnectedDevice) { (session) in guard let session = session else { @@ -1004,9 +978,10 @@ extension MinimedPumpManager: PumpManager { self.recents.tempBasalEngageState = .engaging - do { - let response = try session.setTempBasal(unitsPerHour, duration: duration) - + let result = session.setTempBasal(unitsPerHour, duration: duration) + + switch result { + case .success(let response): let now = Date() let endDate = now.addingTimeInterval(response.timeRemaining) let startDate = endDate.addingTimeInterval(-duration) @@ -1044,8 +1019,8 @@ extension MinimedPumpManager: PumpManager { }) // Continue below - } catch let error as PumpCommandError { - completion(.failure(error)) + case .failure(let error): + completion(.failure(PumpManagerError.communication(error))) // If we got a command-refused error, we might be suspended or bolusing, so update the state accordingly if case .arguments(.pumpError(.commandRefused)) = error { @@ -1063,10 +1038,6 @@ extension MinimedPumpManager: PumpManager { } self.recents.tempBasalEngageState = .stable return - } catch { - self.recents.tempBasalEngageState = .stable - completion(.failure(error)) - return } do { diff --git a/MinimedKit/PumpManager/MinimedPumpManagerError.swift b/MinimedKit/PumpManager/MinimedPumpManagerError.swift index cd1e5ba54..63db67666 100644 --- a/MinimedKit/PumpManager/MinimedPumpManagerError.swift +++ b/MinimedKit/PumpManager/MinimedPumpManagerError.swift @@ -12,6 +12,8 @@ public enum MinimedPumpManagerError: Error { case bolusInProgress case noDate // TODO: This is less of an error and more of a precondition/assertion state case tuneFailed(LocalizedError) + case commsError(LocalizedError) + case storageFailure } @@ -19,26 +21,26 @@ extension MinimedPumpManagerError: LocalizedError { public var errorDescription: String? { switch self { case .noRileyLink: - return nil + return LocalizedString("No RileyLink Connected", comment: "Error description when no rileylink connected") case .bolusInProgress: - return nil + return LocalizedString("Bolus in Progress", comment: "Error description when failure due to bolus in progress") case .noDate: return nil case .tuneFailed(let error): - return [LocalizedString("RileyLink radio tune failed", comment: "Error description"), error.errorDescription].compactMap({ $0 }).joined(separator: ": ") + return [LocalizedString("RileyLink radio tune failed", comment: "Error description for tune failure"), error.errorDescription].compactMap({ $0 }).joined(separator: ": ") + case .commsError(let error): + return error.errorDescription + case .storageFailure: + return LocalizedString("Unable to store pump data", comment: "Error description when storage fails") } } public var failureReason: String? { switch self { - case .noRileyLink: - return nil - case .bolusInProgress: - return nil - case .noDate: - return nil case .tuneFailed(let error): return error.failureReason + default: + return nil } } @@ -46,12 +48,10 @@ extension MinimedPumpManagerError: LocalizedError { switch self { case .noRileyLink: return LocalizedString("Make sure your RileyLink is nearby and powered on", comment: "Recovery suggestion") - case .bolusInProgress: - return nil - case .noDate: - return nil case .tuneFailed(let error): return error.recoverySuggestion + default: + return nil } } } diff --git a/MinimedKitUI/MinimedPumpManager+UI.swift b/MinimedKitUI/MinimedPumpManager+UI.swift index eb64d429a..e61f43895 100644 --- a/MinimedKitUI/MinimedPumpManager+UI.swift +++ b/MinimedKitUI/MinimedPumpManager+UI.swift @@ -23,7 +23,14 @@ extension MinimedPumpManager: PumpManagerUI { let nav = SettingsNavigationViewController(rootViewController: settings) return nav } - + + public func deliveryUncertaintyRecoveryViewController(insulinTintColor: Color, guidanceColors: GuidanceColors) -> (UIViewController & CompletionNotifying) { + // Return settings for now. No uncertainty handling atm. + let settings = MinimedPumpSettingsViewController(pumpManager: self) + let nav = SettingsNavigationViewController(rootViewController: settings) + return nav + } + public var smallImage: UIImage? { return state.smallPumpImage } diff --git a/OmniKit/PumpManager/OmnipodPumpManager.swift b/OmniKit/PumpManager/OmnipodPumpManager.swift index 00b794865..919a455dd 100644 --- a/OmniKit/PumpManager/OmnipodPumpManager.swift +++ b/OmniKit/PumpManager/OmnipodPumpManager.swift @@ -508,7 +508,7 @@ extension OmnipodPumpManager { return state.podState?.fault }) if mockFaultDuringPairing { - completion(.failure(PodCommsError.podFault(fault: fault!))) + completion(.failure(PumpManagerError.deviceState(PodCommsError.podFault(fault: fault!)))) } else { let mockPrimeDuration = TimeInterval(.seconds(3)) completion(.success(mockPrimeDuration)) @@ -671,13 +671,12 @@ extension OmnipodPumpManager { } } - public func refreshStatus(completion: ((_ result: PumpManagerResult) -> Void)? = nil) { + public func refreshStatus() { guard self.hasActivePod else { - completion?(.failure(OmnipodPumpManagerError.noPodPaired)) return } - self.getPodStatus(storeDosesOnSuccess: false, completion: completion) + self.getPodStatus(storeDosesOnSuccess: false, completion: nil) } // MARK: - Pump Commands @@ -685,15 +684,15 @@ extension OmnipodPumpManager { private func getPodStatus(storeDosesOnSuccess: Bool, completion: ((_ result: PumpManagerResult) -> Void)? = nil) { guard state.podState?.unfinalizedBolus?.scheduledCertainty == .uncertain || state.podState?.unfinalizedBolus?.isFinished != false else { self.log.info("Skipping status request due to unfinalized bolus in progress.") - completion?(.failure(PodCommsError.unfinalizedBolus)) + completion?(.failure(PumpManagerError.deviceState(PodCommsError.unfinalizedBolus))) return } let rileyLinkSelector = self.rileyLinkDeviceProvider.firstConnectedDevice podComms.runSession(withName: "Get pod status", using: rileyLinkSelector) { (result) in - do { - switch result { - case .success(let session): + switch result { + case .success(let session): + do { let status = try session.getStatus() if storeDosesOnSuccess { session.dosesForStorage({ (doses) -> Bool in @@ -701,12 +700,12 @@ extension OmnipodPumpManager { }) } completion?(.success(status)) - case .failure(let error): - throw error + } catch let error { + completion?(.failure(PumpManagerError.communication(error as? LocalizedError))) } - } catch let error { - completion?(.failure(error)) + case .failure(let error): self.log.error("Failed to fetch pod status: %{public}@", String(describing: error)) + completion?(.failure(PumpManagerError.communication(error))) } } } @@ -787,7 +786,7 @@ extension OmnipodPumpManager { } guard state.podState?.unfinalizedBolus?.isFinished != false else { - return .failure(PodCommsError.unfinalizedBolus) + return .failure(PumpManagerError.deviceState(PodCommsError.unfinalizedBolus)) } return .success(true) @@ -1287,9 +1286,7 @@ extension OmnipodPumpManager: PumpManager { delegate?.pumpManagerRecommendsLoop(self) case .failure(let error): self.log.default("Not recommending Loop because pump data is stale: %@", String(describing: error)) - if let error = error as? PumpManagerError { - delegate?.pumpManager(self, didError: error) - } + delegate?.pumpManager(self, didError: error) } }) } @@ -1302,9 +1299,9 @@ extension OmnipodPumpManager: PumpManager { } } - public func enactBolus(units: Double, at startDate: Date, willRequest: @escaping (DoseEntry) -> Void, completion: @escaping (PumpManagerResult) -> Void) { + public func enactBolus(units: Double, at startDate: Date, completion: @escaping (PumpManagerResult) -> Void) { guard self.hasActivePod else { - completion(.failure(SetBolusError.certain(OmnipodPumpManagerError.noPodPaired))) + completion(.failure(PumpManagerError.configuration(OmnipodPumpManagerError.noPodPaired))) return } @@ -1318,7 +1315,7 @@ extension OmnipodPumpManager: PumpManager { case .success(let s): session = s case .failure(let error): - completion(.failure(SetBolusError.certain(error))) + completion(.failure(PumpManagerError.communication(error))) return } @@ -1336,7 +1333,7 @@ extension OmnipodPumpManager: PumpManager { do { podStatus = try session.getStatus() } catch let error { - completion(.failure(SetBolusError.certain(error as? PodCommsError ?? PodCommsError.commsError(error: error)))) + completion(.failure(PumpManagerError.communication(error as? LocalizedError))) return } @@ -1347,20 +1344,19 @@ extension OmnipodPumpManager: PumpManager { let beep = self.confirmationBeeps podStatus = try session.resumeBasal(schedule: self.state.basalSchedule, scheduleOffset: scheduleOffset, acknowledgementBeep: beep, completionBeep: beep) } catch let error { - completion(.failure(SetBolusError.certain(error as? PodCommsError ?? PodCommsError.commsError(error: error)))) + completion(.failure(PumpManagerError.communication(error as? LocalizedError))) return } } guard !podStatus.deliveryStatus.bolusing else { - completion(.failure(SetBolusError.certain(PodCommsError.unfinalizedBolus))) + completion(.failure(PumpManagerError.communication(PodCommsError.unfinalizedBolus))) return } let date = Date() let endDate = date.addingTimeInterval(enactUnits / Pod.bolusDeliveryRate) let dose = DoseEntry(type: .bolus, startDate: date, endDate: endDate, value: enactUnits, unit: .units) - willRequest(dose) let beep = self.confirmationBeeps let result = session.bolus(units: enactUnits, acknowledgementBeep: beep, completionBeep: beep) @@ -1372,16 +1368,17 @@ extension OmnipodPumpManager: PumpManager { case .success: completion(.success(dose)) case .certainFailure(let error): - completion(.failure(SetBolusError.certain(error))) + completion(.failure(PumpManagerError.communication(error))) case .uncertainFailure(let error): - completion(.failure(SetBolusError.uncertain(error))) + // TODO: Return PumpManagerError.uncertainDelivery and implement recovery + completion(.failure(PumpManagerError.communication(error))) } } } public func cancelBolus(completion: @escaping (PumpManagerResult) -> Void) { guard self.hasActivePod else { - completion(.failure(OmnipodPumpManagerError.noPodPaired)) + completion(.failure(PumpManagerError.communication(OmnipodPumpManagerError.noPodPaired))) return } @@ -1393,7 +1390,7 @@ extension OmnipodPumpManager: PumpManager { case .success(let s): session = s case .failure(let error): - completion(.failure(error)) + completion(.failure(PumpManagerError.communication(error))) return } @@ -1433,14 +1430,15 @@ extension OmnipodPumpManager: PumpManager { completion(.success(canceledDoseEntry)) } } catch { - completion(.failure(error)) + // TODO: Return PumpManagerError.uncertainDelivery and implement recovery + completion(.failure(PumpManagerError.communication(error as? LocalizedError))) } } } public func enactTempBasal(unitsPerHour: Double, for duration: TimeInterval, completion: @escaping (PumpManagerResult) -> Void) { guard self.hasActivePod else { - completion(.failure(OmnipodPumpManagerError.noPodPaired)) + completion(.failure(PumpManagerError.configuration(OmnipodPumpManagerError.noPodPaired))) return } @@ -1455,96 +1453,97 @@ extension OmnipodPumpManager: PumpManager { case .success(let s): session = s case .failure(let error): - completion(.failure(error)) + completion(.failure(PumpManagerError.communication(error))) return } - do { - let preError = self.setStateWithResult({ (state) -> PodCommsError? in - if case .some(.suspended) = state.podState?.suspendState { - self.log.info("Not enacting temp basal because podState indicates pod is suspended.") - return .podSuspended - } + let preError = self.setStateWithResult({ (state) -> PodCommsError? in + if case .some(.suspended) = state.podState?.suspendState { + self.log.info("Not enacting temp basal because podState indicates pod is suspended.") + return .podSuspended + } - guard state.podState?.unfinalizedBolus?.isFinished != false else { - self.log.info("Not enacting temp basal because podState indicates unfinalized bolus in progress.") - return .unfinalizedBolus - } + guard state.podState?.unfinalizedBolus?.isFinished != false else { + self.log.info("Not enacting temp basal because podState indicates unfinalized bolus in progress.") + return .unfinalizedBolus + } - return nil - }) + return nil + }) - if let error = preError { - throw error - } + if let error = preError { + completion(.failure(PumpManagerError.deviceState(error))) + return + } - let status: StatusResponse - let canceledDose: UnfinalizedDose? + let status: StatusResponse + let canceledDose: UnfinalizedDose? - // if resuming a normal basal as denoted by a 0 duration temp basal, use a confirmation beep if appropriate - let beep: BeepType = duration < .ulpOfOne && self.confirmationBeeps && tempBasalConfirmationBeeps ? .beep : .noBeep - let result = session.cancelDelivery(deliveryType: .tempBasal, beepType: beep) - switch result { - case .certainFailure(let error): - throw error - case .uncertainFailure(let error): - throw error - case .success(let cancelTempStatus, let dose): - status = cancelTempStatus - canceledDose = dose - } + // if resuming a normal basal as denoted by a 0 duration temp basal, use a confirmation beep if appropriate + let beep: BeepType = duration < .ulpOfOne && self.confirmationBeeps && tempBasalConfirmationBeeps ? .beep : .noBeep + let result = session.cancelDelivery(deliveryType: .tempBasal, beepType: beep) + switch result { + case .certainFailure(let error): + completion(.failure(PumpManagerError.communication(error))) + return + case .uncertainFailure(let error): + // TODO: Return PumpManagerError.uncertainDelivery and implement recovery + completion(.failure(PumpManagerError.communication(error))) + return + case .success(let cancelTempStatus, let dose): + status = cancelTempStatus + canceledDose = dose + } - guard !status.deliveryStatus.bolusing else { - throw PodCommsError.unfinalizedBolus - } + guard !status.deliveryStatus.bolusing else { + completion(.failure(PumpManagerError.communication(PodCommsError.unfinalizedBolus))) + return + } - guard status.deliveryStatus != .suspended else { - self.log.info("Canceling temp basal because status return indicates pod is suspended.") - throw PodCommsError.podSuspended - } + guard status.deliveryStatus != .suspended else { + self.log.info("Canceling temp basal because status return indicates pod is suspended.") + completion(.failure(PumpManagerError.communication(PodCommsError.podSuspended))) + return + } - defer { - self.setState({ (state) in - state.tempBasalEngageState = .stable - }) + defer { + self.setState({ (state) in + state.tempBasalEngageState = .stable + }) + } + + if duration < .ulpOfOne { + // 0 duration temp basals are used to cancel any existing temp basal + self.setState({ (state) in + state.tempBasalEngageState = .disengaging + }) + let cancelTime = canceledDose?.finishTime ?? Date() + let dose = DoseEntry(type: .tempBasal, startDate: cancelTime, endDate: cancelTime, value: 0, unit: .unitsPerHour) + session.dosesForStorage() { (doses) -> Bool in + return self.store(doses: doses, in: session) } + completion(.success(dose)) + } else { + self.setState({ (state) in + state.tempBasalEngageState = .engaging + }) - if duration < .ulpOfOne { - // 0 duration temp basals are used to cancel any existing temp basal - self.setState({ (state) in - state.tempBasalEngageState = .disengaging - }) - let cancelTime = canceledDose?.finishTime ?? Date() - let dose = DoseEntry(type: .tempBasal, startDate: cancelTime, endDate: cancelTime, value: 0, unit: .unitsPerHour) - session.dosesForStorage() { (doses) -> Bool in - return self.store(doses: doses, in: session) - } + let beep = self.confirmationBeeps && tempBasalConfirmationBeeps + let result = session.setTempBasal(rate: rate, duration: duration, acknowledgementBeep: beep, completionBeep: beep) + let basalStart = Date() + let dose = DoseEntry(type: .tempBasal, startDate: basalStart, endDate: basalStart.addingTimeInterval(duration), value: rate, unit: .unitsPerHour) + session.dosesForStorage() { (doses) -> Bool in + return self.store(doses: doses, in: session) + } + switch result { + case .success: completion(.success(dose)) - } else { - self.setState({ (state) in - state.tempBasalEngageState = .engaging - }) - - let beep = self.confirmationBeeps && tempBasalConfirmationBeeps - let result = session.setTempBasal(rate: rate, duration: duration, acknowledgementBeep: beep, completionBeep: beep) - let basalStart = Date() - let dose = DoseEntry(type: .tempBasal, startDate: basalStart, endDate: basalStart.addingTimeInterval(duration), value: rate, unit: .unitsPerHour) - session.dosesForStorage() { (doses) -> Bool in - return self.store(doses: doses, in: session) - } - switch result { - case .success: - completion(.success(dose)) - case .uncertainFailure(let error): - self.log.error("Temp basal uncertain error: %@", String(describing: error)) - completion(.success(dose)) - case .certainFailure(let error): - completion(.failure(error)) - } + case .uncertainFailure(let error): + self.log.error("Temp basal uncertain error: %@", String(describing: error)) + completion(.success(dose)) + case .certainFailure(let error): + completion(.failure(PumpManagerError.communication(error))) } - } catch let error { - self.log.error("Error during temp basal: %@", String(describing: error)) - completion(.failure(error)) } } } diff --git a/OmniKit/PumpManager/PodCommsSession.swift b/OmniKit/PumpManager/PodCommsSession.swift index 2168ad579..096d4eadf 100644 --- a/OmniKit/PumpManager/PodCommsSession.swift +++ b/OmniKit/PumpManager/PodCommsSession.swift @@ -574,6 +574,7 @@ public class PodCommsSession { } // use cancelDelivery with .none to get status as well as to validate & advance the nonce + // Throws PodCommsError @discardableResult public func cancelNone() throws -> StatusResponse { var statusResponse: StatusResponse @@ -591,6 +592,7 @@ public class PodCommsSession { return statusResponse } + // Throws PodCommsError @discardableResult public func getStatus() throws -> StatusResponse { if useCancelNoneForStatus { diff --git a/OmniKitUI/PumpManager/OmniPodPumpManager+UI.swift b/OmniKitUI/PumpManager/OmniPodPumpManager+UI.swift index 80bbc42ca..547f56fa3 100644 --- a/OmniKitUI/PumpManager/OmniPodPumpManager+UI.swift +++ b/OmniKitUI/PumpManager/OmniPodPumpManager+UI.swift @@ -24,7 +24,16 @@ extension OmnipodPumpManager: PumpManagerUI { let nav = SettingsNavigationViewController(rootViewController: settings) return nav } + + public func deliveryUncertaintyRecoveryViewController(insulinTintColor: Color, guidanceColors: GuidanceColors) -> (UIViewController & CompletionNotifying) { + + // Return settings for now; uncertainty recovery not implemented yet + let settings = OmnipodSettingsViewController(pumpManager: self) + let nav = SettingsNavigationViewController(rootViewController: settings) + return nav + } + public var smallImage: UIImage? { return UIImage(named: "Pod", in: Bundle(for: OmnipodSettingsViewController.self), compatibleWith: nil)! } diff --git a/RileyLink/DeviceDataManager.swift b/RileyLink/DeviceDataManager.swift index 127a0d164..d03d0efda 100644 --- a/RileyLink/DeviceDataManager.swift +++ b/RileyLink/DeviceDataManager.swift @@ -58,6 +58,7 @@ extension DeviceDataManager: RileyLinkConnectionManagerDelegate { } extension DeviceDataManager: PumpManagerDelegate { + func pumpManager(_ pumpManager: PumpManager, didAdjustPumpClockBy adjustment: TimeInterval) { log.debug("didAdjustPumpClockBy %@", adjustment) } @@ -90,9 +91,9 @@ extension DeviceDataManager: PumpManagerDelegate { func pumpManager(_ pumpManager: PumpManager, hasNewPumpEvents events: [NewPumpEvent], lastReconciliation: Date?, completion: @escaping (_ error: Error?) -> Void) { } - func pumpManager(_ pumpManager: PumpManager, didReadReservoirValue units: Double, at date: Date, completion: @escaping (_ result: PumpManagerResult<(newValue: ReservoirValue, lastValue: ReservoirValue?, areStoredValuesContinuous: Bool)>) -> Void) { + func pumpManager(_ pumpManager: PumpManager, didReadReservoirValue units: Double, at date: Date, completion: @escaping (Result<(newValue: ReservoirValue, lastValue: ReservoirValue?, areStoredValuesContinuous: Bool), Error>) -> Void) { } - + func pumpManagerRecommendsLoop(_ pumpManager: PumpManager) { } diff --git a/RileyLinkKit/PumpOpsSession.swift b/RileyLinkKit/PumpOpsSession.swift index dd699efde..c54e4a773 100644 --- a/RileyLinkKit/PumpOpsSession.swift +++ b/RileyLinkKit/PumpOpsSession.swift @@ -455,9 +455,8 @@ extension PumpOpsSession { /// - Parameters: /// - unitsPerHour: The new basal rate, in Units per hour /// - duration: The duration of the rate - /// - Returns: The pump message body describing the new basal rate - /// - Throws: PumpCommandError - public func setTempBasal(_ unitsPerHour: Double, duration: TimeInterval) throws -> ReadTempBasalCarelinkMessageBody { + /// - Returns: A result containing the pump message body describing the new basal rate or an error + public func setTempBasal(_ unitsPerHour: Double, duration: TimeInterval) -> Result { var lastError: PumpCommandError? let message = PumpMessage(settings: settings, type: .changeTempBasal, body: ChangeTempBasalCarelinkMessageBody(unitsPerHour: unitsPerHour, duration: duration)) @@ -488,9 +487,9 @@ extension PumpOpsSession { let response: ReadTempBasalCarelinkMessageBody = try session.getResponse(to: PumpMessage(settings: settings, type: .readTempBasal), responseType: .readTempBasal) if response.timeRemaining == duration && response.rateType == .absolute { - return response + return .success(response) } else { - throw PumpCommandError.arguments(PumpOpsError.rfCommsFailure("Could not verify TempBasal on attempt \(attempt). ")) + return .failure(PumpCommandError.arguments(PumpOpsError.rfCommsFailure("Could not verify TempBasal on attempt \(attempt). "))) } } catch let error as PumpCommandError { lastError = error @@ -500,8 +499,8 @@ extension PumpOpsSession { lastError = .command(.noResponse(during: "Set temp basal")) } } - - throw lastError! + + return .failure(lastError!) } public func readTempBasal() throws -> Double { @@ -560,53 +559,26 @@ extension PumpOpsSession { public func setNormalBolus(units: Double, cancelExistingTemp: Bool = false) throws { let pumpModel: PumpModel - do { - try wakeup() - pumpModel = try getPumpModel() + try wakeup() + pumpModel = try getPumpModel() - let status = try getPumpStatus() + let status = try getPumpStatus() - if status.bolusing { - throw PumpOpsError.bolusInProgress - } + if status.bolusing { + throw PumpOpsError.bolusInProgress + } - if status.suspended { - throw PumpOpsError.pumpSuspended - } + if status.suspended { + throw PumpOpsError.pumpSuspended + } - if cancelExistingTemp { - _ = try setTempBasal(0, duration: 0) - } - } catch let error as PumpOpsError { - throw SetBolusError.certain(error) - } catch let error as PumpCommandError { - switch error { - case .command(let error): - throw SetBolusError.certain(error) - case .arguments(let error): - throw SetBolusError.certain(error) - } - } catch { - assertionFailure() - return + if cancelExistingTemp { + _ = setTempBasal(0, duration: 0) } - do { - let message = PumpMessage(settings: settings, type: .bolus, body: BolusCarelinkMessageBody(units: units, insulinBitPackingScale: pumpModel.insulinBitPackingScale)) + let message = PumpMessage(settings: settings, type: .bolus, body: BolusCarelinkMessageBody(units: units, insulinBitPackingScale: pumpModel.insulinBitPackingScale)) - let _: PumpAckMessageBody = try runCommandWithArguments(message) - } catch let error as PumpOpsError { - throw SetBolusError.certain(error) - } catch let error as PumpCommandError { - switch error { - case .command(let error): - throw SetBolusError.certain(error) - case .arguments(let error): - throw SetBolusError.uncertain(error) - } - } catch { - assertionFailure() - } + let _: PumpAckMessageBody = try runCommandWithArguments(message) return }