Explorar o código

Refactor condition and checking; no longer attach it to Onboarding

Deniz Cengiz hai 4 meses
pai
achega
b7d42e3377

+ 19 - 0
Model/Helper/PumpEvent+helper.swift

@@ -223,6 +223,25 @@ enum PumpEventDTO: Encodable {
         if case .suspend = self { return true }
         return false
     }
+
+    var timestampDate: Date? {
+        switch self {
+        case let .bolus(dto):
+            return PumpEventStored.dateFormatter.date(from: dto.timestamp)
+        case let .tempBasal(dto):
+            return PumpEventStored.dateFormatter.date(from: dto.timestamp)
+        case let .tempBasalDuration(dto):
+            return PumpEventStored.dateFormatter.date(from: dto.timestamp)
+        case let .suspend(dto):
+            return PumpEventStored.dateFormatter.date(from: dto.timestamp)
+        case let .resume(dto):
+            return PumpEventStored.dateFormatter.date(from: dto.timestamp)
+        case let .rewind(dto):
+            return PumpEventStored.dateFormatter.date(from: dto.timestamp)
+        case let .prime(dto):
+            return PumpEventStored.dateFormatter.date(from: dto.timestamp)
+        }
+    }
 }
 
 // Extension with helper functions to map pump events to DTO objects via uniform masking enum

+ 55 - 29
Trio/Sources/APS/OpenAPS/OpenAPS.swift

@@ -208,40 +208,55 @@ final class OpenAPS {
                 dtos.insert(simulatedBolusDTO, at: 0)
             }
 
+            // FIXME: remove this; for testing only
+//            let datae60minAgo = Date().addingTimeInterval(-3600)
+//            let simulatedResumeDTO = self.createResumeDTO(at: datae60minAgo)
+//            dtos.insert(simulatedResumeDTO, at: 0)
+
             // This condition addresses https://github.com/nightscout/Trio/issues/898
