diff --git a/OmniBLE/PumpManager/OmniBLEPumpManager.swift b/OmniBLE/PumpManager/OmniBLEPumpManager.swift index 35b400ca..ada8e522 100644 --- a/OmniBLE/PumpManager/OmniBLEPumpManager.swift +++ b/OmniBLE/PumpManager/OmniBLEPumpManager.swift @@ -888,6 +888,7 @@ extension OmniBLEPumpManager { self.podComms.pairAndSetupPod(timeZone: .currentFixed, insulinType: insulinType, messageLogger: self) { (result) in + // Have new podState, reset all the per pod pump manager state self.resetPerPodPumpManagerState() // Calls completion @@ -900,6 +901,9 @@ extension OmniBLEPumpManager { } else { self.log.default("Pod already paired. Continuing.") + // Resuming the pod setup, try to ensure pod comms will work right away + self.resumingPodSetup() + self.podComms.runSession(withName: "Prime pod") { (result) in // Calls completion primeSession(result) @@ -959,6 +963,10 @@ extension OmniBLEPumpManager { self.podComms.runSession(withName: "Insert cannula") { (result) in switch result { case .success(let session): + if self.state.podState?.setupProgress.cannulaInsertionSuccessfullyStarted == true { + // Resuming the pod setup, try to ensure pod comms will work right away + self.resumingPodSetup() + } do { if self.state.podState?.setupProgress.needsInitialBasalSchedule == true { let scheduleOffset = timeZone.scheduleOffset(forDate: Date()) @@ -1011,6 +1019,33 @@ extension OmniBLEPumpManager { #endif } + // Called when resuming a pod setup operation which sometimes can fail on the first pod command in various situations. + // Attempting a getStatus and sleeping a couple of seconds on errors greatly improves the odds for first pod command success. + public func resumingPodSetup() { + let sleepTime:UInt32 = 2 + + if !isConnected { + self.log.debug("### Pod setup resume pod not connected, sleeping %d seconds", sleepTime) + sleep(sleepTime) + } + + podComms.runSession(withName: "Resuming pod setup") { (result) in + switch result { + case .success(let session): + let status = try? session.getStatus() + if status == nil { + self.log.debug("### Pod setup resume getStatus failed, sleeping %d seconds", sleepTime) + sleep(sleepTime) + } + case .failure(let error): + self.log.debug("### Pod setup resume session failure, sleeping %d seconds: %@", sleepTime, error.localizedDescription) + sleep(sleepTime) + } + } + } + + // MARK: - Pump Commands + public func getPodStatus(completion: ((_ result: PumpManagerResult) -> Void)? = nil) { guard state.hasActivePod else { completion?(.failure(PumpManagerError.configuration(OmniBLEPumpManagerError.noPodPaired))) @@ -1038,8 +1073,6 @@ extension OmniBLEPumpManager { } } - // MARK: - Pump Commands - public func acknowledgePodAlerts(_ alertsToAcknowledge: AlertSet, completion: @escaping (_ alerts: [AlertSlot: PodAlert]?) -> Void) { guard self.hasActivePod else { completion(nil) diff --git a/OmniBLE/PumpManagerUI/ViewModels/InsertCannulaViewModel.swift b/OmniBLE/PumpManagerUI/ViewModels/InsertCannulaViewModel.swift index 984a507b..733fc36b 100644 --- a/OmniBLE/PumpManagerUI/ViewModels/InsertCannulaViewModel.swift +++ b/OmniBLE/PumpManagerUI/ViewModels/InsertCannulaViewModel.swift @@ -129,6 +129,7 @@ class InsertCannulaViewModel: ObservableObject, Identifiable { } @Published var state: InsertCannulaViewModelState = .ready + public var stateNeedsDeliberateUserAcceptance : Bool { switch state { case .ready: @@ -143,16 +144,19 @@ class InsertCannulaViewModel: ObservableObject, Identifiable { var didRequestDeactivation: (() -> Void)? var cannulaInserter: CannulaInserter - + + var autoRetryAttempted: Bool + init(cannulaInserter: CannulaInserter) { self.cannulaInserter = cannulaInserter + self.autoRetryAttempted = false // If resuming, don't wait for the button action if cannulaInserter.cannulaInsertionSuccessfullyStarted { insertCannula() } } - + private func checkCannulaInsertionFinished() { state = .checkingInsertion cannulaInserter.checkCannulaInsertionFinished() { (error) in @@ -168,6 +172,7 @@ class InsertCannulaViewModel: ObservableObject, Identifiable { private func insertCannula() { state = .startingInsertion + cannulaInserter.insertCannula { (result) in DispatchQueue.main.async { switch(result) { @@ -182,7 +187,19 @@ class InsertCannulaViewModel: ObservableObject, Identifiable { self.state = .finished } case .failure(let error): - self.state = .error(error) + if self.autoRetryAttempted { + self.autoRetryAttempted = false // allow for an auto retry on the next user attempt + self.state = .error(error) + } else { + self.autoRetryAttempted = true + let autoRetryPauseTime = TimeInterval(seconds: 3) + print("### insertCannula encountered error \(error.localizedDescription), retrying after \(autoRetryPauseTime) seconds") + DispatchQueue.global(qos: .utility).async { + Thread.sleep(forTimeInterval: autoRetryPauseTime) + + self.insertCannula() + } + } } } } diff --git a/OmniBLE/PumpManagerUI/ViewModels/PairPodViewModel.swift b/OmniBLE/PumpManagerUI/ViewModels/PairPodViewModel.swift index 5e1ce70b..4e3ec324 100644 --- a/OmniBLE/PumpManagerUI/ViewModels/PairPodViewModel.swift +++ b/OmniBLE/PumpManagerUI/ViewModels/PairPodViewModel.swift @@ -168,8 +168,11 @@ class PairPodViewModel: ObservableObject, Identifiable { var podPairer: PodPairer + var autoRetryAttempted: Bool + init(podPairer: PodPairer) { self.podPairer = podPairer + self.autoRetryAttempted = false // If resuming, don't wait for the button action if podPairer.podCommState == .activating { @@ -189,8 +192,20 @@ class PairPodViewModel: ObservableObject, Identifiable { DispatchQueue.main.async { switch status { case .failure(let error): - let pairingError = DashPairingError.pumpManagerError(error) - self.state = .error(pairingError) + if self.autoRetryAttempted { + self.autoRetryAttempted = false // allow for an auto retry on the next user attempt + let pairAndPrimeError = DashPairingError.pumpManagerError(error) + self.state = .error(pairAndPrimeError) + } else { + self.autoRetryAttempted = true + let autoRetryPauseTime = TimeInterval(seconds: 3) + print("### pairAndPrimePod encountered error \(error.localizedDescription), retrying after \(autoRetryPauseTime) seconds") + DispatchQueue.global(qos: .utility).async { + Thread.sleep(forTimeInterval: autoRetryPauseTime) + + self.pairAndPrime() // handles both pairing or priming failures + } + } case .success(let duration): if duration > 0 {