Просмотр исходного кода

use GlucoseStored wip...some cleanup...

polscm32 2 лет назад
Родитель
Сommit
5d61dc6e1f

+ 8 - 8
FreeAPS.xcodeproj/project.pbxproj

@@ -311,12 +311,12 @@
 		5825D15D2BD4058F00F36E9B /* Target+CoreDataProperties.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5825D12F2BD4058F00F36E9B /* Target+CoreDataProperties.swift */; };
 		5825D15E2BD4058F00F36E9B /* Protein+CoreDataClass.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5825D1302BD4058F00F36E9B /* Protein+CoreDataClass.swift */; };
 		5825D15F2BD4058F00F36E9B /* Protein+CoreDataProperties.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5825D1312BD4058F00F36E9B /* Protein+CoreDataProperties.swift */; };
-		5825D1602BD4058F00F36E9B /* GlucoseStored+CoreDataClass.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5825D1322BD4058F00F36E9B /* GlucoseStored+CoreDataClass.swift */; };
-		5825D1612BD4058F00F36E9B /* GlucoseStored+CoreDataProperties.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5825D1332BD4058F00F36E9B /* GlucoseStored+CoreDataProperties.swift */; };
 		583684062BD178DB00070A60 /* GlucoseStored+helper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 583684052BD178DB00070A60 /* GlucoseStored+helper.swift */; };
 		583684082BD195A700070A60 /* Determination.swift in Sources */ = {isa = PBXBuildFile; fileRef = 583684072BD195A700070A60 /* Determination.swift */; };
 		5837A5302BD2E3C700A5DC04 /* MealsStored+helper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5837A52F2BD2E3C700A5DC04 /* MealsStored+helper.swift */; };
 		5837A5322BD2E81100A5DC04 /* InsulinStored+helper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5837A5312BD2E81100A5DC04 /* InsulinStored+helper.swift */; };
+		5856174D2BDADA3F009B23D7 /* GlucoseStored+CoreDataClass.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5856174B2BDADA3F009B23D7 /* GlucoseStored+CoreDataClass.swift */; };
+		5856174E2BDADA3F009B23D7 /* GlucoseStored+CoreDataProperties.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5856174C2BDADA3F009B23D7 /* GlucoseStored+CoreDataProperties.swift */; };
 		587DA1F62B77F3DD00B28F8A /* SettingsRowView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 587DA1F52B77F3DD00B28F8A /* SettingsRowView.swift */; };
 		5887527C2BD986E1008B081D /* OpenAPSBattery.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5887527B2BD986E1008B081D /* OpenAPSBattery.swift */; };
 		588752842BD9986A008B081D /* OpenAPS_Battery+CoreDataClass.swift in Sources */ = {isa = PBXBuildFile; fileRef = 588752822BD9986A008B081D /* OpenAPS_Battery+CoreDataClass.swift */; };
