Deniz Cengiz пре 1 година
родитељ
комит
c64d85d8f8

+ 27 - 24
FreeAPS/Sources/APS/APSManager.swift

@@ -938,32 +938,34 @@ final class BaseAPSManager: APSManager, Injectable {
 
             // MARK: - Core Data related
 
-            let glucoseStats = await glucoseForStats()
-            let lastLoopForStats = await lastLoopForStats()
-            let loopStats = await loopStats(oneDayGlucose: glucoseStats.oneDayGlucose.readings)
-            let carbTotal = await carbsForStats()
+            async let glucoseStats = glucoseForStats()
+            async let lastLoopForStats = lastLoopForStats()
+            async let carbTotal = carbsForStats()
+            async let preferences = settingsManager.preferences
+
+            let loopStats = await loopStats(oneDayGlucose: await glucoseStats.oneDayGlucose.readings)
 
             // Only save and upload once per day
-            guard (-1 * (lastLoopForStats ?? .distantPast).timeIntervalSinceNow.hours) > 22 else { return }
+            guard (-1 * (await lastLoopForStats ?? .distantPast).timeIntervalSinceNow.hours) > 22 else { return }
 
             let units = settingsManager.settings.units
-            let preferences = settingsManager.preferences
 
             // MARK: - Not Core Data related stuff
 
+            let pref = await preferences
             var algo_ = "Oref0"
 
-            if preferences.sigmoid, preferences.enableDynamicCR {
+            if pref.sigmoid, pref.enableDynamicCR {
                 algo_ = "Dynamic ISF + CR: Sigmoid"
-            } else if preferences.sigmoid, !preferences.enableDynamicCR {
+            } else if pref.sigmoid, !pref.enableDynamicCR {
                 algo_ = "Dynamic ISF: Sigmoid"
-            } else if preferences.useNewFormula, preferences.enableDynamicCR {
+            } else if pref.useNewFormula, pref.enableDynamicCR {
                 algo_ = "Dynamic ISF + CR: Logarithmic"
-            } else if preferences.useNewFormula, !preferences.sigmoid,!preferences.enableDynamicCR {
+            } else if pref.useNewFormula, !pref.sigmoid,!pref.enableDynamicCR {
                 algo_ = "Dynamic ISF: Logarithmic"
             }
-            let af = preferences.adjustmentFactor
-            let insulin_type = preferences.curve
+            let af = pref.adjustmentFactor
+            let insulin_type = pref.curve
             let buildDate = Bundle.main.buildDate
             let version = Bundle.main.releaseVersionNumber
             let build = Bundle.main.buildVersionNumber
@@ -995,11 +997,11 @@ final class BaseAPSManager: APSManager, Injectable {
             let cgm = settingsManager.settings.cgm
             let file = OpenAPS.Monitor.statistics
             var iPa: Decimal = 75
-            if preferences.useCustomPeakTime {
-                iPa = preferences.insulinPeakTime
-            } else if preferences.curve.rawValue == "rapid-acting" {
+            if pref.useCustomPeakTime {
+                iPa = pref.insulinPeakTime
+            } else if pref.curve.rawValue == "rapid-acting" {
                 iPa = 65
-            } else if preferences.curve.rawValue == "ultra-rapid" {
+            } else if pref.curve.rawValue == "ultra-rapid" {
                 iPa = 50
             }
 
@@ -1012,7 +1014,8 @@ final class BaseAPSManager: APSManager, Injectable {
                 total_average: 0
             )
 
-            let overrideHbA1cUnit = glucoseStats.overrideHbA1cUnit
+            let gs = await glucoseStats
+            let overrideHbA1cUnit = gs.overrideHbA1cUnit
             let hbA1cUnit = !overrideHbA1cUnit ? (units == .mmolL ? "mmol/mol" : "%") : (units == .mmolL ? "%" : "mmol/mol")
 
             let dailystat = await Statistics(
@@ -1030,19 +1033,19 @@ final class BaseAPSManager: APSManager, Injectable {
                 CGM: cgm.rawValue,
                 insulinType: insulin_type.rawValue,
                 peakActivityTime: iPa,
-                Carbs_24h: carbTotal,
-                GlucoseStorage_Days: Decimal(roundDouble(glucoseStats.numberofDays, 1)),
+                Carbs_24h: await carbTotal,
+                GlucoseStorage_Days: Decimal(roundDouble(gs.numberofDays, 1)),
                 Statistics: Stats(
-                    Distribution: glucoseStats.TimeInRange,
-                    Glucose: glucoseStats.avg,
-                    HbA1c: glucoseStats.hbs, Units: Units(Glucose: units.rawValue, HbA1c: hbA1cUnit),
+                    Distribution: gs.TimeInRange,
+                    Glucose: gs.avg,
+                    HbA1c: gs.hbs, Units: Units(Glucose: units.rawValue, HbA1c: hbA1cUnit),
                     LoopCycles: loopStats,
                     Insulin: insulin,
-                    Variance: glucoseStats.variance
+                    Variance: gs.variance
                 )
             )
             storage.save(dailystat, as: file)
-            nightscout.uploadStatistics(dailystat: dailystat)
+            await nightscout.uploadStatistics(dailystat: dailystat)
 
             await saveStatsToCoreData()
         }

+ 8 - 10
FreeAPS/Sources/APS/FetchTreatmentsManager.swift

@@ -22,23 +22,21 @@ final class BaseFetchTreatmentsManager: FetchTreatmentsManager, Injectable {
     private func subscribe() {
         timer.publisher
             .receive(on: processQueue)
-            .flatMap { _ -> AnyPublisher<([CarbsEntry], [TempTarget]), Never> in
+            .sink { [weak self] _ in
+                guard let self = self else { return }
                 debug(.nightscout, "FetchTreatmentsManager heartbeat")
                 debug(.nightscout, "Start fetching carbs and temptargets")
-                return Publishers.CombineLatest(
-                    self.nightscoutManager.fetchCarbs(),
-                    self.nightscoutManager.fetchTempTargets()
-                ).eraseToAnyPublisher()
-            }
-            .sink { [weak self] carbs, targets in
-                guard let self = self else { return }
 
                 Task {
-                    let filteredCarbs = carbs.filter { !($0.enteredBy?.contains(CarbsEntry.manual) ?? false) }
+                    async let carbs = self.nightscoutManager.fetchCarbs()
+                    async let tempTargets = self.nightscoutManager.fetchTempTargets()
+
+                    let filteredCarbs = await carbs.filter { !($0.enteredBy?.contains(CarbsEntry.manual) ?? false) }
                     if filteredCarbs.isNotEmpty {
                         await self.carbsStorage.storeCarbs(filteredCarbs)
                     }
-                    let filteredTargets = targets.filter { !($0.enteredBy?.contains(TempTarget.manual) ?? false) }
+
+                    let filteredTargets = await tempTargets.filter { !($0.enteredBy?.contains(TempTarget.manual) ?? false) }
                     if filteredTargets.isNotEmpty {
                         self.tempTargetsStorage.storeTempTargets(filteredTargets)
                     }

+ 2 - 1
FreeAPS/Sources/APS/Storage/OverrideStorage.swift

@@ -219,7 +219,8 @@ final class BaseOverrideStorage: OverrideStorage, Injectable {
 
         return await backgroundContext.perform {
             return fetchedOverrideRuns.map { overrideRun in
-                let durationInMinutes = (overrideRun.endDate?.timeIntervalSince(overrideRun.startDate ?? Date()) ?? 0) / 60
+                var durationInMinutes = (overrideRun.endDate?.timeIntervalSince(overrideRun.startDate ?? Date()) ?? 0) / 60
+                durationInMinutes = durationInMinutes < 1 ? 1 : durationInMinutes
                 return NightscoutExercise(
                     duration: Int(durationInMinutes),
                     eventType: OverrideStored.EventType.nsExercise,

+ 1 - 1
FreeAPS/Sources/Modules/OverrideProfilesConfig/OverrideProfilesStateModel.swift

@@ -172,7 +172,7 @@ extension OverrideProfilesConfig.StateModel {
 
             /// disable all active Overrides and reset state variables
             /// do not create a OverrideRunEntry because we only want that if we cancel a running Override, not when enacting a Preset
-            await disableAllActiveOverrides(except: id, createOverrideRunEntry: false)
+            await disableAllActiveOverrides(except: id, createOverrideRunEntry: currentActiveOverride != nil)
 
             await resetStateVariables()
 

+ 33 - 51
FreeAPS/Sources/Services/Network/NightscoutAPI.swift

@@ -98,7 +98,7 @@ extension NightscoutAPI {
         }
     }
 
-    func fetchCarbs(sinceDate: Date? = nil) -> AnyPublisher<[CarbsEntry], Swift.Error> {
+    func fetchCarbs(sinceDate: Date? = nil) async throws -> [CarbsEntry] {
         var components = URLComponents()
         components.scheme = url.scheme
         components.host = url.host
@@ -131,14 +131,19 @@ extension NightscoutAPI {
             request.addValue(secret.sha1(), forHTTPHeaderField: "api-secret")
         }
 
-        return service.run(request)
-            .retry(Config.retryCount)
-            .decode(type: [CarbsEntry].self, decoder: JSONCoding.decoder)
-            .catch { error -> AnyPublisher<[CarbsEntry], Swift.Error> in
-                warning(.nightscout, "Carbs fetching error: \(error.localizedDescription)")
-                return Just([]).setFailureType(to: Swift.Error.self).eraseToAnyPublisher()
+        do {
+            let (data, response) = try await URLSession.shared.data(for: request)
+
+            guard let httpResponse = response as? HTTPURLResponse, (200 ... 299).contains(httpResponse.statusCode) else {
+                throw URLError(.badServerResponse)
             }
-            .eraseToAnyPublisher()
+
+            let carbs = try JSONCoding.decoder.decode([CarbsEntry].self, from: data)
+            return carbs
+        } catch {
+            warning(.nightscout, "Carbs fetching error: \(error.localizedDescription)")
+            throw error
+        }
     }
 
     func deleteCarbs(withId id: String) async throws {
@@ -232,36 +237,7 @@ extension NightscoutAPI {
         }
     }
 
-//    func deleteInsulin(at date: Date) -> AnyPublisher<Void, Swift.Error> {
-//        var components = URLComponents()
-//        components.scheme = url.scheme
-//        components.host = url.host
-//        components.port = url.port
-//        components.path = Config.treatmentsPath
-//        components.queryItems = [
-//            URLQueryItem(name: "find[bolus][$exists]", value: "true"),
-//            URLQueryItem(
-//                name: "find[created_at][$eq]",
-//                value: Formatter.iso8601withFractionalSeconds.string(from: date)
-//            )
-//        ]
-//
-//        var request = URLRequest(url: components.url!)
-//        request.allowsConstrainedNetworkAccess = false
-//        request.timeoutInterval = Config.timeout
-//        request.httpMethod = "DELETE"
-//
-//        if let secret = secret {
-//            request.addValue(secret.sha1(), forHTTPHeaderField: "api-secret")
-//        }
-//
-//        return service.run(request)
-//            .retry(Config.retryCount)
-//            .map { _ in () }
-//            .eraseToAnyPublisher()
-//    }
-
-    func fetchTempTargets(sinceDate: Date? = nil) -> AnyPublisher<[TempTarget], Swift.Error> {
+    func fetchTempTargets(sinceDate: Date? = nil) async throws -> [TempTarget] {
         var components = URLComponents()
         components.scheme = url.scheme
         components.host = url.host
@@ -295,14 +271,19 @@ extension NightscoutAPI {
             request.addValue(secret.sha1(), forHTTPHeaderField: "api-secret")
         }
 
-        return service.run(request)
-            .retry(Config.retryCount)
-            .decode(type: [TempTarget].self, decoder: JSONCoding.decoder)
-            .catch { error -> AnyPublisher<[TempTarget], Swift.Error> in
-                warning(.nightscout, "TempTarget fetching error: \(error.localizedDescription)")
-                return Just([]).setFailureType(to: Swift.Error.self).eraseToAnyPublisher()
+        do {
+            let (data, response) = try await URLSession.shared.data(for: request)
+
+            guard let httpResponse = response as? HTTPURLResponse, (200 ... 299).contains(httpResponse.statusCode) else {
+                throw URLError(.badServerResponse)
             }
-            .eraseToAnyPublisher()
+
+            let tempTargets = try JSONCoding.decoder.decode([TempTarget].self, from: data)
+            return tempTargets
+        } catch {
+            warning(.nightscout, "TempTarget fetching error: \(error.localizedDescription)")
+            throw error
+        }
     }
 
     func fetchAnnouncement(sinceDate: Date? = nil) -> AnyPublisher<[Announcement], Swift.Error> {
@@ -417,7 +398,7 @@ extension NightscoutAPI {
         debugPrint("Upload successful, response data: \(String(data: data, encoding: .utf8) ?? "No data")")
     }
 
-    func uploadStats(_ stats: NightscoutStatistics) -> AnyPublisher<Void, Swift.Error> {
+    func uploadStats(_ stats: NightscoutStatistics) async throws {
         var components = URLComponents()
         components.scheme = url.scheme
         components.host = url.host
@@ -432,13 +413,14 @@ extension NightscoutAPI {
         if let secret = secret {
             request.addValue(secret.sha1(), forHTTPHeaderField: "api-secret")
         }
-        request.httpBody = try! JSONCoding.encoder.encode(stats)
+        request.httpBody = try JSONCoding.encoder.encode(stats)
         request.httpMethod = "POST"
 
-        return service.run(request)
-            .retry(Config.retryCount)
-            .map { _ in () }
-            .eraseToAnyPublisher()
+        let (data, response) = try await URLSession.shared.data(for: request)
+
+        guard let httpResponse = response as? HTTPURLResponse, (200 ... 299).contains(httpResponse.statusCode) else {
+            throw URLError(.badServerResponse)
+        }
     }
 
     func uploadStatus(_ status: NightscoutStatus) -> AnyPublisher<Void, Swift.Error> {

+ 51 - 28
FreeAPS/Sources/Services/Network/NightscoutManager.swift

@@ -7,8 +7,8 @@ import UIKit
 
 protocol NightscoutManager: GlucoseSource {
     func fetchGlucose(since date: Date) async -> [BloodGlucose]
-    func fetchCarbs() -> AnyPublisher<[CarbsEntry], Never>
-    func fetchTempTargets() -> AnyPublisher<[TempTarget], Never>
+    func fetchCarbs() async -> [CarbsEntry]
+    func fetchTempTargets() async -> [TempTarget]
     func fetchAnnouncements() -> AnyPublisher<[Announcement], Never>
     func deleteCarbs(withID id: String) async
     func deleteInsulin(withID id: String) async
@@ -16,7 +16,7 @@ protocol NightscoutManager: GlucoseSource {
     func uploadStatus()
     func uploadGlucose() async
     func uploadManualGlucose() async
-    func uploadStatistics(dailystat: Statistics)
+    func uploadStatistics(dailystat: Statistics) async
     func uploadPreferences(_ preferences: Preferences)
     func uploadProfileAndSettings(_: Bool)
     var cgmURL: URL? { get }
@@ -154,26 +154,34 @@ final class BaseNightscoutManager: NightscoutManager, Injectable {
         fetch(nil)
     }
 
-    func fetchCarbs() -> AnyPublisher<[CarbsEntry], Never> {
+    func fetchCarbs() async -> [CarbsEntry] {
         guard let nightscout = nightscoutAPI, isNetworkReachable else {
-            return Just([]).eraseToAnyPublisher()
+            return []
         }
 
         let since = carbsStorage.syncDate()
-        return nightscout.fetchCarbs(sinceDate: since)
-            .replaceError(with: [])
-            .eraseToAnyPublisher()
+        do {
+            let carbs = try await nightscout.fetchCarbs(sinceDate: since)
+            return carbs
+        } catch {
+            debug(.nightscout, "Error fetching carbs: \(error.localizedDescription)")
+            return []
+        }
     }
 
-    func fetchTempTargets() -> AnyPublisher<[TempTarget], Never> {
+    func fetchTempTargets() async -> [TempTarget] {
         guard let nightscout = nightscoutAPI, isNetworkReachable else {
-            return Just([]).eraseToAnyPublisher()
+            return []
         }
 
         let since = tempTargetsStorage.syncDate()
-        return nightscout.fetchTempTargets(sinceDate: since)
-            .replaceError(with: [])
-            .eraseToAnyPublisher()
+        do {
+            let tempTargets = try await nightscout.fetchTempTargets(sinceDate: since)
+            return tempTargets
+        } catch {
+            debug(.nightscout, "Error fetching temp targets: \(error.localizedDescription)")
+            return []
+        }
     }
 
     func fetchAnnouncements() -> AnyPublisher<[Announcement], Never> {
@@ -231,29 +239,44 @@ final class BaseNightscoutManager: NightscoutManager, Injectable {
         }
     }
 
-    func uploadStatistics(dailystat: Statistics) {
-        let stats = NightscoutStatistics(
-            dailystats: dailystat
-        )
+    func uploadStatistics(dailystat: Statistics) async {
+        let stats = NightscoutStatistics(dailystats: dailystat)
 
         guard let nightscout = nightscoutAPI, isUploadEnabled else {
             return
         }
 
-        processQueue.async {
-            nightscout.uploadStats(stats)
-                .sink { completion in
-                    switch completion {
-                    case .finished:
-                        debug(.nightscout, "Statistics uploaded")
-                    case let .failure(error):
-                        debug(.nightscout, error.localizedDescription)
-                    }
-                } receiveValue: {}
-                .store(in: &self.lifetime)
+        do {
+            try await nightscout.uploadStats(stats)
+            debug(.nightscout, "Statistics uploaded")
+        } catch {
+            debug(.nightscout, error.localizedDescription)
         }
     }
 
+//    func uploadStatistics(dailystat: Statistics) {
+//        let stats = NightscoutStatistics(
+//            dailystats: dailystat
+//        )
+//
+//        guard let nightscout = nightscoutAPI, isUploadEnabled else {
+//            return
+//        }
+//
+//        processQueue.async {
+//            nightscout.uploadStats(stats)
+//                .sink { completion in
+//                    switch completion {
+//                    case .finished:
+//                        debug(.nightscout, "Statistics uploaded")
+//                    case let .failure(error):
+//                        debug(.nightscout, error.localizedDescription)
+//                    }
+//                } receiveValue: {}
+//                .store(in: &self.lifetime)
+//        }
+//    }
+
     func uploadPreferences(_ preferences: Preferences) {
         let prefs = NightscoutPreferences(
             preferences: settingsManager.preferences