Просмотр исходного кода

connect APS suggestion to pump

Ivan Valkou 5 лет назад
Родитель
Сommit
fc47ae1309

+ 4 - 0
FreeAPS.xcodeproj/project.pbxproj

@@ -84,6 +84,7 @@
 		384E803425C385E60086DB71 /* JavaScriptWorker.swift in Sources */ = {isa = PBXBuildFile; fileRef = 384E803325C385E60086DB71 /* JavaScriptWorker.swift */; };
 		384E803825C388640086DB71 /* Script.swift in Sources */ = {isa = PBXBuildFile; fileRef = 384E803725C388640086DB71 /* Script.swift */; };
 		3870FF4725EC187A0088248F /* BloodGlucose.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3870FF4225EC13F40088248F /* BloodGlucose.swift */; };
+		3871F38725ED661C0013ECB5 /* Suggestion.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3871F38625ED661C0013ECB5 /* Suggestion.swift */; };
 		388E595C25AD948C0019842D /* FreeAPSApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 388E595B25AD948C0019842D /* FreeAPSApp.swift */; };
 		388E596025AD948E0019842D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 388E595F25AD948E0019842D /* Assets.xcassets */; };
 		388E596C25AD95110019842D /* OpenAPS.swift in Sources */ = {isa = PBXBuildFile; fileRef = 388E596B25AD95110019842D /* OpenAPS.swift */; };
@@ -596,6 +597,7 @@
 		384E803325C385E60086DB71 /* JavaScriptWorker.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = JavaScriptWorker.swift; sourceTree = "<group>"; };
 		384E803725C388640086DB71 /* Script.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Script.swift; sourceTree = "<group>"; };
 		3870FF4225EC13F40088248F /* BloodGlucose.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BloodGlucose.swift; sourceTree = "<group>"; };
+		3871F38625ED661C0013ECB5 /* Suggestion.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Suggestion.swift; sourceTree = "<group>"; };
 		388E595825AD948C0019842D /* FreeAPS.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = FreeAPS.app; sourceTree = BUILT_PRODUCTS_DIR; };
 		388E595B25AD948C0019842D /* FreeAPSApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FreeAPSApp.swift; sourceTree = "<group>"; };
 		388E595F25AD948E0019842D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
@@ -1077,6 +1079,7 @@
 				38D0B3D825EC07C400CB6E88 /* CarbHystoryEntry.swift */,
 				3870FF4225EC13F40088248F /* BloodGlucose.swift */,
 				38A0364125ED069400FCBB52 /* TempBasal.swift */,
+				3871F38625ED661C0013ECB5 /* Suggestion.swift */,
 			);
 			path = Models;
 			sourceTree = "<group>";
@@ -1760,6 +1763,7 @@
 				3811DE4325C9D4A100A708ED /* SettingsProvider.swift in Sources */,
 				E102DE9C3E9C8AEDCB3C61BB /* ConfigEditorBuilder.swift in Sources */,
 				45252C95D220E796FDB3B022 /* ConfigEditorDataFlow.swift in Sources */,
+				3871F38725ED661C0013ECB5 /* Suggestion.swift in Sources */,
 				38C4D33A25E9A1ED00D30B77 /* NSObject+AssociatedValues.swift in Sources */,
 				72F1BD388F42FCA6C52E4500 /* ConfigEditorProvider.swift in Sources */,
 				E39E418C56A5A46B61D960EE /* ConfigEditorViewModel.swift in Sources */,

+ 66 - 1
FreeAPS/Sources/APS/APSManager.swift

@@ -1,5 +1,6 @@
 import Combine
 import Foundation
+import LoopKit
 import LoopKitUI
 import Swinject
 
