Jelajahi Sumber

temp targets drawing

Ivan Valkou 5 tahun lalu
induk
melakukan
c10ed57ef2

FreeAPS.xcodeproj/xcshareddata/xcschemes/FreeAPS.xcscheme → FreeAPS.xcodeproj/xcshareddata/xcschemes/FreeAPS X.xcscheme


+ 2 - 2
FreeAPS.xcodeproj/xcuserdata/i.valkou.xcuserdatad/xcschemes/xcschememanagement.plist

@@ -25,10 +25,10 @@
 			<key>orderHint</key>
 			<integer>18</integer>
 		</dict>
-		<key>FreeAPS.xcscheme_^#shared#^_</key>
+		<key>FreeAPS X.xcscheme_^#shared#^_</key>
 		<dict>
 			<key>orderHint</key>
-			<integer>1</integer>
+			<integer>0</integer>
 		</dict>
 		<key>ReactiveSwift (Playground) 1.xcscheme</key>
 		<dict>

+ 1 - 1
FreeAPS/Resources/Info.plist

@@ -11,7 +11,7 @@
 	<key>CFBundleInfoDictionaryVersion</key>
 	<string>6.0</string>
 	<key>CFBundleName</key>
-	<string>$(PRODUCT_NAME)</string>
+	<string>FreeAPS X</string>
 	<key>CFBundlePackageType</key>
 	<string>$(PRODUCT_BUNDLE_PACKAGE_TYPE)</string>
 	<key>CFBundleShortVersionString</key>

+ 1 - 0
FreeAPS/Sources/APS/Storage/TempTargetsStorage.swift

@@ -88,6 +88,7 @@ final class BaseTempTargetsStorage: TempTargetsStorage, Injectable {
 
     func storePresets(_ targets: [TempTarget]) {
         try? storage.remove(OpenAPS.FreeAPS.tempTargetsPresets)
+
         storeTempTargets(targets, isPresets: true)
     }
 

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

@@ -1,6 +1,6 @@
 import Foundation
 
-struct TempTarget: JSON, Identifiable {
+struct TempTarget: JSON, Identifiable, Equatable {
     var id = UUID().uuidString
     let name: String
     var createdAt: Date

+ 1 - 1
FreeAPS/Sources/Modules/AutotuneConfig/View/AutotuneConfigRootView.swift

@@ -55,7 +55,7 @@ extension AutotuneConfig {
                                 Text(autotune.basalProfile[index].start).foregroundColor(.secondary)
                                 Spacer()
                                 Text(rateFormatter.string(from: autotune.basalProfile[index].rate as NSNumber) ?? "0")
-                                Text("U/h").foregroundColor(.secondary)
+                                Text("U/hr").foregroundColor(.secondary)
                             }
                         }
                     }

+ 2 - 2
FreeAPS/Sources/Modules/BasalProfileEditor/View/BasalProfileEditorRootView.swift

@@ -62,7 +62,7 @@ extension BasalProfileEditor {
                                     (
                                         self.rateFormatter
                                             .string(from: viewModel.rateValues[i] as NSNumber) ?? ""
-                                    ) + " U/h"
+                                    ) + " U/hr"
                                 ).tag(i)
                             }
                         }
