polscm32 2 vuotta sitten
vanhempi
commit
4316b6da59

+ 1 - 1
FreeAPS.xcodeproj/xcshareddata/xcschemes/FreeAPS X.xcscheme

@@ -63,7 +63,7 @@
       <CommandLineArguments>
          <CommandLineArgument
             argument = "-com.apple.CoreData.ConcurrencyDebug 1"
-            isEnabled = "NO">
+            isEnabled = "YES">
          </CommandLineArgument>
          <CommandLineArgument
             argument = "-com.apple.CoreData.SQLDebug 1"

+ 108 - 103
FreeAPS/Sources/APS/APSManager.swift

@@ -328,63 +328,65 @@ final class BaseAPSManager: APSManager, Injectable {
     }
 
     func determineBasal() -> AnyPublisher<Bool, Never> {
-        debug(.apsManager, "Start determine basal")
-        let glucose = fetchGlucose(predicate: NSPredicate.predicateFor30MinAgo, fetchLimit: 4)
-        guard glucose.count > 2 else {
-            debug(.apsManager, "Not enough glucose data")
-            processError(APSError.glucoseError(message: "Not enough glucose data"))
-            return Just(false).eraseToAnyPublisher()
-        }
-
-        let dateOfLastGlucose = glucose.first?.date
-        guard dateOfLastGlucose ?? Date() >= Date().addingTimeInterval(-12.minutes.timeInterval) else {
-            debug(.apsManager, "Glucose data is stale")
-            processError(APSError.glucoseError(message: "Glucose data is stale"))
-            return Just(false).eraseToAnyPublisher()
-        }
+        privateContext.performAndWait {
+            debug(.apsManager, "Start determine basal")
+            let glucose = fetchGlucose(predicate: NSPredicate.predicateFor30MinAgo, fetchLimit: 4)
+            guard glucose.count > 2 else {
+                debug(.apsManager, "Not enough glucose data")
+                processError(APSError.glucoseError(message: "Not enough glucose data"))
+                return Just(false).eraseToAnyPublisher()
+            }
 
-        // Only let glucose be flat when 400 mg/dl
-        if (glucose.first?.glucose ?? 100) != 400 {
-            guard !GlucoseStored.glucoseIsFlat(glucose) else {
-                debug(.apsManager, "Glucose data is too flat")
-                processError(APSError.glucoseError(message: "Glucose data is too flat"))
+            let dateOfLastGlucose = glucose.first?.date
+            guard dateOfLastGlucose ?? Date() >= Date().addingTimeInterval(-12.minutes.timeInterval) else {
+                debug(.apsManager, "Glucose data is stale")
+                processError(APSError.glucoseError(message: "Glucose data is stale"))
                 return Just(false).eraseToAnyPublisher()
             }
-        }
 
-        let now = Date()
-        let temp = currentTemp(date: now)
-
-        let mainPublisher = makeProfiles()
-            .flatMap { _ in self.autosens() }
-            .flatMap { _ in self.dailyAutotune() }
-            .flatMap { _ in self.openAPS.determineBasal(currentTemp: temp, clock: now) }
-            .map { determination -> Bool in
-                if let determination = determination {
-                    DispatchQueue.main.async {
-                        self.broadcaster.notify(DeterminationObserver.self, on: .main) {
-                            $0.determinationDidUpdate(determination)
+            // Only let glucose be flat when 400 mg/dl
+            if (glucose.first?.glucose ?? 100) != 400 {
+                guard !GlucoseStored.glucoseIsFlat(glucose) else {
+                    debug(.apsManager, "Glucose data is too flat")
+                    processError(APSError.glucoseError(message: "Glucose data is too flat"))
+                    return Just(false).eraseToAnyPublisher()
+                }
+            }
+
+            let now = Date()
+            let temp = currentTemp(date: now)
+
+            let mainPublisher = makeProfiles()
+                .flatMap { _ in self.autosens() }
+                .flatMap { _ in self.dailyAutotune() }
+                .flatMap { _ in self.openAPS.determineBasal(currentTemp: temp, clock: now) }
+                .map { determination -> Bool in
+                    if let determination = determination {
+                        DispatchQueue.main.async {
+                            self.broadcaster.notify(DeterminationObserver.self, on: .main) {
+                                $0.determinationDidUpdate(determination)
+                            }
                         }
                     }
+
+                    return determination != nil
                 }
+                .eraseToAnyPublisher()
 
-                return determination != nil
+            if temp.duration == 0,
+               settings.closedLoop,
+               settingsManager.preferences.unsuspendIfNoTemp,
+               let pump = pumpManager,
+               pump.status.pumpStatus.suspended
+            {
+                return pump.resumeDelivery()
+                    .flatMap { _ in mainPublisher }
+                    .replaceError(with: false)
+                    .eraseToAnyPublisher()
             }
-            .eraseToAnyPublisher()
 
-        if temp.duration == 0,
-           settings.closedLoop,
-           settingsManager.preferences.unsuspendIfNoTemp,
-           let pump = pumpManager,
-           pump.status.pumpStatus.suspended
-        {
-            return pump.resumeDelivery()
-                .flatMap { _ in mainPublisher }
-                .replaceError(with: false)
-                .eraseToAnyPublisher()
+            return mainPublisher
         }
-
-        return mainPublisher
     }
 
     func determineBasalSync() {
@@ -645,8 +647,9 @@ final class BaseAPSManager: APSManager, Injectable {
     }
 
     private func fetchDetermination() -> OrefDetermination? {
-        CoreDataStack.shared.fetchEntities(
+        CoreDataStack.shared.fetchEntities2(
             ofType: OrefDetermination.self,
+            onContext: privateContext,
             predicate: NSPredicate.predicateFor30MinAgoForDetermination,
             key: "deliverAt",
             ascending: false,
@@ -655,73 +658,74 @@ final class BaseAPSManager: APSManager, Injectable {
     }
 
     private func enactDetermination() -> AnyPublisher<Void, Error> {
-        guard let determination = fetchDetermination() else {
-            return Fail(error: APSError.apsError(message: "Determination not found")).eraseToAnyPublisher()
-        }
-
-        guard let pump = pumpManager else {
-            return Fail(error: APSError.apsError(message: "Pump not set")).eraseToAnyPublisher()
-        }
-
-        // unable to do temp basal during manual temp basal 😁
-        if isManualTempBasal {
-            return Fail(error: APSError.manualBasalTemp(message: "Loop not possible during the manual basal temp"))
-                .eraseToAnyPublisher()
-        }
-
-        let basalPublisher: AnyPublisher<Void, Error> = Deferred { () -> AnyPublisher<Void, Error> in
-            if let error = self.verifyStatus() {
-                return Fail(error: error).eraseToAnyPublisher()
+            guard let determination = fetchDetermination() else {
+                return Fail(error: APSError.apsError(message: "Determination not found")).eraseToAnyPublisher()
             }
 
-            guard let rate = determination.rate else {
-                debug(.apsManager, "No temp required")
-                return Just(()).setFailureType(to: Error.self)
-                    .eraseToAnyPublisher()
-            }
-            return pump.enactTempBasal(
-                unitsPerHour: Double(truncating: rate),
-                for: TimeInterval(determination.duration * 60)
-            ).map { _ in
-                let temp = TempBasal(
-                    duration: Int(determination.duration),
-                    rate: ((determination.rate ?? 0) as NSDecimalNumber) as Decimal,
-                    temp: .absolute,
-                    timestamp: Date()
-                )
-                self.storage.save(temp, as: OpenAPS.Monitor.tempBasal)
-                return ()
+            guard let pump = pumpManager else {
+                return Fail(error: APSError.apsError(message: "Pump not set")).eraseToAnyPublisher()
             }
-            .eraseToAnyPublisher()
-        }.eraseToAnyPublisher()
 
-        let bolusPublisher: AnyPublisher<Void, Error> = Deferred { () -> AnyPublisher<Void, Error> in
-            if let error = self.verifyStatus() {
-                return Fail(error: error).eraseToAnyPublisher()
-            }
-            guard let smbToDeliver = determination.smbToDeliver else {
-                debug(.apsManager, "No bolus required")
-                return Just(()).setFailureType(to: Error.self)
+            // unable to do temp basal during manual temp basal 😁
+            if isManualTempBasal {
+                return Fail(error: APSError.manualBasalTemp(message: "Loop not possible during the manual basal temp"))
                     .eraseToAnyPublisher()
             }
-            return pump.enactBolus(units: Double(truncating: smbToDeliver), automatic: true).map { _ in
-                self.bolusProgress.send(0)
-                return ()
-            }
-            .eraseToAnyPublisher()
-        }.eraseToAnyPublisher()
 
-        return basalPublisher.flatMap { bolusPublisher }.eraseToAnyPublisher()
+            let basalPublisher: AnyPublisher<Void, Error> = Deferred { () -> AnyPublisher<Void, Error> in
+                if let error = self.verifyStatus() {
+                    return Fail(error: error).eraseToAnyPublisher()
+                }
+
+                guard let rate = determination.rate else {
+                    debug(.apsManager, "No temp required")
+                    return Just(()).setFailureType(to: Error.self)
+                        .eraseToAnyPublisher()
+                }
+                return pump.enactTempBasal(
+                    unitsPerHour: Double(truncating: rate),
+                    for: TimeInterval(determination.duration * 60)
+                ).map { _ in
+                    let temp = TempBasal(
+                        duration: Int(determination.duration),
+                        rate: ((determination.rate ?? 0) as NSDecimalNumber) as Decimal,
+                        temp: .absolute,
+                        timestamp: Date()
+                    )
+                    self.storage.save(temp, as: OpenAPS.Monitor.tempBasal)
+                    return ()
+                }
+                .eraseToAnyPublisher()
+            }.eraseToAnyPublisher()
+
+            let bolusPublisher: AnyPublisher<Void, Error> = Deferred { () -> AnyPublisher<Void, Error> in
+                if let error = self.verifyStatus() {
+                    return Fail(error: error).eraseToAnyPublisher()
+                }
+                guard let smbToDeliver = determination.smbToDeliver else {
+                    debug(.apsManager, "No bolus required")
+                    return Just(()).setFailureType(to: Error.self)
+                        .eraseToAnyPublisher()
+                }
+                return pump.enactBolus(units: Double(truncating: smbToDeliver), automatic: true).map { _ in
+                    self.bolusProgress.send(0)
+                    return ()
+                }
+                .eraseToAnyPublisher()
+            }.eraseToAnyPublisher()
+
+            return basalPublisher.flatMap { bolusPublisher }.eraseToAnyPublisher()
+        
     }
 
     private func reportEnacted(received: Bool) {
-        guard let determination = fetchDetermination(), determination.deliverAt != nil else {
-            return
-        }
+        privateContext.performAndWait {
+            guard let determination = fetchDetermination(), determination.deliverAt != nil else {
+                return
+            }
 
-        let objectID = determination.objectID
+            let objectID = determination.objectID
 
-        privateContext.performAndWait {
             if let determinationUpdated = self.privateContext.object(with: objectID) as? OrefDetermination {
                 determinationUpdated.timestamp = Date()
                 determinationUpdated.received = received
@@ -746,7 +750,8 @@ final class BaseAPSManager: APSManager, Injectable {
             saveLastLoop.timestamp = (determination.timestamp ?? .distantPast) as Date
 
             do {
-                try CoreDataStack.shared.saveContext()
+                guard privateContext.hasChanges else { return }
+                try privateContext.save()
             } catch {
                 print(error.localizedDescription)
             }

+ 11 - 9
FreeAPS/Sources/APS/OpenAPS/OpenAPS.swift

@@ -134,15 +134,17 @@ final class OpenAPS {
     }
 
     private func fetchPumpHistoryObjectIDs() -> [NSManagedObjectID]? {
-        let results = CoreDataStack.shared.fetchEntities2(
-            ofType: PumpEventStored.self,
-            onContext: context,
-            predicate: NSPredicate.pumpHistoryLast24h,
-            key: "timestamp",
-            ascending: false,
-            batchSize: 50
-        )
-        return results.map(\.objectID)
+        context.performAndWait {
+            let results = CoreDataStack.shared.fetchEntities2(
+                ofType: PumpEventStored.self,
+                onContext: context,
+                predicate: NSPredicate.pumpHistoryLast24h,
+                key: "timestamp",
+                ascending: false,
+                batchSize: 50
+            )
+            return results.map(\.objectID)
+        }
     }
 
     private func parsePumpHistory(_ pumpHistoryObjectIDs: [NSManagedObjectID]) -> String {

+ 1 - 4
FreeAPS/Sources/APS/Storage/CarbsStorage.swift

@@ -153,10 +153,7 @@ final class BaseCarbsStorage: CarbsStorage, Injectable {
             newItem.id = UUID()
             newItem.isFPU = false
             do {
-//                try CoreDataStack.shared.saveContext()
-                guard self.coredataContext.hasChanges else {
-                    return
-                }
+                guard self.coredataContext.hasChanges else { return }
                 try self.coredataContext.save()
             } catch {
                 print(error.localizedDescription)

+ 4 - 2
FreeAPS/Sources/APS/Storage/GlucoseStorage.swift

@@ -231,8 +231,9 @@ final class BaseGlucoseStorage: GlucoseStorage, Injectable {
     ///
     func fetchGlucose() -> [GlucoseStored] {
         let predicate = NSPredicate.predicateForOneDayAgo
-        return CoreDataStack.shared.fetchEntities(
+        return CoreDataStack.shared.fetchEntities2(
             ofType: GlucoseStored.self,
+            onContext: coredataContext,
             predicate: predicate,
             key: "date",
             ascending: false,
@@ -255,8 +256,9 @@ final class BaseGlucoseStorage: GlucoseStorage, Injectable {
 
     func fetchLatestGlucose() -> GlucoseStored? {
         let predicate = NSPredicate.predicateFor20MinAgo
-        return CoreDataStack.shared.fetchEntities(
+        return CoreDataStack.shared.fetchEntities2(
             ofType: GlucoseStored.self,
+            onContext: coredataContext,
             predicate: predicate,
             key: "date",
             ascending: false,

+ 6 - 3
FreeAPS/Sources/APS/Storage/PumpHistoryStorage.swift

@@ -103,7 +103,8 @@ final class BasePumpHistoryStorage: PumpHistoryStorage, Injectable {
                         newTempBasal.tempType = TempType.absolute.rawValue
 
                         do {
-                            try CoreDataStack.shared.saveContext()
+                            guard self.context.hasChanges else { return }
+                            try self.context.save()
                         } catch {
                             print(error.localizedDescription)
                         }
@@ -142,7 +143,8 @@ final class BasePumpHistoryStorage: PumpHistoryStorage, Injectable {
                         newPumpEvent.type = PumpEvent.pumpSuspend.rawValue
 
                         do {
-                            try CoreDataStack.shared.saveContext()
+                            guard self.context.hasChanges else { return }
+                            try self.context.save()
                         } catch {
                             print(error.localizedDescription)
                         }
@@ -169,7 +171,8 @@ final class BasePumpHistoryStorage: PumpHistoryStorage, Injectable {
                         newPumpEvent.type = PumpEvent.pumpResume.rawValue
 
                         do {
-                            try CoreDataStack.shared.saveContext()
+                            guard self.context.hasChanges else { return }
+                            try self.context.save()
                         } catch {
                             print(error.localizedDescription)
                         }

+ 38 - 33
FreeAPS/Sources/Services/Network/NightscoutManager.swift

@@ -383,32 +383,34 @@ final class BaseNightscoutManager: NightscoutManager, Injectable {
     }
 
     private func fetchBattery() -> Battery {
-        do {
-            let results = try context.fetch(OpenAPS_Battery.fetch(NSPredicate.predicateFor30MinAgo))
-            if let last = results.first {
-                let percent: Int? = Int(last.percent)
-                let voltage: Decimal? = last.voltage as Decimal?
-                let status: String? = last.status
-                let display: Bool? = last.display
-
-                if let percent = percent, let voltage = voltage, let status = status, let display = display {
-                    debugPrint(
-                        "Home State Model: \(#function) \(DebuggingIdentifiers.succeeded) setup battery from core data successfully"
-                    )
-                    return Battery(
-                        percent: percent,
-                        voltage: voltage,
-                        string: BatteryState(rawValue: status) ?? BatteryState.normal,
-                        display: display
-                    )
+        context.performAndWait {
+            do {
+                let results = try context.fetch(OpenAPS_Battery.fetch(NSPredicate.predicateFor30MinAgo))
+                if let last = results.first {
+                    let percent: Int? = Int(last.percent)
+                    let voltage: Decimal? = last.voltage as Decimal?
+                    let status: String? = last.status
+                    let display: Bool? = last.display
+
+                    if let percent = percent, let voltage = voltage, let status = status, let display = display {
+                        debugPrint(
+                            "Home State Model: \(#function) \(DebuggingIdentifiers.succeeded) setup battery from core data successfully"
+                        )
+                        return Battery(
+                            percent: percent,
+                            voltage: voltage,
+                            string: BatteryState(rawValue: status) ?? BatteryState.normal,
+                            display: display
+                        )
+                    }
                 }
+                return Battery(percent: 100, voltage: 100, string: BatteryState.normal, display: false)
+            } catch {
+                debugPrint(
+                    "Home State Model: \(#function) \(DebuggingIdentifiers.failed) failed to setup battery from core data"
+                )
+                return Battery(percent: 100, voltage: 100, string: BatteryState.normal, display: false)
             }
-            return Battery(percent: 100, voltage: 100, string: BatteryState.normal, display: false)
-        } catch {
-            debugPrint(
-                "Home State Model: \(#function) \(DebuggingIdentifiers.failed) failed to setup battery from core data"
-            )
-            return Battery(percent: 100, voltage: 100, string: BatteryState.normal, display: false)
         }
     }
 
@@ -417,15 +419,18 @@ final class BaseNightscoutManager: NightscoutManager, Injectable {
         fetchRequest.sortDescriptors = [NSSortDescriptor(keyPath: \OrefDetermination.deliverAt, ascending: false)]
         fetchRequest.predicate = NSPredicate.predicateFor30MinAgoForDetermination
         fetchRequest.fetchLimit = 2
-        do {
-            lastTwoDeterminations = try context.fetch(fetchRequest)
-            debugPrint(
-                "Home State Model: \(#function) \(DebuggingIdentifiers.succeeded) fetched determinations from core data"
-            )
-        } catch {
-            debugPrint(
-                "Home State Model: \(#function) \(DebuggingIdentifiers.failed) failed to fetch determinations from core data"
-            )
+
+        context.performAndWait {
+            do {
+                lastTwoDeterminations = try context.fetch(fetchRequest)
+                debugPrint(
+                    "Home State Model: \(#function) \(DebuggingIdentifiers.succeeded) fetched determinations from core data"
+                )
+            } catch {
+                debugPrint(
+                    "Home State Model: \(#function) \(DebuggingIdentifiers.failed) failed to fetch determinations from core data"
+                )
+            }
         }
     }
 

+ 2 - 1
FreeAPS/Sources/Services/WatchManager/WatchManager.swift

@@ -83,8 +83,9 @@ final class BaseWatchManager: NSObject, WatchManager, Injectable {
 
         context.performAndWait {
             let predicate = NSPredicate.predicateFor120MinAgo
-            let fetchedGlucose = CoreDataStack.shared.fetchEntities(
+            let fetchedGlucose = CoreDataStack.shared.fetchEntities2(
                 ofType: GlucoseStored.self,
+                onContext: context,
                 predicate: predicate,
                 key: "date",
                 ascending: false,