@@ -23,6 +24,8 @@ final class BaseAPSManager: APSManager, Injectable {
     private var openAPS: OpenAPS!
 
     private var glucoseCancellable: AnyCancellable?
+    private var determineBasalCancellable: AnyCancellable?
+    private var enactCancellable: AnyCancellable?
 
     var pumpManager: PumpManagerUI? {
         get {
@@ -40,12 +43,25 @@ final class BaseAPSManager: APSManager, Injectable {
         openAPS = OpenAPS(storage: storage)
     }
 
+    func loop() {
+        glucoseCancellable = fetchLastGlucose()
+    }
+
     func determineBasal() {
         let now = Date()
         guard let temp = currentTemp(date: now) else {
             return
         }
-        openAPS.determineBasal(currentTemp: temp, clock: now)
+        determineBasalCancellable = openAPS
+            .determineBasal(currentTemp: temp, clock: now)
+            .sink { [weak self] in
+                guard let self = self,
+                      let suggested = try? self.storage.retrieve(OpenAPS.Enact.suggested, as: Suggestion.self)
+                else {
+                    return
+                }
+                self.enact(suggested: suggested)
+            }
     }
 
     func runTest() {
@@ -98,4 +114,53 @@ final class BaseAPSManager: APSManager, Injectable {
         default: return nil
         }
     }
+
+    private func enact(suggested: Suggestion) {
+        guard let pump = pumpManager else { return }
+
+        enactCancellable = pump.enactTempBasal(
+            unitsPerHour: Double(truncating: suggested.rate as NSNumber),
+            for: TimeInterval(suggested.duration * 60)
+        )
+        .flatMap { dose -> AnyPublisher<DoseEntry, Error> in
+            let units = suggested.units.map { Double(truncating: $0 as NSNumber) } ?? 0
+            guard units > 0 else { return Just(dose).setFailureType(to: Error.self).eraseToAnyPublisher() }
+            return pump.enactBolus(units: units, automatic: true)
+        }
+        .sink { completion in
+            if case let .failure(error) = completion {
+                print("Loop failed with error: \(error.localizedDescription)")
+            }
+        } receiveValue: { _ in
+            print("Loop failed succeses")
+        }
+    }
+}
+
+private extension PumpManager {
+    func enactTempBasal(unitsPerHour: Double, for duration: TimeInterval) -> AnyPublisher<DoseEntry, Error> {
+        Future { promise in
+            self.enactTempBasal(unitsPerHour: unitsPerHour, for: duration) { result in
+                switch result {
+                case let .success(dose):
+                    promise(.success(dose))
+                case let .failure(error):
+                    promise(.failure(error))
+                }
+            }
+        }.eraseToAnyPublisher()
+    }
+
+    func enactBolus(units: Double, automatic: Bool) -> AnyPublisher<DoseEntry, Error> {
+        Future { promise in
+            self.enactBolus(units: units, automatic: automatic) { result in
+                switch result {
+                case let .success(dose):
+                    promise(.success(dose))
+                case let .failure(error):
+                    promise(.failure(error))
+                }
+            }
+        }.eraseToAnyPublisher()
+    }
 }

+ 1 - 13
FreeAPS/Sources/APS/DeviceDataManager.swift

@@ -139,20 +139,8 @@ extension BaseDeviceDataManager: PumpManagerDelegate {
         )))
     }
 
