Browse Source

Add ability to read basal rates from pump (e.g. when switchting between loop / freeaps

Jan Dittmer 4 years ago
parent
commit
380864b832

+ 3 - 0
Dependencies/LoopKit/LoopKit/DeviceManager/PumpManager.swift

@@ -183,6 +183,9 @@ public protocol PumpManager: DeviceManager {
     ///   - completion: A closure called after the command is complete
     ///   - completion: A closure called after the command is complete
     ///   - result: A BasalRateSchedule or an error describing why the command failed
     ///   - result: A BasalRateSchedule or an error describing why the command failed
     func syncBasalRateSchedule(items scheduleItems: [RepeatingScheduleValue<Double>], completion: @escaping (_ result: Result<BasalRateSchedule, Error>) -> Void)
     func syncBasalRateSchedule(items scheduleItems: [RepeatingScheduleValue<Double>], completion: @escaping (_ result: Result<BasalRateSchedule, Error>) -> Void)
+
+    func getBasalRateSchedule(completion: @escaping (Result<BasalRateSchedule, Error>) -> Void)
+
 }
 }
 
 
 
 

+ 12 - 0
Dependencies/LoopKit/MockKit/MockPumpManager.swift

@@ -590,6 +590,18 @@ public final class MockPumpManager: TestingPumpManager {
             completion(.success(BasalRateSchedule(dailyItems: scheduleItems, timeZone: self.status.timeZone)!))
             completion(.success(BasalRateSchedule(dailyItems: scheduleItems, timeZone: self.status.timeZone)!))
         }
         }
     }
     }
+
+    public func getBasalRateSchedule(completion: @escaping (Result<BasalRateSchedule, Error>) -> Void) {
+        if let schedule = state.basalRateSchedule {
+            NSLog("Mock read success")
+
+            completion(.success(BasalRateSchedule(dailyItems: schedule.items, timeZone: self.status.timeZone)!))
+        } else {
+            NSLog("Mock read failure")
+            completion(.failure(PumpManagerError.communication(MockPumpManagerError.communicationFailure)))
+        }
+    }
+
 }
 }
 
 
 // MARK: - AlertResponder implementation
 // MARK: - AlertResponder implementation

+ 26 - 0
Dependencies/rileylink_ios/MinimedKit/PumpManager/MinimedPumpManager.swift

@@ -1244,6 +1244,32 @@ extension MinimedPumpManager: PumpManager {
             }
             }
         }
         }
     }
     }
+
+    public func getBasalRateSchedule(completion: @escaping (Result<BasalRateSchedule, Error>) -> Void) {
+        pumpOps.runSession(withName: "Save Basal Profile", using: rileyLinkDeviceProvider.firstConnectedDevice) { (session) in
+            guard let session = session else {
+                completion(.failure(PumpManagerError.connection(MinimedPumpManagerError.noRileyLink)))
+                return
+            }
+
+            do {
+                if let schedule = try session.getBasalSchedule() {
+                    let dailyItems = schedule.entries.map {
+                        RepeatingScheduleValue<Double>(
+                            startTime: $0.timeOffset,
+                            value: $0.rate)
+                    }
+                    completion(.success(BasalRateSchedule(dailyItems: dailyItems, timeZone: session.pump.timeZone)!))
+                } else {
+                    self.log.error("Get basal profile return no result")
+                    completion(.failure(PumpManagerError.communication("Get Basal failed"  as? LocalizedError)))
+                }
+            } catch let error {
+                self.log.error("Get basal profile failed: %{public}@", String(describing: error))
+                completion(.failure(error))
+            }
+        }
+    }
 }
 }
 
 
 extension MinimedPumpManager: PumpOpsDelegate {
 extension MinimedPumpManager: PumpOpsDelegate {

+ 11 - 0
Dependencies/rileylink_ios/MinimedKitUI/MinimedPumpManager+UI.swift

@@ -109,6 +109,17 @@ extension MinimedPumpManager {
         }
         }
     }
     }
 
 
+    public func getScheduleValues(for viewController: BasalScheduleTableViewController, completion: @escaping (SyncBasalScheduleResult<Double>) -> Void) {
+        getBasalRateSchedule() { result in
+            switch result {
+            case .success(let schedule):
+                completion(.success(scheduleItems: schedule.items, timeZone: schedule.timeZone))
+            case .failure(let error):
+                completion(.failure(error))
+            }
+        }
+    }
+
     public func syncButtonTitle(for viewController: BasalScheduleTableViewController) -> String {
     public func syncButtonTitle(for viewController: BasalScheduleTableViewController) -> String {
         return LocalizedString("Save to Pump…", comment: "Title of button to save basal profile to pump")
         return LocalizedString("Save to Pump…", comment: "Title of button to save basal profile to pump")
     }
     }

+ 5 - 0
Dependencies/rileylink_ios/OmniKit/PumpManager/OmnipodPumpManager.swift