-            // If onboardingCompletedAt is present, we know it is a fresh install,
-            // as we only store this computed timestamp for new Trio users.
-            // Fresh installs usually do not have any pump history, so the first
-            // event after onboarding is typically a PumpResume. Without a preceding
-            // suspend, oref interprets the time between midnight and onboarding as lacking
-            // scheduled basal, so IOB becomes negative. When onboarding completed within
-            // the insulin action duration window, we now find the earliest PumpResume in the
-            // mapped DTOs; if no suspend precedes it, prepend a simulated suspend one
-            // second before onboarding completion. This backdated entry gives oref a safe
-            // baseline, i.e. it assumes profile basal rate in lieue of lacking history,
-            // so net iob of 0
-            if let onboardingCompletedAt = PropertyPersistentFlags.shared.onboardingCompletedAt {
-                let timeSinceOnboarding = Date().timeIntervalSince(onboardingCompletedAt)
+            // Within the last 24h of pump history, resumes can appear
+            // without a preceding suspend (freshly onboarded user, pump reconnection, …).
+            // When a resume occurs inside the DIA window and there is no suspend in the
+            // DIA window (or the remaining 24h window), prepend a simulated suspend event
+            // 1 second before the resume event. This backstop avoids oref starting from a
+            // resume-only state that can drive negative IOB while keeping real history intact.
+            if let pumpSettings = self.storage.retrieve(OpenAPS.Settings.settings, as: PumpSettings.self) {
+                let diaWindowSeconds = (pumpSettings.insulinActionCurve as NSDecimalNumber).doubleValue * 60 * 60
+                let diaWindow: TimeInterval = diaWindowSeconds
                 let oneDaySeconds: TimeInterval = 24 * 60 * 60
+                let now = Date()
 
-                if timeSinceOnboarding <= oneDaySeconds,
-                   let pumpSettings = self.storage.retrieve(
-                       OpenAPS.Settings.settings,
-                       as: PumpSettings.self
-                   )
-                {
-                    let durationOfInsulinActionWindowSeconds = (pumpSettings.insulinActionCurve as NSDecimalNumber)
-                        .doubleValue * 60 * 60
-
-                    if timeSinceOnboarding <= durationOfInsulinActionWindowSeconds,
-                       let firstResumeIndex = dtos.firstIndex(where: \.isResume)
-                    {
-                        let hasSuspendBeforeResume = dtos[..<firstResumeIndex].contains(where: \.isSuspend)
+                let datedDTOs = dtos.compactMap { dto -> (PumpEventDTO, Date)? in
+                    guard let timestamp = dto.timestampDate else { return nil }
+                    return (dto, timestamp)
+                }.sorted { $0.1 < $1.1 }
+
+                let recentDTOs = datedDTOs.filter { now.timeIntervalSince($0.1) <= oneDaySeconds }
+                if let resumeIndex = recentDTOs.firstIndex(where: { tuple in
+                    tuple.0.isResume && now.timeIntervalSince(tuple.1) <= diaWindow
+                }) {
+                    let resumeDate = recentDTOs[resumeIndex].1
+
+                    let hasSuspendInDIAWindowBeforeResume = recentDTOs[..<resumeIndex].contains { element in
+                        element.0.isSuspend && now.timeIntervalSince(element.1) <= diaWindow
+                    }
+
+                    if !hasSuspendInDIAWindowBeforeResume {
+                        let hasSuspendInOlderWindow = recentDTOs.contains { element in
+                            element.0.isSuspend
+                                && now.timeIntervalSince(element.1) > diaWindow
+                                && now.timeIntervalSince(element.1) <= oneDaySeconds
+                        }
 
-                        if !hasSuspendBeforeResume {
-                            let suspendDate = onboardingCompletedAt.addingTimeInterval(-1)
+                        if !hasSuspendInOlderWindow {
+                            let suspendDate = resumeDate.addingTimeInterval(-1)
                             let suspendDTO = self.createSimulatedSuspendDTO(at: suspendDate)
-                            dtos.insert(suspendDTO, at: 0)
+
+                            let insertionIndex = dtos.firstIndex { dto in
+                                dto.isResume && dto.timestampDate == resumeDate
+                            } ?? 0
+
+                            dtos.insert(suspendDTO, at: insertionIndex)
                         }
                     }
                 }
@@ -315,6 +330,17 @@ final class OpenAPS {
         return .bolus(bolusDTO)
     }
 
+    // FIXME: remove this; for testing only
+    private func createResumeDTO(at date: Date) -> PumpEventDTO {
+        let dateFormatted = PumpEventStored.dateFormatter.string(from: date)
+
+        let resumeDTO = ResumeDTO(
+            id: UUID().uuidString,
+            timestamp: dateFormatted
+        )
+        return .resume(resumeDTO)
+    }
+
     private func createSimulatedSuspendDTO(at date: Date) -> PumpEventDTO {
         let dateFormatted = PumpEventStored.dateFormatter.string(from: date)
 

+ 0 - 2
Trio/Sources/Helpers/PropertyPersistentFlags.swift

@@ -20,8 +20,6 @@ final class PropertyPersistentFlags {
 
     @PersistedProperty(key: "onboardingCompleted") var onboardingCompleted: Bool?
 
-    @PersistedProperty(key: "onboardingCompletedAt") var onboardingCompletedAt: Date?
-
     @PersistedProperty(key: "diagnosticsSharing") var diagnosticsSharingEnabled: Bool?
 
     @PersistedProperty(key: "lastCleanupDate") var lastCleanupDate: Date?

+ 1 - 1
Trio/Sources/Modules/Onboarding/View/OnboardingRootView.swift

@@ -771,7 +771,7 @@ struct OnboardingNavigationButtons: View {
 
         case .completed:
             state.saveOnboardingData()
-            onboardingManager.completeOnboarding(state.isFreshTrioInstall)
+            onboardingManager.completeOnboarding()
             Foundation.NotificationCenter.default.post(name: .onboardingCompleted, object: nil)
 
         default:

+ 1 - 5
Trio/Sources/Services/OnboardingManager/OnboardingManager.swift

@@ -22,13 +22,9 @@ import Swinject
     }
 
     /// Marks onboarding as completed and updates the shouldShowOnboarding flag.
-    func completeOnboarding(_ isFreshTrioInstallation: Bool) {
+    func completeOnboarding() {
         PropertyPersistentFlags.shared.onboardingCompleted = true
 
-        if isFreshTrioInstallation {
-            PropertyPersistentFlags.shared.onboardingCompletedAt = Date()
-        }
-
         shouldShowOnboarding = false
     }