-    func pumpManagerRecommendsLoop(_ pumpManager: PumpManager) {
+    func pumpManagerRecommendsLoop(_: PumpManager) {
         print("[DeviceDataManager] Recomends loop")
-
-        pumpManager.enactTempBasal(unitsPerHour: 0.75, for: 60.minutes.timeInterval) { result in
-            switch result {
-            case let .success(dose):
-                print("[DeviceDataManager] Temp basal OK: \(dose)")
-            case let .failure(error):
-                print("[DeviceDataManager] Temp basal FAIL: \(error.localizedDescription)")
-            }
-        }
-//        pumpManager.enactBolus(units: 0.1, automatic: true) { _ in
-//            print("[DeviceDataManager] Bolus done")
-//        }
     }
 
     func startDateToFilterNewPumpEvents(for _: PumpManager) -> Date {

+ 60 - 55
FreeAPS/Sources/APS/OpenAPS/OpenAPS.swift

@@ -1,3 +1,4 @@
+import Combine
 import Foundation
 import JavaScriptCore
 
@@ -11,61 +12,65 @@ final class OpenAPS {
         self.storage = storage
     }
 
-    func determineBasal(currentTemp: TempBasal, clock: Date = Date()) {
-        processQueue.async {
-            // clock
-            try? self.storage.save(clock, as: Monitor.clock)
-
-            // temp_basal
-            let tempBasal = currentTemp.rawJSON
-            try? self.storage.save(tempBasal, as: Monitor.tempBasal)
-
-            // meal
-            let pumpHistory = self.loadFileFromStorage(name: OpenAPS.Monitor.pumpHistory)
-            let carbs = self.loadFileFromStorage(name: Monitor.carbHistory)
-            let glucose = self.loadFileFromStorage(name: Monitor.glucose)
-            let profile = self.loadFileFromStorage(name: Settings.profile)
-            let basalProfile = self.loadFileFromStorage(name: Settings.basalProfile)
-
-            let meal = self.meal(
-                pumphistory: pumpHistory,
-                profile: profile,
-                basalProfile: basalProfile,
-                clock: clock,
-                carbs: carbs,
-                glucose: glucose
-            )
-
-            try? self.storage.save(meal, as: Monitor.meal)
-
-            // iob
-            let autosens = self.loadFileFromStorage(name: Settings.autosense)
-            let iob = self.iob(
-                pumphistory: pumpHistory,
-                profile: profile,
-                clock: clock,
-                autosens: autosens.isEmpty ? .null : autosens
-            )
-
-            try? self.storage.save(iob, as: Monitor.iob)
-
-            // determine-basal
-            let reservoir = self.loadFileFromStorage(name: Monitor.reservoir)
-
-            let suggested = self.determineBasal(
-                glucose: glucose,
-                currentTemp: tempBasal,
-                iob: iob,
-                profile: profile,
-                autosens: autosens.isEmpty ? .null : autosens,
-                meal: meal,
-                microBolusAllowed: true,
-                reservoir: reservoir
-            )
-            print("SUGGESTED: \(suggested)")
-
-            try? self.storage.save(suggested, as: Enact.suggested)
-        }
+    func determineBasal(currentTemp: TempBasal, clock: Date = Date()) -> AnyPublisher<Void, Never> {
+        Future { promise in
+            self.processQueue.async {
+                // clock
+                try? self.storage.save(clock, as: Monitor.clock)
+
+                // temp_basal
+                let tempBasal = currentTemp.rawJSON
+                try? self.storage.save(tempBasal, as: Monitor.tempBasal)
+
+                // meal
+                let pumpHistory = self.loadFileFromStorage(name: OpenAPS.Monitor.pumpHistory)
+                let carbs = self.loadFileFromStorage(name: Monitor.carbHistory)
+                let glucose = self.loadFileFromStorage(name: Monitor.glucose)
+                let profile = self.loadFileFromStorage(name: Settings.profile)
+                let basalProfile = self.loadFileFromStorage(name: Settings.basalProfile)
+
+                let meal = self.meal(
+                    pumphistory: pumpHistory,
+                    profile: profile,
+                    basalProfile: basalProfile,
+                    clock: clock,
+                    carbs: carbs,
+                    glucose: glucose
+                )
+
+                try? self.storage.save(meal, as: Monitor.meal)
+
+                // iob
+                let autosens = self.loadFileFromStorage(name: Settings.autosense)
+                let iob = self.iob(
+                    pumphistory: pumpHistory,
+                    profile: profile,
+                    clock: clock,
+                    autosens: autosens.isEmpty ? .null : autosens
+                )
+
+                try? self.storage.save(iob, as: Monitor.iob)
+
+                // determine-basal
+                let reservoir = self.loadFileFromStorage(name: Monitor.reservoir)
+
+                let suggested = self.determineBasal(
+                    glucose: glucose,
+                    currentTemp: tempBasal,
+                    iob: iob,
+                    profile: profile,
+                    autosens: autosens.isEmpty ? .null : autosens,
+                    meal: meal,
+                    microBolusAllowed: true,
+                    reservoir: reservoir
+                )
+                print("SUGGESTED: \(suggested)")
+
+                try? self.storage.save(suggested, as: Enact.suggested)
+
+                promise(.success(()))
+            }
+        }.eraseToAnyPublisher()
     }
 
     func autosense() {

+ 11 - 0
FreeAPS/Sources/Models/Suggestion.swift

@@ -0,0 +1,11 @@
+import Foundation
+
+struct Suggestion: JSON {
+    let reason: String
+    let units: Decimal?
+    let insulinReq: Decimal
+    let eventualBG: Int
+    let sensitivityRatio: Decimal
+    let rate: Decimal
+    let duration: Int
+}