diff --git a/OmniKit.xcodeproj/project.pbxproj b/OmniKit.xcodeproj/project.pbxproj index d3293ea..d8b78df 100644 --- a/OmniKit.xcodeproj/project.pbxproj +++ b/OmniKit.xcodeproj/project.pbxproj @@ -712,31 +712,31 @@ C124022D29C7DA9700B32844 /* Views */ = { isa = PBXGroup; children = ( + C124024629C7DA9700B32844 /* AttachPodView.swift */, + C124024C29C7DA9700B32844 /* BasalStateView.swift */, + C124024429C7DA9700B32844 /* BeepPreferenceSelectionView.swift */, C124022F29C7DA9700B32844 /* CheckInsertedCannulaView.swift */, + C124024B29C7DA9700B32844 /* DeactivatePodView.swift */, + C124024A29C7DA9700B32844 /* DeliveryUncertaintyRecoveryView.swift */, C124023129C7DA9700B32844 /* DesignElements */, - C124023529C7DA9700B32844 /* LowReservoirReminderEditView.swift */, - C124023629C7DA9700B32844 /* InsertCannulaView.swift */, - C124023729C7DA9700B32844 /* RileyLinkSetupView.swift */, - C124023829C7DA9700B32844 /* SetupCompleteView.swift */, C124023A29C7DA9700B32844 /* ExpirationReminderPickerView.swift */, - C124023B29C7DA9700B32844 /* UncertaintyRecoveredView.swift */, + C124024529C7DA9700B32844 /* ExpirationReminderSetupView.swift */, + C124023629C7DA9700B32844 /* InsertCannulaView.swift */, C124023C29C7DA9700B32844 /* InsulinTypeConfirmation.swift */, + C124023529C7DA9700B32844 /* LowReservoirReminderEditView.swift */, + C124024229C7DA9700B32844 /* LowReservoirReminderSetupView.swift */, C124023D29C7DA9700B32844 /* ManualTempBasalEntryView.swift */, C124023F29C7DA9700B32844 /* NotificationSettingsView.swift */, + C124024929C7DA9700B32844 /* OmnipodReservoirView.swift */, C124024029C7DA9700B32844 /* OmnipodSettingsView.swift */, - C124024129C7DA9700B32844 /* PodSetupView.swift */, - C124024229C7DA9700B32844 /* LowReservoirReminderSetupView.swift */, C124024329C7DA9700B32844 /* PairPodView.swift */, - C124024429C7DA9700B32844 /* BeepPreferenceSelectionView.swift */, - C124024529C7DA9700B32844 /* ExpirationReminderSetupView.swift */, - C124024629C7DA9700B32844 /* AttachPodView.swift */, - C124024729C7DA9700B32844 /* ScheduledExpirationReminderEditView.swift */, C124024829C7DA9700B32844 /* PodDetailsView.swift */, - C124024929C7DA9700B32844 /* OmnipodReservoirView.swift */, - C124024A29C7DA9700B32844 /* DeliveryUncertaintyRecoveryView.swift */, - C124024B29C7DA9700B32844 /* DeactivatePodView.swift */, - C124024C29C7DA9700B32844 /* BasalStateView.swift */, + C124024129C7DA9700B32844 /* PodSetupView.swift */, + C124023729C7DA9700B32844 /* RileyLinkSetupView.swift */, + C124024729C7DA9700B32844 /* ScheduledExpirationReminderEditView.swift */, + C124023829C7DA9700B32844 /* SetupCompleteView.swift */, C124024D29C7DA9700B32844 /* TimeView.swift */, + C124023B29C7DA9700B32844 /* UncertaintyRecoveredView.swift */, ); path = Views; sourceTree = ""; diff --git a/OmniKit/PumpManager/OmnipodPumpManager.swift b/OmniKit/PumpManager/OmnipodPumpManager.swift index ec97083..5d2c068 100644 --- a/OmniKit/PumpManager/OmnipodPumpManager.swift +++ b/OmniKit/PumpManager/OmnipodPumpManager.swift @@ -334,7 +334,7 @@ extension OmnipodPumpManager { switch podCommState(for: state) { case .activating: return PumpStatusHighlight( - localizedMessage: LocalizedString("Finish Pairing", comment: "Status highlight that when pod is activating."), + localizedMessage: LocalizedString("Finish Setup", comment: "Status highlight that when pod is activating."), imageName: "exclamationmark.circle.fill", state: .warning) case .deactivating: @@ -586,7 +586,7 @@ extension OmnipodPumpManager { } else if !podState.isSetupComplete { return .activating } - return .deactivating + return .deactivating // Can't be reached and thus will never be returned } public var podCommState: PodCommState { @@ -662,6 +662,25 @@ extension OmnipodPumpManager { return date } + // Reset all the per pod state kept in pump manager state which doesn't span pods + fileprivate func resetPerPodPumpManagerState() { + + // Reset any residual per pod slot based pump manager alerts + // (i.e., all but timeOffsetChangeDetected which isn't actually used) + let podAlerts = state.activeAlerts.filter { $0 != .timeOffsetChangeDetected } + for alert in podAlerts { + self.retractAlert(alert: alert) + } + + self.setState { (state) in + // Reset alertsWithPendingAcknowledgment which are all pod slot based + state.alertsWithPendingAcknowledgment = [] + + // Reset other miscellaneous state variables that are actually per pod + state.podAttachmentConfirmed = false + state.acknowledgedTimeOffsetAlert = false + } + } // MARK: - Pod comms @@ -679,13 +698,14 @@ extension OmnipodPumpManager { podComms.forgetPod() + self.resetPerPodPumpManagerState() + if let dosesToStore = self.state.podState?.dosesToStore { self.store(doses: dosesToStore, completion: { error in self.setState({ (state) in if error != nil { state.unstoredDoses.append(contentsOf: dosesToStore) } - resetPodState(&state) }) completion() @@ -697,7 +717,7 @@ extension OmnipodPumpManager { completion() } } - + // MARK: Testing #if targetEnvironment(simulator) private func jumpStartPod(address: UInt32, lot: UInt32, tid: UInt32, fault: DetailedStatus? = nil, startDate: Date? = nil, mockFault: Bool) { @@ -712,8 +732,14 @@ extension OmnipodPumpManager { podComms = PodComms(podState: podState) + self.podComms.delegate = self + self.podComms.messageLogger = self + + self.resetPerPodPumpManagerState() + setState({ (state) in state.updatePodStateFromPodComms(podState) + state.scheduledExpirationReminderOffset = state.defaultExpirationReminderOffset }) } #endif @@ -808,6 +834,8 @@ extension OmnipodPumpManager { } } + self.resetPerPodPumpManagerState() + // Calls completion primeSession(result) } diff --git a/OmniKit/PumpManager/PodCommsSession.swift b/OmniKit/PumpManager/PodCommsSession.swift index c509ea5..e333f20 100644 --- a/OmniKit/PumpManager/PodCommsSession.swift +++ b/OmniKit/PumpManager/PodCommsSession.swift @@ -423,7 +423,6 @@ public class PodCommsSession { public func insertCannula(optionalAlerts: [PodAlert] = []) throws -> TimeInterval { let cannulaInsertionUnits = Pod.cannulaInsertionUnits + Pod.cannulaInsertionUnitsExtra - let insertionWait: TimeInterval = .seconds(cannulaInsertionUnits / Pod.primeDeliveryRate) guard let activatedAt = podState.activatedAt else { throw PodCommsError.noPodPaired @@ -435,7 +434,8 @@ public class PodCommsSession { if status.podProgressStatus == .insertingCannula { podState.setupProgress = .cannulaInserting podState.updateFromStatusResponse(status, at: currentDate) - return insertionWait // Not sure when it started, wait full time to be sure + // return a non-zero wait time based on the bolus not yet delivered + return (status.bolusNotDelivered / Pod.primeDeliveryRate) + 1 } if status.podProgressStatus.readyForDelivery { markSetupProgressCompleted(statusResponse: status) @@ -464,7 +464,7 @@ public class PodCommsSession { podState.updateFromStatusResponse(status2, at: currentDate) podState.setupProgress = .cannulaInserting - return insertionWait + return status2.bolusNotDelivered / Pod.primeDeliveryRate // seconds for the cannula insert bolus to finish } public func checkInsertionCompleted() throws { diff --git a/OmniKit/PumpManager/PodState.swift b/OmniKit/PumpManager/PodState.swift index fd051b6..954f055 100644 --- a/OmniKit/PumpManager/PodState.swift +++ b/OmniKit/PumpManager/PodState.swift @@ -41,6 +41,10 @@ public enum SetupProgress: Int { public var needsCannulaInsertion: Bool { return self.rawValue < SetupProgress.completed.rawValue } + + public var cannulaInsertionSuccessfullyStarted: Bool { + return self.rawValue > SetupProgress.startingInsertCannula.rawValue + } } // TODO: Mutating functions aren't guaranteed to synchronize read/write calls. diff --git a/OmniKitUI/ViewControllers/OmnipodUICoordinator.swift b/OmniKitUI/ViewControllers/OmnipodUICoordinator.swift index 8865497..e3da75b 100644 --- a/OmniKitUI/ViewControllers/OmnipodUICoordinator.swift +++ b/OmniKitUI/ViewControllers/OmnipodUICoordinator.swift @@ -24,7 +24,7 @@ enum OmnipodUIScreen { case lowReservoirReminderSetup case insulinTypeSelection case rileyLinkSetup - case pairPod + case pairAndPrime case insertCannula case confirmAttachment case checkInsertedCannula @@ -45,8 +45,8 @@ enum OmnipodUIScreen { case .insulinTypeSelection: return .rileyLinkSetup case .rileyLinkSetup: - return .pairPod - case .pairPod: + return .pairAndPrime + case .pairAndPrime: return .confirmAttachment case .confirmAttachment: return .insertCannula @@ -61,7 +61,7 @@ enum OmnipodUIScreen { case .uncertaintyRecovered: return nil case .deactivate: - return .pairPod + return .pairAndPrime case .settings: return nil } @@ -199,7 +199,7 @@ class OmnipodUICoordinator: UINavigationController, PumpManagerOnboarding, Compl let view = OmnipodSettingsView(viewModel: viewModel, rileyLinkListDataSource: rileyLinkListDataSource, handleRileyLinkSelection: handleRileyLinkSelection, supportedInsulinTypes: allowedInsulinTypes) return hostingController(rootView: view) - case .pairPod: + case .pairAndPrime: pumpManagerOnboardingDelegate?.pumpManagerOnboarding(didCreatePumpManager: pumpManager) let viewModel = PairPodViewModel(podPairer: pumpManager) @@ -344,8 +344,8 @@ class OmnipodUICoordinator: UINavigationController, PumpManagerOnboarding, Compl } private func hostingController(rootView: Content, onDisappear: @escaping () -> Void = {}) -> DismissibleHostingController { - return DismissibleHostingController(content: rootView, onDisappear: onDisappear, colorPalette: colorPalette) - } + return DismissibleHostingController(content: rootView, onDisappear: onDisappear, colorPalette: colorPalette) + } private func stepFinished() { if let nextStep = currentScreen.next() { @@ -411,13 +411,13 @@ class OmnipodUICoordinator: UINavigationController, PumpManagerOnboarding, Compl if pumpManager.podAttachmentConfirmed { return .insertCannula } else { - return .confirmAttachment + return .pairAndPrime // need to finish the priming } } else if !pumpManager.isOnboarded { if !pumpManager.initialConfigurationCompleted { return .firstRunScreen } - return .pairPod + return .pairAndPrime // pair and prime a new pod } else { return .settings } diff --git a/OmniKitUI/ViewModels/InsertCannulaViewModel.swift b/OmniKitUI/ViewModels/InsertCannulaViewModel.swift index 029ef7d..bb96379 100644 --- a/OmniKitUI/ViewModels/InsertCannulaViewModel.swift +++ b/OmniKitUI/ViewModels/InsertCannulaViewModel.swift @@ -13,9 +13,14 @@ import OmniKit public protocol CannulaInserter { func insertCannula(completion: @escaping (Result) -> ()) func checkCannulaInsertionFinished(completion: @escaping (OmnipodPumpManagerError?) -> Void) + var cannulaInsertionSuccessfullyStarted: Bool { get } } -extension OmnipodPumpManager: CannulaInserter {} +extension OmnipodPumpManager: CannulaInserter { + public var cannulaInsertionSuccessfullyStarted: Bool { + return state.podState?.setupProgress.cannulaInsertionSuccessfullyStarted == true + } +} class InsertCannulaViewModel: ObservableObject, Identifiable { @@ -29,9 +34,9 @@ class InsertCannulaViewModel: ObservableObject, Identifiable { var actionButtonAccessibilityLabel: String { switch self { - case .ready, .startingInsertion: + case .ready: return LocalizedString("Slide Button to insert Cannula", comment: "Insert cannula slider button accessibility label while ready to pair") - case .inserting: + case .inserting, .startingInsertion: return LocalizedString("Inserting. Please wait.", comment: "Insert cannula action button accessibility label while pairing") case .checkingInsertion: return LocalizedString("Checking Insertion", comment: "Insert cannula action button accessibility label checking insertion") @@ -142,22 +147,15 @@ class InsertCannulaViewModel: ObservableObject, Identifiable { init(cannulaInserter: CannulaInserter) { self.cannulaInserter = cannulaInserter + + // If resuming, don't wait for the button action + if cannulaInserter.cannulaInsertionSuccessfullyStarted { + insertCannula() + } } -// private func handleEvent(_ event: ActivationStep2Event) { -// switch event { -// case .insertingCannula: -// let finishTime = TimeInterval(Pod.estimatedCannulaInsertionDuration) -// state = .inserting(finishTime: CACurrentMediaTime() + finishTime) -// case .step2Completed: -// state = .finished -// default: -// break -// } -// } - private func checkCannulaInsertionFinished() { - state = .startingInsertion + state = .checkingInsertion cannulaInserter.checkCannulaInsertionFinished() { (error) in DispatchQueue.main.async { if let error = error { @@ -171,7 +169,6 @@ class InsertCannulaViewModel: ObservableObject, Identifiable { private func insertCannula() { state = .startingInsertion - cannulaInserter.insertCannula { (result) in DispatchQueue.main.async { switch(result) { @@ -189,14 +186,6 @@ class InsertCannulaViewModel: ObservableObject, Identifiable { self.state = .error(error) } } - - -// switch status { -// case .error(let error): -// self.state = .error(error) -// case .event(let event): -// self.handleEvent(event) -// } } } @@ -214,7 +203,6 @@ class InsertCannulaViewModel: ObservableObject, Identifiable { insertCannula() } } - } public extension OmnipodPumpManagerError { diff --git a/OmniKitUI/ViewModels/PairPodViewModel.swift b/OmniKitUI/ViewModels/PairPodViewModel.swift index c7c560b..ffd79ee 100644 --- a/OmniKitUI/ViewModels/PairPodViewModel.swift +++ b/OmniKitUI/ViewModels/PairPodViewModel.swift @@ -39,7 +39,7 @@ class PairPodViewModel: ObservableObject, Identifiable { enum PairPodViewModelState { case ready case pairing - case priming(finishTime: CFTimeInterval) + case priming(finishTime: CFTimeInterval?) case error(OmnipodPairingError) case finished @@ -85,14 +85,6 @@ class PairPodViewModel: ObservableObject, Identifiable { } var navBarButtonAction: NavBarButtonAction { -// switch self { -// case .error(_, let podCommState): -// if podCommState == .activating { -// return .discard -// } -// default: -// break -// } return .cancel } @@ -119,7 +111,11 @@ class PairPodViewModel: ObservableObject, Identifiable { case .pairing: return .indeterminantProgress case .priming(let finishTime): - return .timedProgress(finishTime: finishTime) + if let finishTime { + return .timedProgress(finishTime: finishTime) + } else { + return .indeterminantProgress + } case .finished: return .completed } @@ -152,7 +148,7 @@ class PairPodViewModel: ObservableObject, Identifiable { @Published var state: PairPodViewModelState = .ready var podIsActivated: Bool { - return false // podPairer.podCommState != .noPod + return podPairer.podCommState != .noPod } var backButtonHidden: Bool { @@ -175,12 +171,22 @@ class PairPodViewModel: ObservableObject, Identifiable { init(podPairer: PodPairer) { self.podPairer = podPairer + + // If resuming, don't wait for the button action + if podPairer.podCommState == .activating { + pairAndPrime() + } } - - private func pair() { - state = .pairing - - podPairer.pair { (status) in + + private func pairAndPrime() { + if podPairer.podCommState == .noPod { + state = .pairing + } else { + // Already paired, so resume with the prime + state = .priming(finishTime: nil) + } + + podPairer.pairAndPrimePod { (status) in DispatchQueue.main.async { switch status { case .failure(let error): @@ -208,14 +214,14 @@ class PairPodViewModel: ObservableObject, Identifiable { self.didRequestDeactivation?() } else { // Retry - pair() + pairAndPrime() } case .finished: didFinish?() default: - pair() + pairAndPrime() } - } + } } // Pairing recovery suggestions @@ -246,15 +252,16 @@ enum OmnipodPairingError : LocalizedError { } public protocol PodPairer { - func pair(completion: @escaping (PumpManagerResult) -> Void) + func pairAndPrimePod(completion: @escaping (PumpManagerResult) -> Void) func discardPod(completion: @escaping (Bool) -> ()) + var podCommState: PodCommState { get } } extension OmnipodPumpManager: PodPairer { public func discardPod(completion: @escaping (Bool) -> ()) { } - public func pair(completion: @escaping (PumpManagerResult) -> Void) { + public func pairAndPrimePod(completion: @escaping (PumpManagerResult) -> Void) { pairAndPrime(completion: completion) } } diff --git a/OmniKitUI/ViewModels/PodLifeState.swift b/OmniKitUI/ViewModels/PodLifeState.swift index fc625c2..601b33f 100644 --- a/OmniKitUI/ViewModels/PodLifeState.swift +++ b/OmniKitUI/ViewModels/PodLifeState.swift @@ -72,7 +72,7 @@ enum PodLifeState { var nextPodLifecycleAction: OmnipodUIScreen { switch self { case .podActivating, .noPod: - return .pairPod + return .pairAndPrime default: return .deactivate } diff --git a/OmniKitUI/Views/InsertCannulaView.swift b/OmniKitUI/Views/InsertCannulaView.swift index c1761ff..bf8aec8 100644 --- a/OmniKitUI/Views/InsertCannulaView.swift +++ b/OmniKitUI/Views/InsertCannulaView.swift @@ -76,7 +76,7 @@ struct InsertCannulaView: View { .padding() } .alert(isPresented: $cancelModalIsPresented) { cancelPairingModal } - .navigationBarTitle("Insert Cannula", displayMode: .automatic) + .navigationBarTitle(LocalizedString("Insert Cannula", comment: "navigation bar title for insert cannula"), displayMode: .automatic) .navigationBarBackButtonHidden(true) .navigationBarItems(trailing: cancelButton) } @@ -131,7 +131,7 @@ struct InsertCannulaView: View { } class MockCannulaInserter: CannulaInserter { - public func insertCannula(completion: @escaping (Result) -> Void) { + func insertCannula(completion: @escaping (Result) -> Void) { let mockDelay = TimeInterval(seconds: 3) let result :Result = .success(mockDelay) completion(result) @@ -141,15 +141,13 @@ class MockCannulaInserter: CannulaInserter { completion(nil) } - + var cannulaInsertionSuccessfullyStarted: Bool = false } + struct InsertCannulaView_Previews: PreviewProvider { static var mockInserter = MockCannulaInserter() static var model = InsertCannulaViewModel(cannulaInserter: mockInserter) static var previews: some View { InsertCannulaView(viewModel: model) - - - } }