فهرست منبع

refactoring, cone instead of single forecast lines

polscm32 aka Marvout 1 سال پیش
والد
کامیت
7a91cfcad5

+ 1 - 1
FreeAPS.xcodeproj/xcshareddata/xcschemes/Trio.xcscheme

@@ -3,7 +3,7 @@
    LastUpgradeVersion = "1240"
    version = "1.3">
    <BuildAction
-      parallelizeBuildables = "NO"
+      parallelizeBuildables = "YES"
       buildImplicitDependencies = "YES">
       <BuildActionEntries>
          <BuildActionEntry

+ 29 - 37
FreeAPS/Sources/Modules/Bolus/BolusStateModel.swift

@@ -97,9 +97,11 @@ extension Bolus {
         @Published var glucoseFromPersistence: [GlucoseStored] = []
         @Published var determination: [OrefDetermination] = []
         @Published var preprocessedData: [(id: UUID, forecast: Forecast, forecastValue: ForecastValue)] = []
-        @Published var simulatedDetermination: Determination?
         @Published var predictionsForChart: Predictions?
 
+        @Published var minForecast: [Int] = []
+        @Published var maxForecast: [Int] = []
+
         let now = Date.now
 
         let context = CoreDataStack.shared.persistentContainer.viewContext
@@ -114,6 +116,10 @@ extension Bolus {
             coreDataObserver = CoreDataObserver()
             registerHandlers()
 
+            Task {
+                await updateForecasts()
+            }
+
             setupGlucoseArray()
             setupDeterminationsArray()
 
@@ -624,7 +630,7 @@ extension Bolus.StateModel {
                 predicate: NSPredicate.enactedDetermination
             )
             await updateDeterminationsArray(with: ids)
-            await updateForecastData()
+            await updateForecasts()
         }
     }
 