@@ -1721,6 +1721,11 @@ extension OmnipodPumpManager: PumpManager {
         }
         }
     }
     }
 
 
+    public func getBasalRateSchedule(completion: @escaping (Result<BasalRateSchedule, Error>) -> Void) {
+        completion(.failure(PumpManagerError.deviceState(PodCommsError.podSuspended)))
+
+    }
+
     // This cannot be called from within the lockedState lock!
     // This cannot be called from within the lockedState lock!
     func store(doses: [UnfinalizedDose], in session: PodCommsSession) -> Bool {
     func store(doses: [UnfinalizedDose], in session: PodCommsSession) -> Bool {
         session.assertOnSessionQueue()
         session.assertOnSessionQueue()

+ 1 - 0
FreeAPS/Sources/Modules/BasalProfileEditor/BasalProfileEditorDataFlow.swift

@@ -28,4 +28,5 @@ protocol BasalProfileEditorProvider: Provider {
     var profile: [BasalProfileEntry] { get }
     var profile: [BasalProfileEntry] { get }
     var supportedBasalRates: [Decimal]? { get }
     var supportedBasalRates: [Decimal]? { get }
     func saveProfile(_ profile: [BasalProfileEntry]) -> AnyPublisher<Void, Error>
     func saveProfile(_ profile: [BasalProfileEntry]) -> AnyPublisher<Void, Error>
+    func readProfile() -> AnyPublisher<Void, Error>
 }
 }

+ 41 - 0
FreeAPS/Sources/Modules/BasalProfileEditor/BasalProfileEditorProvider.swift

@@ -38,5 +38,46 @@ extension BasalProfileEditor {
                 }
                 }
             }.eraseToAnyPublisher()
             }.eraseToAnyPublisher()
         }
         }
+
+        func readProfile() -> AnyPublisher<Void, Error> {
+            guard let pump = deviceManager?.pumpManager else {
+                // storage.save(profile, as: OpenAPS.Settings.basalProfile)
+                return Just(()).setFailureType(to: Error.self).eraseToAnyPublisher()
+            }
+
+            // let syncValues = profile.map {
+            //    RepeatingScheduleValue(startTime: TimeInterval($0.minutes * 60), value: Double($0.rate))
+            // }
+
+            return Future { promise in
+                pump.getBasalRateSchedule { result in
+                    switch result {
+                    case let .success(scheduleItems):
+                        var newProfile: [BasalProfileEntry] = []
+                        for item in scheduleItems.items {
+                            NSLog("getBasalRateSchedule \(item.startTime) \(item.value)")
+                            let startMinutes = Int(item.startTime / 60) // seconds to minutes
+                            let start = String(format: "%2d:%2d", startMinutes / 60, startMinutes % 60)
+                            let rate = Decimal(item.value)
+                            newProfile.append(BasalProfileEntry(
+                                start: start,
+                                minutes: startMinutes,
+                                rate: rate
+                            ))
+                        }
+
+                        for p in newProfile {
+                            NSLog("getBasalRateSchedule \(p.start) \(p.minutes) \(p.rate)")
+                        }
+
+                        self.storage.save(newProfile, as: OpenAPS.Settings.basalProfile)
+                        // self.profile = newProfile
+                        promise(.success(()))
+                    case let .failure(error):
+                        promise(.failure(error))
+                    }
+                }
+            }.eraseToAnyPublisher()
+        }
     }
     }
 }
 }

+ 11 - 0
FreeAPS/Sources/Modules/BasalProfileEditor/BasalProfileEditorStateModel.swift

@@ -56,6 +56,17 @@ extension BasalProfileEditor {
                 .store(in: &lifetime)
                 .store(in: &lifetime)
         }
         }
 
 
+        func read() {
+            syncInProgress = true
+            provider.readProfile()
+                .receive(on: DispatchQueue.main)
+                .sink { _ in
+                    self.syncInProgress = false
+                    self.subscribe()
+                } receiveValue: {}
+                .store(in: &lifetime)
+        }
+
         func validate() {
         func validate() {
             DispatchQueue.main.async {
             DispatchQueue.main.async {
                 let uniq = Array(Set(self.items))
                 let uniq = Array(Set(self.items))

+ 11 - 0
FreeAPS/Sources/Modules/BasalProfileEditor/View/BasalProfileEditorRootView.swift

@@ -37,6 +37,17 @@ extension BasalProfileEditor {
                         }
                         }
                         .disabled(state.syncInProgress || state.items.isEmpty)
                         .disabled(state.syncInProgress || state.items.isEmpty)
                     }
                     }
+
+                    HStack {
+                        if state.syncInProgress {
+                            ProgressView().padding(.trailing, 10)
+                        }
+                        Button { state.read() }
+                        label: {
+                            Text(state.syncInProgress ? "Readimg..." : "Read from Pump")
+                        }
+                        .disabled(state.syncInProgress || state.items.isEmpty)
+                    }
                 }
                 }
             }
             }
             .onAppear(perform: configureView)
             .onAppear(perform: configureView)