@@ -926,12 +926,12 @@
 		5825D12F2BD4058F00F36E9B /* Target+CoreDataProperties.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Target+CoreDataProperties.swift"; sourceTree = SOURCE_ROOT; };
 		5825D1302BD4058F00F36E9B /* Protein+CoreDataClass.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Protein+CoreDataClass.swift"; sourceTree = SOURCE_ROOT; };
 		5825D1312BD4058F00F36E9B /* Protein+CoreDataProperties.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Protein+CoreDataProperties.swift"; sourceTree = SOURCE_ROOT; };
-		5825D1322BD4058F00F36E9B /* GlucoseStored+CoreDataClass.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "GlucoseStored+CoreDataClass.swift"; sourceTree = SOURCE_ROOT; };
-		5825D1332BD4058F00F36E9B /* GlucoseStored+CoreDataProperties.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "GlucoseStored+CoreDataProperties.swift"; sourceTree = SOURCE_ROOT; };
 		583684052BD178DB00070A60 /* GlucoseStored+helper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "GlucoseStored+helper.swift"; sourceTree = "<group>"; };
 		583684072BD195A700070A60 /* Determination.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Determination.swift; sourceTree = "<group>"; };
 		5837A52F2BD2E3C700A5DC04 /* MealsStored+helper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "MealsStored+helper.swift"; sourceTree = "<group>"; };
 		5837A5312BD2E81100A5DC04 /* InsulinStored+helper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "InsulinStored+helper.swift"; sourceTree = "<group>"; };
+		5856174B2BDADA3F009B23D7 /* GlucoseStored+CoreDataClass.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "GlucoseStored+CoreDataClass.swift"; sourceTree = SOURCE_ROOT; };
+		5856174C2BDADA3F009B23D7 /* GlucoseStored+CoreDataProperties.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "GlucoseStored+CoreDataProperties.swift"; sourceTree = SOURCE_ROOT; };
 		587DA1F52B77F3DD00B28F8A /* SettingsRowView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsRowView.swift; sourceTree = "<group>"; };
 		5887527B2BD986E1008B081D /* OpenAPSBattery.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OpenAPSBattery.swift; sourceTree = "<group>"; };
 		588752822BD9986A008B081D /* OpenAPS_Battery+CoreDataClass.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "OpenAPS_Battery+CoreDataClass.swift"; sourceTree = SOURCE_ROOT; };
@@ -2138,6 +2138,8 @@
 		5825D1052BD4056700F36E9B /* Classes+Properties */ = {
 			isa = PBXGroup;
 			children = (
+				5856174B2BDADA3F009B23D7 /* GlucoseStored+CoreDataClass.swift */,
+				5856174C2BDADA3F009B23D7 /* GlucoseStored+CoreDataProperties.swift */,
 				588752822BD9986A008B081D /* OpenAPS_Battery+CoreDataClass.swift */,
 				588752832BD9986A008B081D /* OpenAPS_Battery+CoreDataProperties.swift */,
 				5825D1062BD4058F00F36E9B /* BGaverages+CoreDataClass.swift */,
@@ -2184,8 +2186,6 @@
 				5825D12F2BD4058F00F36E9B /* Target+CoreDataProperties.swift */,
 				5825D1302BD4058F00F36E9B /* Protein+CoreDataClass.swift */,
 				5825D1312BD4058F00F36E9B /* Protein+CoreDataProperties.swift */,
-				5825D1322BD4058F00F36E9B /* GlucoseStored+CoreDataClass.swift */,
-				5825D1332BD4058F00F36E9B /* GlucoseStored+CoreDataProperties.swift */,
 			);
 			path = "Classes+Properties";
 			sourceTree = "<group>";
@@ -3064,7 +3064,6 @@
 				19DC677F29CA675700FD9EC4 /* OverrideProfilesDataFlow.swift in Sources */,
 				1935364028496F7D001E0B16 /* Oref2_variables.swift in Sources */,
 				CE2FAD3A297D93F0001A872C /* BloodGlucoseExtensions.swift in Sources */,
-				5825D1612BD4058F00F36E9B /* GlucoseStored+CoreDataProperties.swift in Sources */,
 				38E4453A274E411700EC9A94 /* Disk+[UIImage].swift in Sources */,
 				72F1BD388F42FCA6C52E4500 /* ConfigEditorProvider.swift in Sources */,
 				E39E418C56A5A46B61D960EE /* ConfigEditorStateModel.swift in Sources */,
@@ -3129,6 +3128,7 @@
 				6632A0DC746872439A858B44 /* ISFEditorDataFlow.swift in Sources */,
 				DBA5254DBB2586C98F61220C /* ISFEditorProvider.swift in Sources */,
 				1BBB001DAD60F3B8CEA4B1C7 /* ISFEditorStateModel.swift in Sources */,
+				5856174E2BDADA3F009B23D7 /* GlucoseStored+CoreDataProperties.swift in Sources */,
 				F816826028DB441800054060 /* BluetoothTransmitter.swift in Sources */,
 				BD7DA9A72AE06E2B00601B20 /* BolusCalculatorConfigProvider.swift in Sources */,
 				38192E0D261BAF980094D973 /* ConvenienceExtensions.swift in Sources */,
@@ -3165,7 +3165,6 @@
 				CE48C86428CA69D5007C0598 /* OmniBLEPumpManagerExtensions.swift in Sources */,
 				38E8755427561E9800975559 /* DataFlow.swift in Sources */,
 				38E44522274E3DDC00EC9A94 /* NetworkReachabilityManager.swift in Sources */,
-				5825D1602BD4058F00F36E9B /* GlucoseStored+CoreDataClass.swift in Sources */,
 				CE7CA34F2A064973004BE681 /* BaseIntentsRequest.swift in Sources */,
 				D2165E9D78EFF692C1DED1C6 /* AddTempTargetDataFlow.swift in Sources */,
 				CE82E02728E869DF00473A9C /* AlertEntry.swift in Sources */,
@@ -3175,6 +3174,7 @@
 				E0D4F80527513ECF00BDF1FE /* HealthKitSample.swift in Sources */,
 				919DBD08F13BAFB180DF6F47 /* AddTempTargetStateModel.swift in Sources */,
 				8BC2F5A29AD1ED08AC0EE013 /* AddTempTargetRootView.swift in Sources */,
+				5856174D2BDADA3F009B23D7 /* GlucoseStored+CoreDataClass.swift in Sources */,
 				38A00B1F25FC00F7006BC0B0 /* Autotune.swift in Sources */,
 				38AAF85525FFF846004AF583 /* CurrentGlucoseView.swift in Sources */,
 				041D1E995A6AE92E9289DC49 /* BolusDataFlow.swift in Sources */,

+ 12 - 7
FreeAPS/Sources/APS/APSManager.swift

@@ -330,22 +330,23 @@ final class BaseAPSManager: APSManager, Injectable {
 
     func determineBasal() -> AnyPublisher<Bool, Never> {
         debug(.apsManager, "Start determine basal")
-        guard let glucose = storage.retrieve(OpenAPS.Monitor.glucose, as: [BloodGlucose].self), glucose.isNotEmpty else {
+        let glucose = fetchGlucoseData(forPeriod: "30min", withPredicate: NSPredicate.predicateFor30MinAgo, fetchLimit: 4)
+        guard glucose.count > 2 else {
             debug(.apsManager, "Not enough glucose data")
             processError(APSError.glucoseError(message: "Not enough glucose data"))
             return Just(false).eraseToAnyPublisher()
         }
 
-        let lastGlucoseDate = glucoseStorage.lastGlucoseDate()
-        guard lastGlucoseDate >= Date().addingTimeInterval(-12.minutes.timeInterval) else {
+        let dateOfLastGlucose = glucose.first?.date
+        guard dateOfLastGlucose ?? Date() >= Date().addingTimeInterval(-12.minutes.timeInterval) else {
             debug(.apsManager, "Glucose data is stale")
             processError(APSError.glucoseError(message: "Glucose data is stale"))
             return Just(false).eraseToAnyPublisher()
         }
 
         // Only let glucose be flat when 400 mg/dl
-        if (glucoseStorage.recent().last?.glucose ?? 100) != 400 {
-            guard glucoseStorage.isGlucoseNotFlat() else {
+        if (glucose.first?.glucose ?? 100) != 400 {
+            guard !GlucoseStored.glucoseIsFlat(glucose) else {
                 debug(.apsManager, "Glucose data is too flat")
                 processError(APSError.glucoseError(message: "Glucose data is too flat"))
                 return Just(false).eraseToAnyPublisher()
@@ -905,9 +906,13 @@ final class BaseAPSManager: APSManager, Injectable {
     }
 
     // fetch glucose for time interval
-    private func fetchGlucoseData(forPeriod period: String, withPredicate predicate: NSPredicate) -> [GlucoseStored] {
+    private func fetchGlucoseData(
+        forPeriod period: String,
+        withPredicate predicate: NSPredicate,
+        fetchLimit: Int? = nil
+    ) -> [GlucoseStored] {
         do {
-            let fetchedData = try viewContext.fetch(GlucoseStored.fetch(predicate, ascending: false))
+            let fetchedData = try privateContext.fetch(GlucoseStored.fetch(predicate, ascending: false, fetchLimit: fetchLimit))
             debugPrint(
                 "APSManager: \(CoreDataStack.identifier) \(DebuggingIdentifiers.succeeded) fetched glucose for \(period) period"
             )

+ 20 - 1
FreeAPS/Sources/Modules/DataTable/DataTableStateModel.swift

@@ -10,7 +10,7 @@ extension DataTable {
         @Injected() var pumpHistoryStorage: PumpHistoryStorage!
         @Injected() var healthKitManager: HealthKitManager!
 
-        let coredataContext = CoreDataStack.shared.persistentContainer.viewContext
+        let coredataContext = CoreDataStack.shared.viewContext
 
         @Published var mode: Mode = .treatments
         @Published var treatments: [Treatment] = []
@@ -229,6 +229,7 @@ extension DataTable {
 
         func addManualGlucose() {
             let glucose = units == .mmolL ? manualGlucose.asMgdL : manualGlucose
+            let glucoseAsInt = Int(glucose)
             let now = Date()
             let id = UUID().uuidString
 
@@ -248,6 +249,24 @@ extension DataTable {
             // Save to Health
             var saveToHealth = [BloodGlucose]()
             saveToHealth.append(saveToJSON)
+
+            // save to core data
+            let newItem = GlucoseStored(context: coredataContext)
+            newItem.id = UUID()
+            newItem.date = Date()
+            newItem.glucose = Int16(glucoseAsInt)
+            newItem.isManual = true
+
+            do {
+                try coredataContext.save()
+                debugPrint(
+                    "Data table state model: \(#function) \(CoreDataStack.identifier) \(DebuggingIdentifiers.succeeded) added manual glucose to core data"
+                )
+            } catch {
+                debugPrint(
+                    "Data table state model: \(#function) \(CoreDataStack.identifier) \(DebuggingIdentifiers.failed) failed to add manual glucose to core data"
+                )
+            }
         }
     }
 }

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

@@ -9,7 +9,6 @@ protocol HomeProvider: Provider {
     var suggestion: Suggestion? { get }
     var enactedSuggestion: Suggestion? { get }
     func heartbeatNow()
-    func filteredGlucose(hours: Int) -> [BloodGlucose]
     func pumpHistory(hours: Int) -> [PumpHistoryEvent]
     func pumpSettings() -> PumpSettings
     func autotunedBasalProfile() -> [BasalProfileEntry]

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

@@ -27,12 +27,6 @@ extension Home {
             apsManager.heartbeat(date: Date())
         }
 
-        func filteredGlucose(hours: Int) -> [BloodGlucose] {
-            glucoseStorage.recent().filter {
-                $0.dateString.addingTimeInterval(hours.hours.timeInterval) > Date()
-            }
-        }
-
         func pumpHistory(hours: Int) -> [PumpHistoryEvent] {
             pumpHistoryStorage.recent().filter {
                 $0.timestamp.addingTimeInterval(hours.hours.timeInterval) > Date()

+ 1 - 73
FreeAPS/Sources/Modules/Home/HomeStateModel.swift

@@ -11,7 +11,6 @@ extension Home {
         @Injected() var nightscoutManager: NightscoutManager!
         private let timer = DispatchTimer(timeInterval: 5)
         private(set) var filteredHours = 24
-        @Published var glucose: [BloodGlucose] = []
         @Published var manualGlucose: [BloodGlucose] = []
         @Published var announcement: [Announcement] = []
         @Published var uploadStats = false
@@ -24,7 +23,6 @@ extension Home {
         @Published var autotunedBasalProfile: [BasalProfileEntry] = []
         @Published var basalProfile: [BasalProfileEntry] = []
         @Published var tempTargets: [TempTarget] = []
-        @Published var carbs: [CarbsEntry] = []
         @Published var timerDate = Date()
         @Published var closedLoop = false
         @Published var pumpSuspended = false
@@ -42,7 +40,6 @@ extension Home {
         @Published var errorDate: Date? = nil
         @Published var bolusProgress: Decimal?
         @Published var eventualBG: Int?
-        @Published var carbsRequired: Decimal?
         @Published var allowManualTemp = false
         @Published var units: GlucoseUnits = .mmolL
         @Published var pumpDisplayState: PumpDisplayState?
@@ -65,32 +62,24 @@ extension Home {
         @Published var tins: Bool = false
         @Published var isTempTargetActive: Bool = false
 
-        @Published var cob: Decimal = 0
         @Published var roundedTotalBolus: String = ""
 
         @Published var selectedTab: Int = 0
 
         @Published var waitForSuggestion: Bool = false
 
-        @Published var carbsForChart: [CarbsEntry] = []
-        @Published var fpusForChart: [CarbsEntry] = []
-
         let context = CoreDataStack.shared.viewContext
 
         override func subscribe() {
-            setupGlucose()
             setupBasals()
             setupBoluses()
             setupSuspensions()
             setupPumpSettings()
             setupBasalProfile()
             setupTempTargets()
-            setupCarbs()
             setupReservoir()
             setupAnnouncements()
             setupCurrentPumpTimezone()
-            filterCarbs()
-            filterFpus()
 
             uploadStats = settingsManager.settings.uploadStats
             units = settingsManager.settings.units
@@ -117,7 +106,6 @@ extension Home {
             broadcaster.register(PumpSettingsObserver.self, observer: self)
             broadcaster.register(BasalProfileObserver.self, observer: self)
             broadcaster.register(TempTargetsObserver.self, observer: self)
-            broadcaster.register(CarbsObserver.self, observer: self)
             broadcaster.register(PumpReservoirObserver.self, observer: self)
 
             animatedBackground = settingsManager.settings.animatedBackground
@@ -204,28 +192,6 @@ extension Home {
                 .store(in: &lifetime)
         }
 
-        func filterCarbs() {
-            DispatchQueue.main.async { [weak self] in
-                guard let self = self else { return }
-                let allCarbs = self.provider.carbs(hours: self.filteredHours)
-                let filteredCarbs = allCarbs.filter { !($0.isFPU ?? false) }
-
-                self.carbsForChart.removeAll()
-                self.carbsForChart.append(contentsOf: filteredCarbs)
-            }
-        }
-
-        func filterFpus() {
-            DispatchQueue.main.async { [weak self] in
-                guard let self = self else { return }
-                let allCarbs = self.provider.carbs(hours: self.filteredHours)
-                let filteredFpus = allCarbs.filter { $0.isFPU ?? false }
-
-                self.fpusForChart.removeAll()
-                self.fpusForChart.append(contentsOf: filteredFpus)
-            }
-        }
-
         func runLoop() {
             provider.heartbeatNow()
         }
@@ -246,28 +212,6 @@ extension Home {
             }
         }
 
-        private func setupGlucose() {
-            DispatchQueue.main.async { [weak self] in
-                guard let self = self else { return }
-                let filteredGlucose = self.provider.filteredGlucose(hours: self.filteredHours)
-
-                self.glucose = filteredGlucose
-                self.manualGlucose = filteredGlucose.filter { $0.type == GlucoseType.manual.rawValue }
-
-                self.recentGlucose = self.glucose.last
-
-                if self.glucose.count >= 2 {
-                    self
-                        .glucoseDelta = (self.recentGlucose?.glucose ?? 0) -
-                        (self.glucose[self.glucose.count - 2].glucose ?? 0)
-                } else {
-                    self.glucoseDelta = nil
-                }
-
-                self.alarm = self.provider.glucoseStorage.alarm
-            }
-        }
-
         private func setupBasals() {
             DispatchQueue.main.async { [weak self] in
                 guard let self = self else { return }
@@ -356,13 +300,6 @@ extension Home {
             }
         }
 
-        private func setupCarbs() {
-            DispatchQueue.main.async { [weak self] in
-                guard let self = self else { return }
-                self.carbs = self.provider.carbs(hours: self.filteredHours)
-            }
-        }
-
         private func setupAnnouncements() {
             DispatchQueue.main.async { [weak self] in
                 guard let self = self else { return }
@@ -422,12 +359,11 @@ extension Home.StateModel:
     PumpSettingsObserver,
     BasalProfileObserver,
     TempTargetsObserver,
-    CarbsObserver,
     PumpReservoirObserver,
     PumpTimeZoneObserver
 {
     func glucoseDidUpdate(_: [BloodGlucose]) {
-        setupGlucose()
+//        setupGlucose()
     }
 
     func determinationDidUpdate(_: Determination) {
@@ -449,8 +385,6 @@ extension Home.StateModel:
         displayYgridLines = settingsManager.settings.yGridLines
         thresholdLines = settingsManager.settings.rulerMarks
         tins = settingsManager.settings.tins
-
-        setupGlucose()
     }
 
     func pumpHistoryDidUpdate(_: [PumpHistoryEvent]) {
@@ -472,12 +406,6 @@ extension Home.StateModel:
         setupTempTargets()
     }
 
-    func carbsDidUpdate(_: [CarbsEntry]) {
-        setupCarbs()
-        filterFpus()
-        filterCarbs()
-    }
-
     func pumpReservoirDidChange(_: Decimal) {
         setupReservoir()
     }

+ 17 - 16
FreeAPS/Sources/Modules/Home/View/Chart/MainChartView.swift

@@ -47,9 +47,6 @@ struct MainChartView: View {
         static let minGlucose = 45
     }
 
-    @Binding var glucose: [BloodGlucose]
-    @Binding var manualGlucose: [BloodGlucose]
-    @Binding var fpusForChart: [CarbsEntry]
     @Binding var units: GlucoseUnits
     @Binding var tempBasals: [PumpHistoryEvent]
     @Binding var boluses: [PumpHistoryEvent]
@@ -106,11 +103,16 @@ struct MainChartView: View {
     ) var insulinFromPersistence: FetchedResults<InsulinStored>
 
     @FetchRequest(
-        fetchRequest: GlucoseStored.fetch(NSPredicate.predicateForOneDayAgo, ascending: true),
+        fetchRequest: GlucoseStored.fetch(NSPredicate.glucose, ascending: true),
         animation: Animation.bouncy
     ) var glucoseFromPersistence: FetchedResults<GlucoseStored>
 
     @FetchRequest(
+        fetchRequest: GlucoseStored.fetch(NSPredicate.manualGlucose, ascending: true),
+        animation: Animation.bouncy
+    ) var manualGlucoseFromPersistence: FetchedResults<GlucoseStored>
+
+    @FetchRequest(
         fetchRequest: OrefDetermination.fetch(NSPredicate.enactedDetermination),
         animation: Animation.bouncy
     ) var determinations: FetchedResults<OrefDetermination>
@@ -251,7 +253,7 @@ extension MainChartView {
                 }
             }
             .id("MainChart")
-            .onChange(of: glucose) { _ in
+            .onChange(of: glucoseFromPersistence.map(\.id)) { _ in
                 calculatePredictions()
             }
             .onChange(of: boluses) { _ in
@@ -546,16 +548,15 @@ extension MainChartView {
 
     private func drawManualGlucose() -> some ChartContent {
         /// manual glucose mark
-        ForEach(manualGlucose) { item in
-            if let manualGlucose = item.glucose {
-                PointMark(
-                    x: .value("Time", item.dateString, unit: .second),
-                    y: .value("Value", Decimal(manualGlucose) * conversionFactor)
-                )
-                .symbol {
-                    Image(systemName: "drop.fill").font(.system(size: 10)).symbolRenderingMode(.monochrome)
-                        .foregroundStyle(.red)
-                }
+        ForEach(manualGlucoseFromPersistence) { item in
+            let manualGlucose = item.glucose
+            PointMark(
+                x: .value("Time", item.date ?? Date(), unit: .second),
+                y: .value("Value", Decimal(manualGlucose) * conversionFactor)
+            )
+            .symbol {
+                Image(systemName: "drop.fill").font(.system(size: 10)).symbolRenderingMode(.monochrome)
+                    .foregroundStyle(.red)
             }
         }
     }
@@ -898,7 +899,7 @@ extension MainChartView {
     // MARK: - Chart formatting
 
     private func yAxisChartData() {
-        let glucoseMapped = glucose.compactMap(\.glucose)
+        let glucoseMapped = glucoseFromPersistence.map(\.glucose)
         guard let minGlucose = glucoseMapped.min(), let maxGlucose = glucoseMapped.max() else {
             // default values
             minValue = 45 * conversionFactor - 20 * conversionFactor

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

@@ -373,9 +373,6 @@ extension Home {
                 }
 
                 MainChartView(
-                    glucose: $state.glucose,
-                    manualGlucose: $state.manualGlucose,
-                    fpusForChart: $state.fpusForChart,
                     units: $state.units,
                     tempBasals: $state.tempBasals,
                     boluses: $state.boluses,

+ 1 - 0
GlucoseStored+CoreDataProperties.swift

@@ -10,6 +10,7 @@ public extension GlucoseStored {
     @NSManaged var direction: String?
     @NSManaged var glucose: Int16
     @NSManaged var id: UUID?
+    @NSManaged var isManual: Bool
 }
 
 extension GlucoseStored: Identifiable {}

+ 1 - 0
Model/Core_Data.xcdatamodeld/Core_Data.xcdatamodel/contents

@@ -30,6 +30,7 @@
         <attribute name="direction" optional="YES" attributeType="String"/>
         <attribute name="glucose" optional="YES" attributeType="Integer 16" defaultValueString="0" usesScalarValueType="YES"/>
         <attribute name="id" optional="YES" attributeType="UUID" usesScalarValueType="NO"/>
+        <attribute name="isManual" optional="YES" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
     </entity>
     <entity name="HbA1c" representedClassName="HbA1c" syncable="YES">
         <attribute name="date" optional="YES" attributeType="Date" usesScalarValueType="NO"/>

+ 25 - 1
Model/Helper/GlucoseStored+helper.swift

@@ -2,13 +2,25 @@ import CoreData
 import Foundation
 
 extension GlucoseStored {
-    static func fetch(_ predicate: NSPredicate = .all, ascending: Bool) -> NSFetchRequest<GlucoseStored> {
+    static func fetch(_ predicate: NSPredicate = .all, ascending: Bool, fetchLimit: Int? = nil) -> NSFetchRequest<GlucoseStored> {
         let request = GlucoseStored.fetchRequest()
         request.sortDescriptors = [NSSortDescriptor(keyPath: \GlucoseStored.date, ascending: ascending)]
         request.predicate = predicate
+        if let limit = fetchLimit {
+            request.fetchLimit = limit
+        }
         return request
     }
 
+    static func glucoseIsFlat(_ glucose: [GlucoseStored]) -> Bool {
+        guard glucose.count >= 3 else { return false }
+
+        let lastThreeValues = glucose.suffix(3)
+        let firstValue = lastThreeValues.last?.glucose
+
+        return lastThreeValues.allSatisfy { $0.glucose == firstValue }
+    }
+
 //    static func asyncFetch(_ predicate: NSPredicate = NSPredicate(value: true), completion: @escaping (NSAsynchronousFetchResult<GlucoseStored>)->Void) -> NSAsynchronousFetchRequest<GlucoseStored> {
 //           let request: NSFetchRequest<GlucoseStored> = GlucoseStored.fetchRequest()
 //           request.sortDescriptors = [NSSortDescriptor(keyPath: \GlucoseStored.date, ascending: true)]
@@ -22,3 +34,15 @@ extension GlucoseStored {
 //           return asyncFetchRequest
 //       }
 }
+
+extension NSPredicate {
+    static var glucose: NSPredicate {
+        let date = Date.oneDayAgo
+        return NSPredicate(format: "isManual == %@ AND date >= %@", false as NSNumber, date as NSDate)
+    }
+
+    static var manualGlucose: NSPredicate {
+        let date = Date.oneDayAgo
+        return NSPredicate(format: "isManual == %@ AND date >= %@", true as NSNumber, date as NSDate)
+    }
+}