Kaynağa Gözat

Fix healthkit issue for Insulin treatment

Fix the healthKit sync when stopped before the end #231
Fix the 0U basal in healthKit #149
Pierre L 2 yıl önce
ebeveyn
işleme
cc158e2660

+ 0 - 5
FreeAPS/Sources/APS/APSManager.swift

@@ -72,7 +72,6 @@ final class BaseAPSManager: APSManager, Injectable {
     @Injected() private var nightscout: NightscoutManager!
     @Injected() private var nightscout: NightscoutManager!
     @Injected() private var settingsManager: SettingsManager!
     @Injected() private var settingsManager: SettingsManager!
     @Injected() private var broadcaster: Broadcaster!
     @Injected() private var broadcaster: Broadcaster!
-    @Injected() private var healthKitManager: HealthKitManager!
     @Persisted(key: "lastAutotuneDate") private var lastAutotuneDate = Date()
     @Persisted(key: "lastAutotuneDate") private var lastAutotuneDate = Date()
     @Persisted(key: "lastStartLoopDate") private var lastStartLoopDate: Date = .distantPast
     @Persisted(key: "lastStartLoopDate") private var lastStartLoopDate: Date = .distantPast
     @Persisted(key: "lastLoopDate") var lastLoopDate: Date = .distantPast {
     @Persisted(key: "lastLoopDate") var lastLoopDate: Date = .distantPast {
@@ -268,10 +267,6 @@ final class BaseAPSManager: APSManager, Injectable {
     private func loopCompleted(error: Error? = nil, loopStatRecord: LoopStats) {
     private func loopCompleted(error: Error? = nil, loopStatRecord: LoopStats) {
         isLooping.send(false)
         isLooping.send(false)
 
 
-        // save AH events
-        let events = pumpHistoryStorage.recent()
-        healthKitManager.saveIfNeeded(pumpEvents: events)
-
         if let error = error {
         if let error = error {
             warning(.apsManager, "Loop failed with error: \(error.localizedDescription)")
             warning(.apsManager, "Loop failed with error: \(error.localizedDescription)")
             if let backgroundTask = backGroundTaskID {
             if let backgroundTask = backGroundTaskID {

+ 44 - 3
FreeAPS/Sources/Services/HealthKit/HealthKitManager.swift

@@ -31,7 +31,7 @@ protocol HealthKitManager: GlucoseSource {
     func deleteInsulin(syncID: String)
     func deleteInsulin(syncID: String)
 }
 }
 
 
-final class BaseHealthKitManager: HealthKitManager, Injectable, CarbsObserver {
+final class BaseHealthKitManager: HealthKitManager, Injectable, CarbsObserver, PumpHistoryObserver {
     private enum Config {
     private enum Config {
         // unwraped HKObjects
         // unwraped HKObjects
         static var readPermissions: Set<HKSampleType> {
         static var readPermissions: Set<HKSampleType> {
@@ -68,7 +68,7 @@ final class BaseHealthKitManager: HealthKitManager, Injectable, CarbsObserver {
         }
         }
         get {
         get {
             guard let data = persistedBGAnchor else { return nil }
             guard let data = persistedBGAnchor else { return nil }
-            return try? NSKeyedUnarchiver.unarchiveTopLevelObjectWithData(data) as? HKQueryAnchor
+            return try? NSKeyedUnarchiver.unarchivedObject(ofClass: HKQueryAnchor.self, from: data)
         }
         }
     }
     }
 
 
@@ -115,6 +115,7 @@ final class BaseHealthKitManager: HealthKitManager, Injectable, CarbsObserver {
         enableBackgroundDelivery()
         enableBackgroundDelivery()
 
 
         broadcaster.register(CarbsObserver.self, observer: self)
         broadcaster.register(CarbsObserver.self, observer: self)
+        broadcaster.register(PumpHistoryObserver.self, observer: self)
 
 
         debug(.service, "HealthKitManager did create")
         debug(.service, "HealthKitManager did create")
     }
     }
@@ -226,6 +227,21 @@ final class BaseHealthKitManager: HealthKitManager, Injectable, CarbsObserver {
               events.isNotEmpty
               events.isNotEmpty
         else { return }
         else { return }
 
 
+        func delete(syncIds: [String]?) {
+            syncIds?.forEach { syncID in
+                let predicate = HKQuery.predicateForObjects(
+                    withMetadataKey: HKMetadataKeySyncIdentifier,
+                    operatorType: .equalTo,
+                    value: syncID
+                )
+
+                self.healthKitStore.deleteObjects(of: sampleType, predicate: predicate) { _, _, error in
+                    guard let error = error else { return }
+                    warning(.service, "Cannot delete sample with syncID: \(syncID)", error: error)
+                }
+            }
+        }
+
         func save(bolus: [InsulinBolus], basal: [InsulinBasal]) {
         func save(bolus: [InsulinBolus], basal: [InsulinBasal]) {
             let bolusSamples = bolus
             let bolusSamples = bolus
                 .map {
                 .map {
@@ -264,6 +280,26 @@ final class BaseHealthKitManager: HealthKitManager, Injectable, CarbsObserver {
             healthKitStore.save(bolusSamples + basalSamples) { _, _ in }
             healthKitStore.save(bolusSamples + basalSamples) { _, _ in }
         }
         }
 
 
+        // delete existing event in HK where the amount is not the last value in the pumphistory
+        loadSamplesFromHealth(sampleType: sampleType, withIDs: events.map(\.id))
+            .receive(on: processQueue)
+            .compactMap { samples -> [String] in
+                let sampleIDs = samples.compactMap(\.syncIdentifier)
+                let bolusToDelete = events
+                    .filter { $0.type == .bolus && sampleIDs.contains($0.id) }
+                    .compactMap { event -> String? in
+                        guard let amount = event.amount else { return nil }
+                        guard let sampleAmount = samples.first(where: { $0.syncIdentifier == event.id }) as? HKQuantitySample
+                        else { return nil }
+                        if Double(amount) != sampleAmount.quantity.doubleValue(for: .internationalUnit()) {
+                            return sampleAmount.syncIdentifier
+                        } else { return nil }
+                    }
+                return bolusToDelete
+            }
+            .sink(receiveValue: delete)
+            .store(in: &lifetime)
+
         loadSamplesFromHealth(sampleType: sampleType, withIDs: events.map(\.id))
         loadSamplesFromHealth(sampleType: sampleType, withIDs: events.map(\.id))
             .receive(on: processQueue)
             .receive(on: processQueue)
             .compactMap { samples -> ([InsulinBolus], [InsulinBasal]) in
             .compactMap { samples -> ([InsulinBolus], [InsulinBasal]) in
@@ -276,6 +312,7 @@ final class BaseHealthKitManager: HealthKitManager, Injectable, CarbsObserver {
                     }
                     }
                 let basalEvents = events
                 let basalEvents = events
                     .filter { $0.type == .tempBasal && !sampleIDs.contains($0.id) }
                     .filter { $0.type == .tempBasal && !sampleIDs.contains($0.id) }
+                    .sorted(by: { $0.timestamp < $1.timestamp })
                 let basal = basalEvents.enumerated()
                 let basal = basalEvents.enumerated()
                     .compactMap { item -> InsulinBasal? in
                     .compactMap { item -> InsulinBasal? in
                         let nextElementEventIndex = item.offset + 1
                         let nextElementEventIndex = item.offset + 1
@@ -300,7 +337,7 @@ final class BaseHealthKitManager: HealthKitManager, Injectable, CarbsObserver {
                         }
                         }
 
 
                         let id = String(item.element.id.dropFirst())
                         let id = String(item.element.id.dropFirst())
-                        guard amountRounded >= 0,
+                        guard amountRounded > 0,
                               id != ""
                               id != ""
                         else { return nil }
                         else { return nil }
 
 
@@ -317,6 +354,10 @@ final class BaseHealthKitManager: HealthKitManager, Injectable, CarbsObserver {
             .store(in: &lifetime)
             .store(in: &lifetime)
     }
     }
 
 
+    func pumpHistoryDidUpdate(_ events: [PumpHistoryEvent]) {
+        saveIfNeeded(pumpEvents: events)
+    }
+
     func createBGObserver() {
     func createBGObserver() {
         guard settingsManager.settings.useAppleHealth else { return }
         guard settingsManager.settings.useAppleHealth else { return }