@@ -658,45 +664,31 @@ extension Bolus.StateModel {
 }
 
 extension Bolus.StateModel {
-    func preprocessForecastData() async
-        -> [(id: UUID, forecastID: NSManagedObjectID, forecastValueIDs: [NSManagedObjectID])]
-    {
-        guard let id = determination.first?.objectID else {
-            return []
-        }
-
-        // Get forecast and forecast values
-        let forecastIDs = await determinationStorage.getForecastIDs(for: id, in: context)
-        var result: [(id: UUID, forecastID: NSManagedObjectID, forecastValueIDs: [NSManagedObjectID])] = []
+    @MainActor func updateForecasts() async {
+        let simulatedDetermination = await apsManager.simulateDetermineBasal(carbs: carbs, iob: amount)
+        predictionsForChart = simulatedDetermination?.predictions
 
-        for forecastID in forecastIDs {
-            // Get the forecast value IDs for the given forecast ID
-            let forecastValueIDs = await determinationStorage.getForecastValueIDs(for: forecastID, in: context)
-            let uuid = UUID()
-            result.append((id: uuid, forecastID: forecastID, forecastValueIDs: forecastValueIDs))
+        let iob: [Int] = predictionsForChart?.iob ?? []
+        let zt: [Int] = predictionsForChart?.zt ?? []
+        let cob: [Int] = predictionsForChart?.cob ?? []
+        let uam: [Int] = predictionsForChart?.uam ?? []
+
+        // Filter out the empty arrays and find the maximum length of the remaining arrays
+        let nonEmptyArrays: [[Int]] = [iob, zt, cob, uam].filter { !$0.isEmpty }
+        guard !nonEmptyArrays.isEmpty, let maxCount = nonEmptyArrays.map(\.count).max(), maxCount > 0 else {
+            minForecast = []
+            maxForecast = []
+            return
         }
 
-        return result
-    }
-
-    @MainActor func updateForecastData() async {
-        let forecastData = await preprocessForecastData()
-
-        preprocessedData = forecastData.reduce(into: []) { result, data in
-            guard let forecast = try? context.existingObject(with: data.forecastID) as? Forecast else {
-                return
-            }
-
-            for forecastValueID in data.forecastValueIDs {
-                if let forecastValue = try? context.existingObject(with: forecastValueID) as? ForecastValue {
-                    result.append((id: data.id, forecast: forecast, forecastValue: forecastValue))
-                }
-            }
+        minForecast = (0 ..< maxCount).map { index -> Int in
+            let valuesAtCurrentIndex = nonEmptyArrays.compactMap { $0.indices.contains(index) ? $0[index] : nil }
+            return valuesAtCurrentIndex.min() ?? 0
         }
-    }
 
-    func updateForecasts() async {
-        simulatedDetermination = await apsManager.simulateDetermineBasal(carbs: carbs, iob: amount)
-        predictionsForChart = simulatedDetermination?.predictions
+        maxForecast = (0 ..< maxCount).map { index -> Int in
+            let valuesAtCurrentIndex = nonEmptyArrays.compactMap { $0.indices.contains(index) ? $0[index] : nil }
+            return valuesAtCurrentIndex.max() ?? 0
+        }
     }
 }

+ 5 - 1
FreeAPS/Sources/Modules/Bolus/View/BolusRootView.swift

@@ -426,7 +426,11 @@ extension Bolus {
                                     textColor: colorScheme == .dark ? .white : .blue,
                                     maxLength: 5,
                                     numberFormatter: formatter
-                                )
+                                ).onChange(of: state.amount) { _ in
+                                    Task {
+                                        await state.updateForecasts()
+                                    }
+                                }
                                 Text(" U").foregroundColor(.secondary)
                             }
 

+ 28 - 31
FreeAPS/Sources/Modules/Bolus/View/ForeCastChart.swift

@@ -23,7 +23,8 @@ struct ForeCastChart: View {
         Chart {
             drawGlucose()
             drawCurrentTimeMarker()
-            drawForecasts()
+//            drawForecasts()
+            drawForecastArea()
         }
         .chartXAxis { forecastChartXAxis }
         .chartXScale(domain: startMarker ... endMarker)
@@ -63,41 +64,37 @@ struct ForeCastChart: View {
         return currentTime.addingTimeInterval(timeInterval)
     }
 
-    private func drawForecasts() -> some ChartContent {
-        let predictions = state.predictionsForChart
-
-        let predictionData = [
-            ("IOB", predictions?.iob),
-            ("ZT", predictions?.zt),
-            ("COB", predictions?.cob),
-            ("UAM", predictions?.uam)
-        ]
-
-        return ForEach(predictionData, id: \.0) { name, values in
-            if let values = values {
-                ForEach(values.indices, id: \.self) { index in
-                    LineMark(
-                        x: .value("Time", timeForIndex(Int32(index))),
-                        y: .value("Value", Decimal(values[index]) * conversionFactor)
-                    )
-                    .foregroundStyle(by: .value("Prediction Type", name))
-                }
-            }
+    private func drawForecastArea() -> some ChartContent {
+        ForEach(state.minForecast.indices, id: \.self) { index in
+            AreaMark(
+                x: .value("Time", timeForIndex(Int32(index))),
+                yStart: .value("Min Value", Decimal(state.minForecast[index]) * conversionFactor),
+                yEnd: .value("Max Value", Decimal(state.maxForecast[index]) * conversionFactor)
+            )
+            .foregroundStyle(Color.blue.opacity(0.3))
         }
     }
 
 //    private func drawForecasts() -> some ChartContent {
-//        ForEach(state.preprocessedData, id: \.id) { tuple in
-//            let forecastValue = tuple.forecastValue
-//            let forecast = tuple.forecast
-//            let valueAsDecimal = Decimal(forecastValue.value)
-//            let displayValue = units == .mmolL ? valueAsDecimal.asMmolL : valueAsDecimal
+//        let predictions = state.predictionsForChart
+//
+//        let predictionData = [
+//            ("IOB", predictions?.iob),
+//            ("ZT", predictions?.zt),
+//            ("COB", predictions?.cob),
+//            ("UAM", predictions?.uam)
+//        ]
 //
-//            LineMark(
-//                x: .value("Time", timeForIndex(forecastValue.index)),
-//                y: .value("Value", displayValue)
-//            )
-//            .foregroundStyle(by: .value("Predictions", forecast.type ?? ""))
+//        return ForEach(predictionData, id: \.0) { name, values in
+//            if let values = values {
+//                ForEach(values.indices, id: \.self) { index in
+//                    LineMark(
+//                        x: .value("Time", timeForIndex(Int32(index))),
+//                        y: .value("Value", Decimal(values[index]) * conversionFactor)
+//                    )
+//                    .foregroundStyle(by: .value("Prediction Type", name))
+//                }
+//            }
 //        }
 //    }
 

+ 1 - 3
FreeAPS/Sources/Modules/Home/View/Chart/MainChartView.swift

@@ -359,8 +359,7 @@ extension MainChartView {
             .chartXAxis { basalChartXAxis }
             .chartXAxis(.hidden)
             .chartYAxis(.hidden)
-            .rotationEffect(.degrees(180))
-            .scaleEffect(x: -1, y: 1, anchor: .center)
+            .chartPlotStyle { basalChartPlotStyle($0) }
         }
     }
 
@@ -1036,7 +1035,6 @@ extension MainChartView {
         plotContent
             .rotationEffect(.degrees(180))
             .scaleEffect(x: -1, y: 1)
-            .chartXAxis(.hidden)
     }
 
     private var mainChartXAxis: some AxisContent {