Przeglądaj źródła

Fix mmol/L conversion issues, remove conversion factors WIP

Deniz Cengiz 1 rok temu
rodzic
commit
5a8f7f0b74

+ 20 - 3
FreeAPS/Sources/Modules/Bolus/View/ForeCastChart.swift

@@ -19,6 +19,20 @@ struct ForeCastChart: View {
             )) // min is 1.5h -> (1.5*1h = 1.5*(5*12*60))
     }
 
+    private var glucoseFormatter: NumberFormatter {
+        let formatter = NumberFormatter()
+        formatter.numberStyle = .decimal
+
+        if units == .mmolL {
+            formatter.maximumFractionDigits = 1
+            formatter.minimumFractionDigits = 1
+            formatter.roundingMode = .halfUp
+        } else {
+            formatter.maximumFractionDigits = 0
+        }
+        return formatter
+    }
+
     var body: some View {
         VStack {
             forecastChart
@@ -30,9 +44,12 @@ struct ForeCastChart: View {
 
                 if let eventualBG = state.simulatedDetermination?.eventualBG {
                     HStack {
-                        Text("\(eventualBG)")
-                            .font(.footnote)
-                            .foregroundStyle(.primary)
+                        Text(
+                            glucoseFormatter
+                                .string(from: (units == .mgdL ? Decimal(eventualBG) : eventualBG.asMmolL) as NSNumber) ?? "--"
+                        )
+                        .font(.footnote)
+                        .foregroundStyle(.primary)
                         Text("\(units.rawValue)")
                             .font(.footnote)
                             .foregroundStyle(.secondary)

+ 6 - 6
FreeAPS/Sources/Modules/Home/HomeStateModel.swift

@@ -47,8 +47,8 @@ extension Home {
         @Published var manualTempBasal = false
         @Published var smooth = false
         @Published var maxValue: Decimal = 1.2
-        @Published var lowGlucose: Decimal = 4 / 0.0555
-        @Published var highGlucose: Decimal = 10 / 0.0555
+        @Published var lowGlucose: Decimal = 72
+        @Published var highGlucose: Decimal = 180
         @Published var overrideUnit: Bool = false
         @Published var displayXgridLines: Bool = false
         @Published var displayYgridLines: Bool = false
@@ -126,8 +126,8 @@ extension Home {
             setupCurrentTempTarget()
             smooth = settingsManager.settings.smoothGlucose
             maxValue = settingsManager.preferences.autosensMax
-            lowGlucose = settingsManager.settings.low
-            highGlucose = settingsManager.settings.high
+            lowGlucose = units == .mgdL ? settingsManager.settings.low : settingsManager.settings.low.asMmolL
+            highGlucose = units == .mgdL ? settingsManager.settings.high : settingsManager.settings.high.asMmolL
             overrideUnit = settingsManager.settings.overrideHbA1cUnit
             displayXgridLines = settingsManager.settings.xGridLines
             displayYgridLines = settingsManager.settings.yGridLines
@@ -463,8 +463,8 @@ extension Home.StateModel:
         animatedBackground = settingsManager.settings.animatedBackground
         manualTempBasal = apsManager.isManualTempBasal
         smooth = settingsManager.settings.smoothGlucose
-        lowGlucose = settingsManager.settings.low
-        highGlucose = settingsManager.settings.high
+        lowGlucose = units == .mgdL ? settingsManager.settings.low : settingsManager.settings.low.asMmolL
+        highGlucose = units == .mgdL ? settingsManager.settings.high : settingsManager.settings.high.asMmolL
         overrideUnit = settingsManager.settings.overrideHbA1cUnit
         displayXgridLines = settingsManager.settings.xGridLines
         displayYgridLines = settingsManager.settings.yGridLines

+ 28 - 29
FreeAPS/Sources/Modules/Home/View/Chart/MainChartView.swift

@@ -92,10 +92,6 @@ struct MainChartView: View {
         return formatter
     }
 
-    private var conversionFactor: Decimal {
-        units == .mmolL ? 0.0555 : 1
-    }
-
     private var upperLimit: Decimal {
         units == .mgdL ? 400 : 22.2
     }
@@ -251,9 +247,9 @@ extension MainChartView {
         Chart {
             /// high and low threshold lines
             if thresholdLines {
-                RuleMark(y: .value("High", highGlucose * conversionFactor)).foregroundStyle(Color.loopYellow)
+                RuleMark(y: .value("High", highGlucose)).foregroundStyle(Color.loopYellow)
                     .lineStyle(.init(lineWidth: 1, dash: [5]))
-                RuleMark(y: .value("Low", lowGlucose * conversionFactor)).foregroundStyle(Color.loopRed)
+                RuleMark(y: .value("Low", lowGlucose)).foregroundStyle(Color.loopRed)
                     .lineStyle(.init(lineWidth: 1, dash: [5]))
             }
         }
@@ -373,14 +369,14 @@ extension MainChartView {
             .chartYAxis { mainChartYAxis }
             .chartYAxis(.hidden)
             .backport.chartXSelection(value: $selection)
-            .chartYScale(domain: minValue ... maxValue)
+            .chartYScale(domain: units == .mgdL ? minValue ... maxValue : minValue.asMmolL ... maxValue.asMmolL)
             .backport.chartForegroundStyleScale(state: state)
         }
     }
 
     @ViewBuilder var selectionPopover: some View {
         if let sgv = selectedGlucose?.glucose {
-            let glucoseToShow = Decimal(sgv) * conversionFactor
+            let glucoseToShow = units == .mgdL ? Decimal(sgv) : Decimal(sgv).asMmolL
             VStack(alignment: .leading) {
                 HStack {
                     Image(systemName: "clock")
@@ -389,12 +385,12 @@ extension MainChartView {
                 }.font(.body).padding(.bottom, 5)
 
                 HStack {
-                    Text(glucoseToShow.formatted(.number.precision(units == .mmolL ? .fractionLength(1) : .fractionLength(0))))
+                    Text(glucoseToShow.description)
                         .bold()
                         + Text(" \(units.rawValue)")
                 }.foregroundStyle(
-                    Decimal(sgv) < lowGlucose ? Color
-                        .red : (Decimal(sgv) > highGlucose ? Color.orange : Color.primary)
+                    glucoseToShow < lowGlucose ? Color
+                        .red : (glucoseToShow > highGlucose ? Color.orange : Color.primary)
                 ).font(.body)
 
                 if let selectedIOBValue, let iob = selectedIOBValue.iob {
@@ -537,7 +533,7 @@ extension MainChartView {
             let bolusDate = insulin.timestamp ?? Date()
 
             if amount != 0, let glucose = timeToNearestGlucose(time: bolusDate.timeIntervalSince1970)?.glucose {
-                let yPosition = (Decimal(glucose) * conversionFactor) + bolusOffset
+                let yPosition = (units == .mgdL ? Decimal(glucose) : Decimal(glucose).asMmolL) + bolusOffset
                 let size = (Config.bolusSize + CGFloat(truncating: amount) * Config.bolusScale) * 1.8
 
                 PointMark(
@@ -563,7 +559,7 @@ extension MainChartView {
             let carbDate = carb.date ?? Date()
 
             if let glucose = timeToNearestGlucose(time: carbDate.timeIntervalSince1970)?.glucose {
-                let yPosition = (Decimal(glucose) * conversionFactor) - bolusOffset
+                let yPosition = (units == .mgdL ? Decimal(glucose) : Decimal(glucose).asMmolL) - bolusOffset
                 let size = (Config.carbsSize + CGFloat(carbAmount) * Config.carbsScale)
                 let limitedSize = size > 30 ? 30 : size
 
@@ -603,7 +599,8 @@ extension MainChartView {
         let low = Double(lowGlucose)
         let high = Double(highGlucose)
 
-        let glucoseValues = state.glucoseFromPersistence.map { Decimal($0.glucose) * conversionFactor }
+        let glucoseValues = state.glucoseFromPersistence
+            .map { units == .mgdL ? Decimal($0.glucose) : Decimal($0.glucose).asMmolL }
 
         let minimum = glucoseValues.min() ?? 0.0
         let maximum = glucoseValues.max() ?? 0.0
@@ -628,27 +625,28 @@ extension MainChartView {
         /// glucose point mark
         /// filtering for high and low bounds in settings
         ForEach(state.glucoseFromPersistence) { item in
+            let glucoseToDisplay = units == .mgdL ? Decimal(item.glucose) : Decimal(item.glucose).asMmolL
             if smooth {
-                LineMark(x: .value("Time", item.date ?? Date()), y: .value("Value", Decimal(item.glucose) * conversionFactor))
+                LineMark(x: .value("Time", item.date ?? Date()), y: .value("Value", glucoseToDisplay))
                     .foregroundStyle(
                         .linearGradient(stops: stops, startPoint: .bottom, endPoint: .top)
                     )
                     .symbol(.circle)
             } else {
-                if item.glucose > Int(highGlucose) {
+                if glucoseToDisplay > highGlucose {
                     PointMark(
                         x: .value("Time", item.date ?? Date(), unit: .second),
-                        y: .value("Value", Decimal(item.glucose) * conversionFactor)
+                        y: .value("Value", glucoseToDisplay)
                     ).foregroundStyle(Color.orange.gradient).symbolSize(20)
-                } else if item.glucose < Int(lowGlucose) {
+                } else if glucoseToDisplay < lowGlucose {
                     PointMark(
                         x: .value("Time", item.date ?? Date(), unit: .second),
-                        y: .value("Value", Decimal(item.glucose) * conversionFactor)
+                        y: .value("Value", glucoseToDisplay)
                     ).foregroundStyle(Color.red.gradient).symbolSize(20)
                 } else {
                     PointMark(
                         x: .value("Time", item.date ?? Date(), unit: .second),
-                        y: .value("Value", Decimal(item.glucose) * conversionFactor)
+                        y: .value("Value", glucoseToDisplay)
                     ).foregroundStyle(Color.green.gradient).symbolSize(20)
                 }
             }
@@ -790,10 +788,10 @@ extension MainChartView {
     private func drawManualGlucose() -> some ChartContent {
         /// manual glucose mark
         ForEach(state.manualGlucoseFromPersistence) { item in
-            let manualGlucose = item.glucose
+            let manualGlucose = units == .mgdL ? Decimal(item.glucose) : Decimal(item.glucose).asMmolL
             PointMark(
                 x: .value("Time", item.date ?? Date(), unit: .second),
-                y: .value("Value", Decimal(manualGlucose) * conversionFactor)
+                y: .value("Value", manualGlucose)
             )
             .symbol {
                 Image(systemName: "drop.fill").font(.system(size: 10)).symbolRenderingMode(.monochrome)
@@ -1052,9 +1050,10 @@ extension MainChartView {
             isTempTargetActive = firstNonZeroTarget.createdAt <= now && now <= end
 
             if firstNonZeroTarget.targetTop != nil {
+                let targetTop = firstNonZeroTarget.targetTop ?? 0
                 calculatedTTs
                     .append(ChartTempTarget(
-                        amount: (firstNonZeroTarget.targetTop ?? 0) * conversionFactor,
+                        amount: units == .mgdL ? targetTop : targetTop.asMmolL,
                         start: firstNonZeroTarget.createdAt,
                         end: end
                     ))
@@ -1178,19 +1177,19 @@ extension MainChartView {
               let minForecast = forecastValues.min(), let maxForecast = forecastValues.max()
         else {
             // default values
-            minValue = 45 * conversionFactor - 20 * conversionFactor
-            maxValue = 270 * conversionFactor + 50 * conversionFactor
+            minValue = 45 - 20
+            maxValue = 270 + 50
             return
         }
 
         // Ensure maxForecast is not more than 100 over maxGlucose
         let adjustedMaxForecast = min(maxForecast, maxGlucose + 100)
 
-        let minOverall = min(minGlucose, minForecast)
-        let maxOverall = max(maxGlucose, adjustedMaxForecast)
+        var minOverall = min(minGlucose, minForecast)
+        var maxOverall = max(maxGlucose, adjustedMaxForecast)
 
-        minValue = minOverall * conversionFactor - 50 * conversionFactor
-        maxValue = maxOverall * conversionFactor + 80 * conversionFactor
+        minValue = minOverall - 50
+        maxValue = maxOverall + 80
     }
 
     private func yAxisChartDataCobChart() {

+ 2 - 11
FreeAPS/Sources/Modules/Home/View/Header/CurrentGlucoseView.swift

@@ -84,10 +84,10 @@ struct CurrentGlucoseView: View {
                 VStack(alignment: .center) {
                     HStack {
                         if let glucoseValue = combinedGlucoseValues.first?.glucose {
-                            let displayGlucose = convertGlucose(glucoseValue, to: units)
+                            let displayGlucose = units == .mgdL ? Decimal(glucoseValue) : Decimal(glucoseValue).asMmolL
                             Text(
                                 glucoseValue == 400 ? "HIGH" :
-                                    glucoseFormatter.string(from: NSNumber(value: displayGlucose)) ?? "--"
+                                    glucoseFormatter.string(from: displayGlucose as NSNumber) ?? "--"
                             )
                             .font(.system(size: 40, weight: .bold, design: .rounded))
                             .foregroundColor(alarm == nil ? colourGlucoseText : .loopRed)
@@ -155,15 +155,6 @@ struct CurrentGlucoseView: View {
         }
     }
 
-    private func convertGlucose(_ value: Int16, to units: GlucoseUnits) -> Double {
-        switch units {
-        case .mmolL:
-            return Double(value) / 18.0
-        case .mgdL:
-            return Double(value)
-        }
-    }
-
     private var delta: String {
         guard combinedGlucoseValues.count >= 2 else {
             return "--"

+ 35 - 23
FreeAPS/Sources/Modules/Home/View/HomeRootView.swift

@@ -787,30 +787,42 @@ extension Home {
                     .font(.subheadline)
                     .foregroundColor(.secondary)
 
-                    List {
-                        DefinitionRow(
-                            term: "IOB (Insulin on Board)",
-                            definition: "Forecasts BG based on the amount of insulin still active in the body.",
-                            color: .insulin
-                        )
-                        DefinitionRow(
-                            term: "ZT (Zero-Temp)",
-                            definition: "Forecasts the worst-case blood glucose (BG) scenario if no carbs are absorbed and insulin delivery is stopped until BG starts rising.",
-                            color: .zt
-                        )
-                        DefinitionRow(
-                            term: "COB (Carbs on Board)",
-                            definition: "Forecasts BG changes by considering the amount of carbohydrates still being absorbed in the body.",
-                            color: .loopYellow
-                        )
-                        DefinitionRow(
-                            term: "UAM (Unannounced Meal)",
-                            definition: "Forecasts BG levels and insulin dosing needs for unexpected meals or other causes of BG rises without prior notice.",
-                            color: .uam
-                        )
+                    if state.settingsManager.settings.displayForecastsAsLines {
+                        List {
+                            DefinitionRow(
+                                term: "IOB (Insulin on Board)",
+                                definition: "Forecasts BG based on the amount of insulin still active in the body.",
+                                color: .insulin
+                            )
+                            DefinitionRow(
+                                term: "ZT (Zero-Temp)",
+                                definition: "Forecasts the worst-case blood glucose (BG) scenario if no carbs are absorbed and insulin delivery is stopped until BG starts rising.",
+                                color: .zt
+                            )
+                            DefinitionRow(
+                                term: "COB (Carbs on Board)",
+                                definition: "Forecasts BG changes by considering the amount of carbohydrates still being absorbed in the body.",
+                                color: .loopYellow
+                            )
+                            DefinitionRow(
+                                term: "UAM (Unannounced Meal)",
+                                definition: "Forecasts BG levels and insulin dosing needs for unexpected meals or other causes of BG rises without prior notice.",
+                                color: .uam
+                            )
+                        }
+                        .padding(.trailing, 10)
+                        .navigationBarTitle("Legend", displayMode: .inline)
+                    } else {
+                        List {
+                            DefinitionRow(
+                                term: "Cone of Uncertainty",
+                                definition: "For simplicity reasons, oref's various forecast curves are displayed as a \"Cone of Uncertainty\" that depicts a possible, forecasted range of future glucose fluctuation based on the current data and the algothim's result.",
+                                color: Color.blue.opacity(0.5)
+                            )
+                        }
+                        .padding(.trailing, 10)
+                        .navigationBarTitle("Legend", displayMode: .inline)
                     }
-                    .padding(.trailing, 10)
-                    .navigationBarTitle("Legend", displayMode: .inline)
 
                     Button { state.isLegendPresented.toggle() }
                     label: { Text("Got it!").frame(maxWidth: .infinity, alignment: .center) }