Bläddra i källkod

Pump suspensions on basal chart

Ivan Valkou 5 år sedan
förälder
incheckning
7e2a48f513

+ 2 - 2
FreeAPS.xcworkspace/xcshareddata/swiftpm/Package.resolved

@@ -24,8 +24,8 @@
         "repositoryURL": "https://github.com/apple/swift-algorithms",
         "state": {
           "branch": null,
-          "revision": "1e761dd787b0f148f0b7aec42a7ff401767c26fa",
-          "version": "0.0.3"
+          "revision": "04d91803a2dd58c14db9cf66c7412c6dd4a26fc8",
+          "version": "0.1.1"
         }
       },
       {

+ 2 - 2
FreeAPS/Sources/APS/APSManager.swift

@@ -292,7 +292,7 @@ final class BaseAPSManager: APSManager, Injectable {
 
         pump.enactBolus(units: roundedAmout, automatic: isSMB).sink { completion in
             if case let .failure(error) = completion {
-                debug(.apsManager, "Bolus failed with error: \(error.localizedDescription)")
+                warning(.apsManager, "Bolus failed with error: \(error.localizedDescription)")
                 self.processError(APSError.pumpError(error))
             } else {
                 debug(.apsManager, "Bolus succeeded")
@@ -524,7 +524,7 @@ final class BaseAPSManager: APSManager, Injectable {
     }
 
     private func processError(_ error: Error) {
-        debug(.apsManager, "\(error.localizedDescription)")
+        warning(.apsManager, "\(error.localizedDescription)")
         lastError.send(error)
     }
 

+ 0 - 1
FreeAPS/Sources/Logger/Logger.swift

@@ -246,7 +246,6 @@ final class Logger {
         reporter.log(category.name, message, file: file, function: function, line: line)
         if !LoggerTestMode, maybeError?.shouldReportNonFatalIssue ?? true {
             reporter.reportNonFatalIssue(withError: loggerError.asNSError())
-            showAlert(message)
         }
     }
 

+ 1 - 1
FreeAPS/Sources/Models/BloodGlucose.swift

@@ -25,7 +25,7 @@ struct BloodGlucose: JSON, Identifiable, Hashable {
     let direction: Direction?
     let date: Decimal
     let dateString: Date
-    let filtered: Double?
+    let filtered: Decimal?
     let noise: Int?
 
     var glucose: Int?

+ 13 - 0
FreeAPS/Sources/Modules/Home/HomeViewModel.swift

@@ -18,12 +18,14 @@ extension Home {
         @Published var glucoseDelta: Int?
         @Published var tempBasals: [PumpHistoryEvent] = []
         @Published var boluses: [PumpHistoryEvent] = []
+        @Published var suspensions: [PumpHistoryEvent] = []
         @Published var maxBasal: Decimal = 2
         @Published var basalProfile: [BasalProfileEntry] = []
         @Published var tempTargets: [TempTarget] = []
         @Published var carbs: [CarbsEntry] = []
         @Published var timerDate = Date()
         @Published var closedLoop = false
+        @Published var pumpSuspended = false
         @Published var isLooping = false
         @Published var statusTitle = ""
         @Published var lastLoopDate: Date = .distantPast
@@ -45,6 +47,7 @@ extension Home {
             setupGlucose()
             setupBasals()
             setupBoluses()
+            setupSuspensions()
             setupPumpSettings()
             setupBasalProfile()
             setupTempTargets()
@@ -173,6 +176,15 @@ extension Home {
             }
         }
 
+        private func setupSuspensions() {
+            DispatchQueue.main.async {
+                self.suspensions = self.provider.pumpHistory(hours: self.filteredHours).filter {
+                    $0.type == .pumpSuspend || $0.type == .pumpResume
+                }
+                self.pumpSuspended = self.suspensions.last?.type == .pumpSuspend
+            }
+        }
+
         private func setupPumpSettings() {
             DispatchQueue.main.async {
                 self.maxBasal = self.provider.pumpSettings().maxBasal
@@ -279,6 +291,7 @@ extension Home.ViewModel:
     func pumpHistoryDidUpdate(_: [PumpHistoryEvent]) {
         setupBasals()
         setupBoluses()
+        setupSuspensions()
     }
 
     func pumpSettingsDidChange(_: PumpSettings) {

+ 42 - 0
FreeAPS/Sources/Modules/Home/View/Chart/MainChartView.swift

@@ -37,6 +37,7 @@ struct MainChartView: View {
     @Binding var suggestion: Suggestion?
     @Binding var tempBasals: [PumpHistoryEvent]
     @Binding var boluses: [PumpHistoryEvent]
+    @Binding var suspensions: [PumpHistoryEvent]
     @Binding var hours: Int
     @Binding var maxBasal: Decimal
     @Binding var basalProfile: [BasalProfileEntry]
@@ -53,6 +54,7 @@ struct MainChartView: View {
     @State private var tempBasalPath = Path()
     @State private var regularBasalPath = Path()
     @State private var tempTargetsPath = Path()
+    @State private var suspensionsPath = Path()
     @State private var carbsDots: [DotInfo] = []
     @State private var carbsPath = Path()
     @State private var glucoseYGange: GlucoseYRange = (0, 0, 0, 0)
@@ -162,6 +164,7 @@ struct MainChartView: View {
         ZStack {
             tempBasalPath.fill(Color.tempBasal)
             tempBasalPath.stroke(Color.tempBasal, lineWidth: 1)
+            suspensionsPath.fill(Color.loopGray)
             regularBasalPath.stroke(Color.basal, lineWidth: 1)
         }
         .frame(width: fullGlucoseWidth(viewWidth: fullSize.width) + additionalWidth(viewWidth: fullSize.width))
@@ -170,6 +173,9 @@ struct MainChartView: View {
         .onChange(of: tempBasals) { _ in
             calculateBasalPoints(fullSize: fullSize)
         }
+        .onChange(of: suspensions) { _ in
+            calculateSuspensions(fullSize: fullSize)
+        }
         .onChange(of: maxBasal) { _ in
             calculateBasalPoints(fullSize: fullSize)
         }
@@ -360,6 +366,7 @@ extension MainChartView {
         calculateTempTargetsRects(fullSize: fullSize)
         calculateTempTargetsRects(fullSize: fullSize)
         calculateBasalPoints(fullSize: fullSize)
+        calculateSuspensions(fullSize: fullSize)
     }
 
     private func calculateGlucoseDots(fullSize: CGSize) {
@@ -513,6 +520,41 @@ extension MainChartView {
         }
     }
 
+    private func calculateSuspensions(fullSize: CGSize) {
+        calculationQueue.async {
+            var rects = suspensions.windows(ofCount: 2).map { window -> CGRect? in
+                let window = Array(window)
+                guard window[0].type == .pumpSuspend, window[1].type == .pumpResume else { return nil }
+                let x0 = self.timeToXCoordinate(window[0].timestamp.timeIntervalSince1970, fullSize: fullSize)
+                let x1 = self.timeToXCoordinate(window[1].timestamp.timeIntervalSince1970, fullSize: fullSize)
+                return CGRect(x: x0, y: 0, width: x1 - x0, height: Config.basalHeight)
+            }
+
+            let firstRec = self.suspensions.first.flatMap { event -> CGRect? in
+                guard event.type == .pumpResume else { return nil }
+                let width = self.timeToXCoordinate(event.timestamp.timeIntervalSince1970, fullSize: fullSize)
+                return CGRect(x: 0, y: 0, width: width, height: Config.basalHeight)
+            }
+
+            let lastRec = self.suspensions.last.flatMap { event -> CGRect? in
+                guard event.type == .pumpSuspend else { return nil }
+                let x0 = self.timeToXCoordinate(event.timestamp.timeIntervalSince1970, fullSize: fullSize)
+                let x1 = self.fullGlucoseWidth(viewWidth: fullSize.width) + self.additionalWidth(viewWidth: fullSize.width)
+                return CGRect(x: x0, y: 0, width: x1 - x0, height: Config.basalHeight)
+            }
+            rects.append(firstRec)
+            rects.append(lastRec)
+
+            let path = Path { path in
+                path.addRects(rects.compactMap { $0 })
+            }
+
+            DispatchQueue.main.async {
+                suspensionsPath = path
+            }
+        }
+    }
+
     private func maxBasalRate() -> Decimal {
         if let cached = cachedMaxBasalRate {
             return cached

+ 6 - 1
FreeAPS/Sources/Modules/Home/View/HomeRootView.swift

@@ -86,7 +86,11 @@ extension Home {
 
         var infoPanal: some View {
             HStack(alignment: .center) {
-                if let tempRate = viewModel.tempRate {
+                if viewModel.pumpSuspended {
+                    Text("Pump suspended")
+                        .font(.system(size: 12, weight: .bold)).foregroundColor(.loopGray)
+                        .padding(.leading, 8)
+                } else if let tempRate = viewModel.tempRate {
                     Text((numberFormatter.string(from: tempRate as NSNumber) ?? "0") + " U/hr")
                         .font(.system(size: 12, weight: .bold)).foregroundColor(.insulin)
                         .padding(.leading, 8)
@@ -190,6 +194,7 @@ extension Home {
                         suggestion: $viewModel.suggestion,
                         tempBasals: $viewModel.tempBasals,
                         boluses: $viewModel.boluses,
+                        suspensions: $viewModel.suspensions,
                         hours: .constant(viewModel.filteredHours),
                         maxBasal: $viewModel.maxBasal,
                         basalProfile: $viewModel.basalProfile,

+ 12 - 0
FreeAPS/Sources/Services/Network/NightscoutAPI.swift

@@ -77,6 +77,10 @@ extension NightscoutAPI {
         return service.run(request)
             .retry(Config.retryCount)
             .decode(type: [BloodGlucose].self, decoder: JSONCoding.decoder)
+            .catch { error -> AnyPublisher<[BloodGlucose], Swift.Error> in
+                warning(.nightscout, "Glucose fetching error: \(error.localizedDescription)")
+                return Just([]).setFailureType(to: Swift.Error.self).eraseToAnyPublisher()
+            }
             .map { glucose in
                 glucose
                     .map {
@@ -124,6 +128,10 @@ extension NightscoutAPI {
         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()
+            }
             .eraseToAnyPublisher()
     }
 
@@ -193,6 +201,10 @@ extension NightscoutAPI {
         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()
+            }
             .eraseToAnyPublisher()
     }