@@ -94,7 +94,7 @@ extension BasalProfileEditor {
                         HStack {
                             Text("Rate").foregroundColor(.secondary)
                             Text(
-                                "\(rateFormatter.string(from: viewModel.rateValues[item.rateIndex] as NSNumber) ?? "0") U/h"
+                                "\(rateFormatter.string(from: viewModel.rateValues[item.rateIndex] as NSNumber) ?? "0") U/hr"
                             )
                             Spacer()
                             Text("starts at").foregroundColor(.secondary)

+ 1 - 0
FreeAPS/Sources/Modules/Home/HomeDataFlow.swift

@@ -11,4 +11,5 @@ protocol HomeProvider: Provider {
     func pumpHistory(hours: Int) -> [PumpHistoryEvent]
     func pumpSettings() -> PumpSettings
     func basalProfile() -> [BasalProfileEntry]
+    func tempTargets(hours: Int) -> [TempTarget]
 }

+ 7 - 0
FreeAPS/Sources/Modules/Home/HomeProvider.swift

@@ -6,6 +6,7 @@ extension Home {
         @Injected() var apsManager: APSManager!
         @Injected() var glucoseStorage: GlucoseStorage!
         @Injected() var pumpHistoryStorage: PumpHistoryStorage!
+        @Injected() var tempTargetsStorage: TempTargetsStorage!
 
         var suggestion: Suggestion? {
             try? storage.retrieve(OpenAPS.Enact.suggested, as: Suggestion.self)
@@ -27,6 +28,12 @@ extension Home {
             }
         }
 
+        func tempTargets(hours: Int) -> [TempTarget] {
+            tempTargetsStorage.recent().filter {
+                $0.createdAt.addingTimeInterval(hours.hours.timeInterval) > Date()
+            }
+        }
+
         func pumpSettings() -> PumpSettings {
             (try? storage.retrieve(OpenAPS.Settings.settings, as: PumpSettings.self))
                 ?? PumpSettings(from: OpenAPS.defaults(for: OpenAPS.Settings.settings))

+ 22 - 2
FreeAPS/Sources/Modules/Home/HomeViewModel.swift

@@ -15,6 +15,7 @@ extension Home {
         @Published var tempBasals: [PumpHistoryEvent] = []
         @Published var maxBasal: Decimal = 2
         @Published var basalProfile: [BasalProfileEntry] = []
+        @Published var tempTargets: [TempTarget] = []
 
         @Published var allowManualTemp = false
         private(set) var units: GlucoseUnits = .mmolL
@@ -24,15 +25,18 @@ extension Home {
             setupBasals()
             setupPumpSettings()
             setupBasalProfile()
+            setupTempTargets()
             suggestion = provider.suggestion
             units = settingsManager.settings.units
             allowManualTemp = !settingsManager.settings.closedLoop
+
             broadcaster.register(GlucoseObserver.self, observer: self)
             broadcaster.register(SuggestionObserver.self, observer: self)
             broadcaster.register(SettingsObserver.self, observer: self)
             broadcaster.register(PumpHistoryObserver.self, observer: self)
             broadcaster.register(PumpSettingsObserver.self, observer: self)
             broadcaster.register(BasalProfileObserver.self, observer: self)
+            broadcaster.register(TempTargetsObserver.self, observer: self)
         }
 
         func addCarbs() {
@@ -94,11 +98,23 @@ extension Home {
                 self.basalProfile = self.provider.basalProfile()
             }
         }
+
+        private func setupTempTargets() {
+            DispatchQueue.main.async {
+                self.tempTargets = self.provider.tempTargets(hours: self.filteredHours)
+            }
+        }
     }
 }
 
-extension Home.ViewModel: GlucoseObserver, SuggestionObserver, SettingsObserver, PumpHistoryObserver, PumpSettingsObserver,
-    BasalProfileObserver
+extension Home.ViewModel:
+    GlucoseObserver,
+    SuggestionObserver,
+    SettingsObserver,
+    PumpHistoryObserver,
+    PumpSettingsObserver,
+    BasalProfileObserver,
+    TempTargetsObserver
 {
     func glucoseDidUpdate(_: [BloodGlucose]) {
         setupGlucose()
@@ -123,4 +139,8 @@ extension Home.ViewModel: GlucoseObserver, SuggestionObserver, SettingsObserver,
     func basalProfileDidChange(_: [BasalProfileEntry]) {
         setupBasalProfile()
     }
+
+    func tempTargetsDidUpdate(_: [TempTarget]) {
+        setupTempTargets()
+    }
 }

+ 65 - 9
FreeAPS/Sources/Modules/Home/View/Chart/MainChartView.swift

@@ -17,6 +17,7 @@ struct MainChartView: View {
         static let bottomYPadding: CGFloat = 50
         static let minAdditionalWidth: CGFloat = 150
         static let maxGlucose = 450
+        static let minGlucose = 70
         static let yLinesCount = 5
     }
 
@@ -26,6 +27,7 @@ struct MainChartView: View {
     @Binding var hours: Int
     @Binding var maxBasal: Decimal
     @Binding var basalProfile: [BasalProfileEntry]
+    @Binding var tempTargets: [TempTarget]
     let units: GlucoseUnits
 
     @State var didAppearTrigger = false
@@ -33,6 +35,7 @@ struct MainChartView: View {
     @State private var predictionDots: [PredictionType: [CGRect]] = [:]
     @State private var tempBasalPath = Path()
     @State private var regularBasalPath = Path()
+    @State private var tempTargetsPath = Path()
 
     private var dateDormatter: DateFormatter {
         let formatter = DateFormatter()
@@ -72,6 +75,7 @@ struct MainChartView: View {
                 ScrollView(.horizontal, showsIndicators: false) {
                     ScrollViewReader { scroll in
                         ZStack(alignment: .top) {
+                            tempTargetsView(fullSize: geo.size)
                             basalChart(fullSize: geo.size)
                             mainChart(fullSize: geo.size).id("End")
                                 .onChange(of: glucose) { _ in
@@ -190,6 +194,22 @@ struct MainChartView: View {
         }
     }
 
+    private func tempTargetsView(fullSize: CGSize) -> some View {
+        ZStack {
+            tempTargetsPath
+                .fill(Color.gray.opacity(0.5))
+        }
+        .onChange(of: glucose) { _ in
+            calculateTempTargetsRects(fullSize: fullSize)
+        }
+        .onChange(of: tempTargets) { _ in
+            calculateTempTargetsRects(fullSize: fullSize)
+        }
+        .onChange(of: didAppearTrigger) { _ in
+            calculateTempTargetsRects(fullSize: fullSize)
+        }
+    }
+
     private func predictions(fullSize: CGSize) -> some View {
         Group {
             Path { path in
@@ -383,7 +403,7 @@ struct MainChartView: View {
             return ""
         }
         let lastRate = lastBasal[0].rate ?? 0
-        return (basalFormatter.string(from: lastRate as NSNumber) ?? "0") + " U/h"
+        return (basalFormatter.string(from: lastRate as NSNumber) ?? "0") + " U/hr"
     }
 
     private func fullGlucoseWidth(viewWidth: CGFloat) -> CGFloat {
@@ -427,14 +447,17 @@ struct MainChartView: View {
     }
 
     private func minPredValue() -> Int {
-        [
-            suggestion?.predictions?.cob ?? [],
-            suggestion?.predictions?.iob ?? [],
-            suggestion?.predictions?.zt ?? [],
-            suggestion?.predictions?.uam ?? []
-        ]
-        .flatMap { $0 }
-        .min() ?? 0
+        let min =
+            [
+                suggestion?.predictions?.cob ?? [],
+                suggestion?.predictions?.iob ?? [],
+                suggestion?.predictions?.zt ?? [],
+                suggestion?.predictions?.uam ?? []
+            ]
+            .flatMap { $0 }
+            .min() ?? Config.minGlucose
+
+        return Swift.min(min, Config.minGlucose)
     }
 
     private func glucoseToCoordinate(_ glucoseEntry: BloodGlucose, fullSize: CGSize) -> CGPoint {
@@ -502,4 +525,37 @@ struct MainChartView: View {
         let oneSecondWidth = oneSecondStep(viewWidth: viewWidth)
         return oneSecondWidth * CGFloat(lastDeltaTime)
     }
+
+    private func calculateTempTargetsRects(fullSize: CGSize) {
+        var rects = tempTargets.map { tempTarget -> CGRect in
+            let x0 = timeToXCoordinate(tempTarget.createdAt.timeIntervalSince1970, fullSize: fullSize)
+            let y0 = glucoseToYCoordinate(Int(tempTarget.targetTop), fullSize: fullSize)
+            let x1 = timeToXCoordinate(
+                tempTarget.createdAt.timeIntervalSince1970 + Int(tempTarget.duration).minutes.timeInterval,
+                fullSize: fullSize
+            )
+            let y1 = glucoseToYCoordinate(Int(tempTarget.targetBottom), fullSize: fullSize)
+            return CGRect(
+                x: x0,
+                y: y0 - 3,
+                width: x1 - x0,
+                height: y1 - y0 + 6
+            )
+        }
+        if rects.count > 1 {
+            rects = rects.reduce([]) { result, rect -> [CGRect] in
+                guard var last = result.last else { return [rect] }
+                if last.origin.x + last.width > rect.origin.x {
+                    last.size.width = rect.origin.x - last.origin.x
+                }
+                var res = Array(result.dropLast())
+                res.append(contentsOf: [last, rect])
+                return res
+            }
+        }
+
+        tempTargetsPath = Path { path in
+            path.addRects(rects)
+        }
+    }
 }

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

@@ -54,6 +54,7 @@ extension Home {
                         hours: .constant(viewModel.filteredHours),
                         maxBasal: $viewModel.maxBasal,
                         basalProfile: $viewModel.basalProfile,
+                        tempTargets: $viewModel.tempTargets,
                         units: viewModel.units
                     )
 

+ 1 - 1
FreeAPS/Sources/Modules/ManualTempBasal/View/ManualTempBasalRootView.swift

@@ -18,7 +18,7 @@ extension ManualTempBasal {
                         Text("Amount")
                         Spacer()
                         DecimalTextField("0", value: $viewModel.rate, formatter: formatter, autofocus: true, cleanInput: true)
-                        Text("U/hour").foregroundColor(.secondary)
+                        Text("U/hr").foregroundColor(.secondary)
                     }
                     Picker(selection: $viewModel.durationIndex, label: Text("Duration")) {
                         ForEach(0 ..< viewModel.durationValues.count) { index in

+ 1 - 1
FreeAPS/Sources/Modules/PumpSettingsEditor/View/PumpSettingsEditorRootView.swift

@@ -19,7 +19,7 @@ extension PumpSettingsEditor {
                     }
                     HStack {
                         Text("Max Bolus")
-                        DecimalTextField("U/hour", value: $viewModel.maxBolus, formatter: formatter)
+                        DecimalTextField("U/hr", value: $viewModel.maxBolus, formatter: formatter)
                     }
                 }