Explorar o código

use determination in aps manager, save smbs

polscm32 %!s(int64=2) %!d(string=hai) anos
pai
achega
e6d7f41533

+ 9 - 1
FreeAPS.xcodeproj/project.pbxproj

@@ -270,7 +270,9 @@
 		581516A92BCEEDF800BF67D7 /* NSPredicates.swift in Sources */ = {isa = PBXBuildFile; fileRef = 581516A82BCEEDF800BF67D7 /* NSPredicates.swift */; };
 		58237D9E2BCF0A6B00A47A79 /* PopupView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58237D9D2BCF0A6B00A47A79 /* PopupView.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 */; };
 		587DA1F62B77F3DD00B28F8A /* SettingsRowView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 587DA1F52B77F3DD00B28F8A /* SettingsRowView.swift */; };
+		58F107742BD1A4D000B1A680 /* Determination+helper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58F107732BD1A4D000B1A680 /* Determination+helper.swift */; };
 		5BFA1C2208114643B77F8CEB /* AddTempTargetProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = AEE53A13D26F101B332EFFC8 /* AddTempTargetProvider.swift */; };
 		5D16287A969E64D18CE40E44 /* PumpConfigStateModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3F60E97100041040446F44E7 /* PumpConfigStateModel.swift */; };
 		61962FCAF8A2D222553AC5A3 /* LibreConfigDataFlow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 66A5B83E7967C38F7CBD883C /* LibreConfigDataFlow.swift */; };
@@ -835,7 +837,9 @@
 		581516A82BCEEDF800BF67D7 /* NSPredicates.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NSPredicates.swift; sourceTree = "<group>"; };
 		58237D9D2BCF0A6B00A47A79 /* PopupView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PopupView.swift; sourceTree = "<group>"; };
 		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>"; };
 		587DA1F52B77F3DD00B28F8A /* SettingsRowView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsRowView.swift; sourceTree = "<group>"; };
+		58F107732BD1A4D000B1A680 /* Determination+helper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Determination+helper.swift"; sourceTree = "<group>"; };
 		5B8A42073A2D03A278914448 /* AddTempTargetDataFlow.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = AddTempTargetDataFlow.swift; sourceTree = "<group>"; };
 		5C018D1680307A31C9ED7120 /* CGMStateModel.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = CGMStateModel.swift; sourceTree = "<group>"; };
 		5D5B4F8B4194BB7E260EF251 /* ConfigEditorStateModel.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = ConfigEditorStateModel.swift; sourceTree = "<group>"; };
@@ -1732,6 +1736,7 @@
 				CC6C406D2ACDD69E009B8058 /* RawFetchedProfile.swift */,
 				CC41E2992B1E1F460070974F /* HistoryLayout.swift */,
 				BDF530D72B40F8AC002CAF43 /* LockScreenView.swift */,
+				583684072BD195A700070A60 /* Determination.swift */,
 			);
 			path = Models;
 			sourceTree = "<group>";
@@ -2049,10 +2054,11 @@
 			isa = PBXGroup;
 			children = (
 				FEFA5C10299F814A00765C17 /* CoreDataStack.swift */,
-				583684052BD178DB00070A60 /* GlucoseStored+helper.swift */,
 				1956FB202AFF79E200C7B4FF /* CoreDataStorage.swift */,
 				FEFA5C0D299F810B00765C17 /* Core_Data.xcdatamodeld */,
 				581516A82BCEEDF800BF67D7 /* NSPredicates.swift */,
+				583684052BD178DB00070A60 /* GlucoseStored+helper.swift */,
+				58F107732BD1A4D000B1A680 /* Determination+helper.swift */,
 			);
 			path = Model;
 			sourceTree = "<group>";
@@ -2855,6 +2861,7 @@
 				38E87408274F9AD000975559 /* UserNotificationsManager.swift in Sources */,
 				CE82E02528E867BA00473A9C /* AlertStorage.swift in Sources */,
 				38BF021D25E7E3AF00579895 /* Reservoir.swift in Sources */,
+				583684082BD195A700070A60 /* Determination.swift in Sources */,
 				38BF021B25E7D06400579895 /* PumpSettingsView.swift in Sources */,
 				3862CC05273D152B00BF832C /* CalibrationService.swift in Sources */,
 				3811DEEA25CA063400A708ED /* SyncAccess.swift in Sources */,
@@ -2949,6 +2956,7 @@
 				FEFA5C0F299F810B00765C17 /* Core_Data.xcdatamodeld in Sources */,
 				88AB39B23C9552BD6E0C9461 /* ISFEditorRootView.swift in Sources */,
 				F816825E28DB441200054060 /* HeartBeatManager.swift in Sources */,
+				58F107742BD1A4D000B1A680 /* Determination+helper.swift in Sources */,
 				38FEF413273B317A00574A46 /* HKUnit.swift in Sources */,
 				A33352ED40476125EBAC6EE0 /* CREditorDataFlow.swift in Sources */,
 				17A9D0899046B45E87834820 /* CREditorProvider.swift in Sources */,

A diferenza do arquivo foi suprimida porque é demasiado grande
+ 56 - 33
FreeAPS/Sources/APS/APSManager.swift


+ 62 - 27
FreeAPS/Sources/APS/OpenAPS/OpenAPS.swift

@@ -16,7 +16,7 @@ final class OpenAPS {
         self.storage = storage
     }
 
-    func determineBasal(currentTemp: TempBasal, clock: Date = Date()) -> Future<Suggestion?, Never> {
+    func determineBasal(currentTemp: TempBasal, clock: Date = Date()) -> Future<Determination?, Never> {
         Future { promise in
             self.processQueue.async {
                 debug(.openAPS, "Start determineBasal")
@@ -64,7 +64,7 @@ final class OpenAPS {
                 // oref2
                 let oref2_variables = self.oref2()
 
-                let suggested = self.determineBasal(
+                let orefDetermination = self.determineBasal(
                     glucose: glucose,
                     currentTemp: tempBasal,
                     iob: iob,
@@ -78,54 +78,89 @@ final class OpenAPS {
                     basalProfile: basalProfile,
                     oref2_variables: oref2_variables
                 )
-                debug(.openAPS, "SUGGESTED: \(suggested)")
+                debug(.openAPS, "SUGGESTED: \(orefDetermination)")
 
-                if var suggestion = Suggestion(from: suggested) {
-                    suggestion.timestamp = suggestion.deliverAt ?? clock
-                    self.storage.save(suggestion, as: Enact.suggested)
+                if var determination = Determination(from: orefDetermination) {
+                    determination.timestamp = determination.deliverAt ?? clock
+                    self.storage.save(determination, as: Enact.suggested)
 
                     // save to core data asynchronously
                     self.coredataContext.perform {
-                        let new = Determination(context: self.coredataContext)
-                        new.cob = suggestion.cob as? NSDecimalNumber
-                        new.iob = suggestion.iob as? NSDecimalNumber
-                        new.deliverAt = suggestion.deliverAt
-                        new.glucose = suggestion.bg as? NSDecimalNumber
+                        let new = OrefDetermination(context: self.coredataContext)
+                        new.totalDailyDose = determination.tdd as? NSDecimalNumber
+                        new.insulinSensitivity = determination.isf as? NSDecimalNumber
+                        new.currentTarget = determination.current_target as? NSDecimalNumber
+                        new.eventualBG = determination.eventualBG as? NSDecimalNumber
+                        new.deliverAt = determination.deliverAt
+                        new.enacted = false
+                        new.insulinForManualBolus = determination.insulinForManualBolus as? NSDecimalNumber
+                        new.carbRatio = determination.carbRatio as? NSDecimalNumber
+                        new.glucose = determination.bg as? NSDecimalNumber
+                        new.reservoir = determination.reservoir as? NSDecimalNumber
+                        new.insulinReq = determination.insulinReq as? NSDecimalNumber
+                        new.temp = determination.temp?.rawValue ?? "absolute"
+                        new.rate = determination.rate as? NSDecimalNumber
+                        new.reason = determination.reason
+                        new.duration = Int16(determination.duration ?? 0)
+                        new.iob = determination.iob as? NSDecimalNumber
+                        new.treshold = determination.treshold as? NSDecimalNumber
+//                                                new.predBGs: [
+//                                                "zt": immutableDetermination.predictions?.zt ?? [],
+//                                                "cob": immutableDetermination.predictions?.cob ?? [],
+//                                                "iob": immutableDetermination.predictions?.iob ?? [],
+//                                                "uam": immutableDetermination.predictions?.uam ?? []
+//                                            ]
+                        new.minDelta = determination.minDelta as? NSDecimalNumber
+                        new.sensitivityRatio = determination.sensitivityRatio as? NSDecimalNumber
+                        new.expectedDelta = determination.expectedDelta as? NSDecimalNumber
+                        new.cob = Int16(Int(determination.cob ?? 0))
+                        new.manualBolusErrorString = determination.manualBolusErrorString as? NSDecimalNumber
+                        new.timestampEnacted = nil
+                        new.tempBasal = determination.insulin?.temp_basal as? NSDecimalNumber
+                        new.scheduledBasal = determination.insulin?.scheduled_basal as? NSDecimalNumber
+                        new.bolus = determination.insulin?.bolus as? NSDecimalNumber
+                        new.smbToDeliver = determination.units as? NSDecimalNumber
+                        new.carbsRequired = Int16(Int(determination.carbsReq ?? 0))
                         do {
                             try self.coredataContext.save()
+                            debugPrint(
+                                "OpenAPS: \(CoreDataStack.identifier) \(DebuggingIdentifiers.succeeded) saved determination"
+                            )
                         } catch {
-                            print("failed")
+                            debugPrint(
+                                "OpenAPS: \(CoreDataStack.identifier) \(DebuggingIdentifiers.failed) error while saving determination"
+                            )
                         }
                     }
 
                     // MARK: Save to CoreData also. To do: Remove JSON saving
 
-                    if suggestion.tdd ?? 0 > 0 {
+                    if determination.tdd ?? 0 > 0 {
                         self.coredataContext.perform {
                             let saveToTDD = TDD(context: self.coredataContext)
 
-                            saveToTDD.timestamp = suggestion.timestamp ?? Date()
-                            saveToTDD.tdd = (suggestion.tdd ?? 0) as NSDecimalNumber?
+                            saveToTDD.timestamp = determination.timestamp ?? Date()
+                            saveToTDD.tdd = (determination.tdd ?? 0) as NSDecimalNumber?
                             try? self.coredataContext.save()
 
                             let saveTarget = Target(context: self.coredataContext)
-                            saveTarget.current = (suggestion.current_target ?? 100) as NSDecimalNumber?
+                            saveTarget.current = (determination.current_target ?? 100) as NSDecimalNumber?
                             try? self.coredataContext.save()
                         }
 
-                        self.coredataContext.perform {
-                            let saveToInsulin = InsulinDistribution(context: self.coredataContext)
-
-                            saveToInsulin.bolus = (suggestion.insulin?.bolus ?? 0) as NSDecimalNumber?
-                            saveToInsulin.scheduledBasal = (suggestion.insulin?.scheduled_basal ?? 0) as NSDecimalNumber?
-                            saveToInsulin.tempBasal = (suggestion.insulin?.temp_basal ?? 0) as NSDecimalNumber?
-                            saveToInsulin.date = Date()
-
-                            try? self.coredataContext.save()
-                        }
+//                        self.coredataContext.perform {
+//                            let saveToInsulin = InsulinDistribution(context: self.coredataContext)
+//
+//                            saveToInsulin.bolus = (determination.insulin?.bolus ?? 0) as NSDecimalNumber?
+//                            saveToInsulin.scheduledBasal = (determination.insulin?.scheduled_basal ?? 0) as NSDecimalNumber?
+//                            saveToInsulin.tempBasal = (determination.insulin?.temp_basal ?? 0) as NSDecimalNumber?
+//                            saveToInsulin.date = Date()
+//
+//                            try? self.coredataContext.save()
+//                        }
                     }
 
-                    promise(.success(suggestion))
+                    promise(.success(determination))
                 } else {
                     promise(.success(nil))
                 }

+ 14 - 0
FreeAPS/Sources/APS/Storage/PumpHistoryStorage.swift

@@ -26,6 +26,8 @@ final class BasePumpHistoryStorage: PumpHistoryStorage, Injectable {
         injectServices(resolver)
     }
 
+    private let context = CoreDataStack.shared.persistentContainer.viewContext
+
     func storePumpEvents(_ events: [NewPumpEvent]) {
         processQueue.async {
             let eventsToStore = events.flatMap { event -> [PumpHistoryEvent] in
@@ -35,6 +37,18 @@ final class BasePumpHistoryStorage: PumpHistoryStorage, Injectable {
                     guard let dose = event.dose else { return [] }
                     let amount = Decimal(string: dose.unitsInDeliverableIncrements.description)
                     let minutes = Int((dose.endDate - dose.startDate).timeInterval / 60)
+
+                    // MARK: - save to Core Data
+
+                    self.context.perform {
+                        let new = InsulinStored(context: self.context)
+                        new.amount = amount as? NSDecimalNumber
+                        new.date = Date()
+                        new.external = false
+                        new.id = UUID()
+                        new.isSMB = true
+                    }
+
                     return [PumpHistoryEvent(
                         id: id,
                         type: .bolus,

+ 86 - 0
FreeAPS/Sources/Models/Determination.swift

@@ -0,0 +1,86 @@
+import Foundation
+
+struct Determination: JSON, Equatable {
+    let reason: String
+    let units: Decimal?
+    let insulinReq: Decimal?
+    let eventualBG: Int?
+    let sensitivityRatio: Decimal?
+    let rate: Decimal?
+    let duration: Int?
+    let iob: Decimal?
+    let cob: Decimal?
+    var predictions: Predictions?
+    let deliverAt: Date?
+    let carbsReq: Decimal?
+    let temp: TempType?
+    let bg: Decimal?
+    let reservoir: Decimal?
+    let isf: Decimal?
+    var timestamp: Date?
+    var recieved: Bool?
+    let tdd: Decimal?
+    let insulin: Insulin?
+    let current_target: Decimal?
+    let insulinForManualBolus: Decimal?
+    let manualBolusErrorString: Decimal?
+    let minDelta: Decimal?
+    let expectedDelta: Decimal?
+    let minGuardBG: Decimal?
+    let minPredBG: Decimal?
+    let threshold: Decimal?
+    let carbRatio: Decimal?
+}
+
+extension Determination {
+    private enum CodingKeys: String, CodingKey {
+        case reason
+        case units
+        case insulinReq
+        case eventualBG
+        case sensitivityRatio
+        case rate
+        case duration
+        case iob = "IOB"
+        case cob = "COB"
+        case predictions = "predBGs"
+        case deliverAt
+        case carbsReq
+        case temp
+        case bg
+        case reservoir
+        case timestamp
+        case recieved
+        case isf = "ISF"
+        case tdd = "TDD"
+        case insulin
+        case current_target
+        case insulinForManualBolus
+        case manualBolusErrorString
+        case minDelta
+        case expectedDelta
+        case minGuardBG
+        case minPredBG
+        case threshold
+        case carbRatio = "CR"
+    }
+}
+
+protocol DeterminationObserver {
+    func determinationDidUpdate(_ determination: Determination)
+}
+
+// needed?
+protocol EnactedDeterminationObserver {
+    func enactedSDeterminationDidUpdate(_ determination: Determination)
+}
+
+extension Determination {
+    var reasonParts: [String] {
+        reason.components(separatedBy: "; ").first?.components(separatedBy: ", ") ?? []
+    }
+
+    var reasonConclusion: String {
+        reason.components(separatedBy: "; ").last ?? ""
+    }
+}

+ 0 - 28
FreeAPS/Sources/Modules/Home/HomeStateModel.swift

@@ -105,7 +105,6 @@ extension Home {
             carbsRequired = suggestion?.carbsReq
             alarm = provider.glucoseStorage.alarm
             manualTempBasal = apsManager.isManualTempBasal
-            setStatusTitle()
             setupCurrentTempTarget()
             smooth = settingsManager.settings.smoothGlucose
             maxValue = settingsManager.preferences.autosensMax
@@ -382,31 +381,6 @@ extension Home {
             }
         }
 
-        private func setStatusTitle() {
-            guard let suggestion = suggestion else {
-                statusTitle = "No suggestion"
-                return
-            }
-
-            let dateFormatter = DateFormatter()
-            dateFormatter.timeStyle = .short
-            if closedLoop,
-               let enactedSuggestion = enactedSuggestion,
-               let timestamp = enactedSuggestion.timestamp,
-               enactedSuggestion.deliverAt == suggestion.deliverAt, enactedSuggestion.recieved == true
-            {
-                statusTitle = NSLocalizedString("Enacted at", comment: "Headline in enacted pop up") + " " + dateFormatter
-                    .string(from: timestamp)
-            } else if let suggestedDate = suggestion.deliverAt {
-                statusTitle = NSLocalizedString("Suggested at", comment: "Headline in suggested pop up") + " " + dateFormatter
-                    .string(from: suggestedDate)
-            } else {
-                statusTitle = "Suggested"
-            }
-
-            eventualBG = suggestion.eventualBG
-        }
-
         private func setupReservoir() {
             DispatchQueue.main.async { [weak self] in
                 guard let self = self else { return }
@@ -479,7 +453,6 @@ extension Home.StateModel:
     func suggestionDidUpdate(_ suggestion: Suggestion) {
         self.suggestion = suggestion
         carbsRequired = suggestion.carbsReq
-        setStatusTitle()
         waitForSuggestion = false
     }
 
@@ -529,7 +502,6 @@ extension Home.StateModel:
 
     func enactedSuggestionDidUpdate(_ suggestion: Suggestion) {
         enactedSuggestion = suggestion
-        setStatusTitle()
     }
 
     func pumpBatteryDidChange(_: Battery) {

+ 10 - 13
FreeAPS/Sources/Modules/Home/View/Header/LoopView.swift

@@ -8,8 +8,13 @@ struct LoopView: View {
         static let lag: TimeInterval = 30
     }
 
-    @Binding var suggestion: Suggestion?
-    @Binding var enactedSuggestion: Suggestion?
+    // TODO: -fetch only enacted determinations
+
+    @FetchRequest(
+        fetchRequest: OrefDetermination
+            .fetch(NSPredicate.predicateFor30MinAgoForDetermination)
+    ) var determination: FetchedResults<OrefDetermination>
+
     @Binding var closedLoop: Bool
     @Binding var timerDate: Date
     @Binding var isLooping: Bool
@@ -37,7 +42,7 @@ struct LoopView: View {
                 Text("looping")
             } else if manualTempBasal {
                 Text("Manual")
-            } else if actualSuggestion?.timestamp != nil {
+            } else if determination.first?.timestamp != nil {
                 Text(timeString)
             } else {
                 Text("--")
@@ -57,7 +62,7 @@ struct LoopView: View {
     }
 
     private var color: Color {
-        guard actualSuggestion?.timestamp != nil else {
+        guard determination.first?.timestamp != nil else {
             return .secondary
         }
         guard manualTempBasal == false else {
@@ -70,7 +75,7 @@ struct LoopView: View {
         let delta = timerDate.timeIntervalSince(lastLoopDate) - Config.lag
 
         if delta <= 5.minutes.timeInterval {
-            guard actualSuggestion?.deliverAt != nil else {
+            guard determination.first?.deliverAt != nil else {
                 return .loopYellow
             }
             return .loopGreen
@@ -88,14 +93,6 @@ struct LoopView: View {
         }
         return path
     }
-
-    private var actualSuggestion: Suggestion? {
-        if closedLoop, enactedSuggestion?.recieved == true {
-            return enactedSuggestion ?? suggestion
-        } else {
-            return suggestion
-        }
-    }
 }
 
 extension View {

+ 34 - 12
FreeAPS/Sources/Modules/Home/View/HomeRootView.swift

@@ -14,6 +14,7 @@ extension Home {
         @State var isMenuPresented = false
         @State var showTreatments = false
         @State var selectedTab: Int = 0
+        @State private var statusTitle: String = ""
 
         struct Buttons: Identifiable {
             let label: String
@@ -42,9 +43,11 @@ extension Home {
         ) var fetchedPercent: FetchedResults<Override>
 
         @FetchRequest(
-            entity: Determination.entity(),
-            sortDescriptors: [NSSortDescriptor(key: "deliverAt", ascending: false)]
-        ) var determination: FetchedResults<Determination>
+            entity: OrefDetermination.entity(),
+            sortDescriptors: [NSSortDescriptor(key: "deliverAt", ascending: false)],
+            predicate: NSPredicate.predicateFor30MinAgoForDetermination,
+            animation: Animation.bouncy
+        ) var determination: FetchedResults<OrefDetermination>
 
         @FetchRequest(
             entity: OverridePresets.entity(),
@@ -430,8 +433,6 @@ extension Home {
             VStack(alignment: .leading, spacing: 20) {
                 /// Loop view at bottomLeading
                 LoopView(
-                    suggestion: $state.suggestion,
-                    enactedSuggestion: $state.enactedSuggestion,
                     closedLoop: $state.closedLoop,
                     timerDate: $state.timerDate,
                     isLooping: $state.isLooping,
@@ -439,6 +440,7 @@ extension Home {
                     manualTempBasal: $state.manualTempBasal
                 ).onTapGesture {
                     state.isStatusPopupPresented = true
+                    setStatusTitle()
                 }.onLongPressGesture {
                     let impactHeavy = UIImpactFeedbackGenerator(style: .heavy)
                     impactHeavy.impactOccurred()
@@ -506,7 +508,7 @@ extension Home {
                 if !state.tins {
                     Spacer()
                     Text(
-                        "TDD: " + (numberFormatter.string(from: (state.suggestion?.tdd ?? 0) as NSNumber) ?? "0") +
+                        "TDD: " + (numberFormatter.string(from: (determination.first?.totalDailyDose ?? 0) as NSNumber) ?? "0") +
                             NSLocalizedString(" U", comment: "Insulin unit")
                     )
                     .font(.system(size: 16, weight: .bold, design: .rounded))
@@ -847,15 +849,15 @@ extension Home {
 
         private var popup: some View {
             VStack(alignment: .leading, spacing: 4) {
-                Text(state.statusTitle).font(.headline).foregroundColor(.white)
+                Text(statusTitle).font(.headline).foregroundColor(.white)
                     .padding(.bottom, 4)
-                if let suggestion = state.suggestion {
-                    TagCloudView(tags: suggestion.reasonParts).animation(.none, value: false)
+                if let determination = determination.first {
+                    TagCloudView(tags: determination.reasonParts).animation(.none, value: false)
 
-                    Text(suggestion.reasonConclusion.capitalizingFirstLetter()).font(.caption).foregroundColor(.white)
+                    Text(determination.reasonConclusion.capitalizingFirstLetter()).font(.caption).foregroundColor(.white)
 
                 } else {
-                    Text("No sugestion found").font(.body).foregroundColor(.white)
+                    Text("No determination found").font(.body).foregroundColor(.white)
                 }
 
                 if let errorMessage = state.errorMessage, let date = state.errorDate {
@@ -865,11 +867,31 @@ extension Home {
                         .padding(.bottom, 4)
                         .padding(.top, 8)
                     Text(errorMessage).font(.caption).foregroundColor(.loopRed)
-                } else if let suggestion = state.suggestion, (suggestion.bg ?? 100) == 400 {
+                } else if let determination = determination.first, (determination.glucose ?? 100) == 400 {
                     Text("Invalid CGM reading (HIGH).").font(.callout).bold().foregroundColor(.loopRed).padding(.top, 8)
                     Text("SMBs and High Temps Disabled.").font(.caption).foregroundColor(.white).padding(.bottom, 4)
                 }
             }
         }
+
+        private func setStatusTitle() {
+            guard let determination = determination.first else {
+                statusTitle = "No Oref determination"
+                return
+            }
+
+            let dateFormatter = DateFormatter()
+            dateFormatter.timeStyle = .short
+            if state.closedLoop
+            {
+                statusTitle = NSLocalizedString("Oref Determination enacted at", comment: "Headline in enacted pop up") + " " +
+                    dateFormatter
+                    .string(from: determination.timestamp ?? Date())
+            } else {
+                statusTitle = NSLocalizedString("Determinated at", comment: "Headline in suggested pop up") + " " +
+                    dateFormatter
+                    .string(from: determination.deliverAt ?? Date())
+            }
+        }
     }
 }

+ 0 - 14
Model/CoreDataStorage.swift

@@ -6,20 +6,6 @@ import Swinject
 final class CoreDataStorage {
     let coredataContext = CoreDataStack.shared.persistentContainer.viewContext
 
-    func fetchGlucose(interval: NSDate) -> [Readings] {
-        var fetchGlucose = [Readings]()
-        coredataContext.performAndWait {
-            let requestReadings = Readings.fetchRequest() as NSFetchRequest<Readings>
-            let sort = NSSortDescriptor(key: "date", ascending: false)
-            requestReadings.sortDescriptors = [sort]
-            requestReadings.predicate = NSPredicate(
-                format: "glucose > 0 AND date > %@", interval
-            )
-            try? fetchGlucose = self.coredataContext.fetch(requestReadings)
-        }
-        return fetchGlucose
-    }
-
     func fetchLatestOverride() -> [Override] {
         var overrideArray = [Override]()
         coredataContext.performAndWait {

+ 32 - 7
Model/Core_Data.xcdatamodeld/Core_Data.xcdatamodel/contents

@@ -30,13 +30,6 @@
         <attribute name="date" optional="YES" attributeType="Date" usesScalarValueType="NO"/>
         <attribute name="enteredBy" optional="YES" attributeType="String"/>
     </entity>
-    <entity name="Determination" representedClassName="Determination" syncable="YES" codeGenerationType="class">
-        <attribute name="cob" optional="YES" attributeType="Decimal" defaultValueString="0"/>
-        <attribute name="deliverAt" optional="YES" attributeType="Date" usesScalarValueType="NO"/>
-        <attribute name="enacted" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
-        <attribute name="glucose" optional="YES" attributeType="Decimal" defaultValueString="0.0"/>
-        <attribute name="iob" optional="YES" attributeType="Decimal" defaultValueString="0.0"/>
-    </entity>
     <entity name="Fat" representedClassName="Fat" syncable="YES" codeGenerationType="class">
         <attribute name="date" optional="YES" attributeType="Date" usesScalarValueType="NO"/>
         <attribute name="enteredBy" optional="YES" attributeType="String"/>
@@ -108,6 +101,38 @@
         <relationship name="computedInsulinDistribution" optional="YES" toMany="YES" deletionRule="Nullify" destinationEntity="InsulinDistribution" inverseName="insulin" inverseEntity="InsulinDistribution"/>
         <relationship name="computedTDD" optional="YES" toMany="YES" deletionRule="Nullify" destinationEntity="TDD" inverseName="computed" inverseEntity="TDD"/>
     </entity>
+    <entity name="OrefDetermination" representedClassName="OrefDetermination" syncable="YES" codeGenerationType="class">
+        <attribute name="bolus" optional="YES" attributeType="Decimal" defaultValueString="0.0"/>
+        <attribute name="carbRatio" optional="YES" attributeType="Decimal" defaultValueString="0.0"/>
+        <attribute name="carbsRequired" optional="YES" attributeType="Integer 16" defaultValueString="0" usesScalarValueType="YES"/>
+        <attribute name="cob" optional="YES" attributeType="Integer 16" defaultValueString="0" usesScalarValueType="YES"/>
+        <attribute name="currentTarget" optional="YES" attributeType="Decimal" defaultValueString="0.0"/>
+        <attribute name="deliverAt" optional="YES" attributeType="Date" usesScalarValueType="NO"/>
+        <attribute name="duration" optional="YES" attributeType="Integer 16" defaultValueString="0" usesScalarValueType="YES"/>
+        <attribute name="enacted" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
+        <attribute name="eventualBG" optional="YES" attributeType="Decimal" defaultValueString="0"/>
+        <attribute name="expectedDelta" optional="YES" attributeType="Decimal" defaultValueString="0.0"/>
+        <attribute name="glucose" optional="YES" attributeType="Decimal" defaultValueString="0.0"/>
+        <attribute name="insulinForManualBolus" optional="YES" attributeType="Decimal" defaultValueString="0.0"/>
+        <attribute name="insulinReq" optional="YES" attributeType="Decimal" defaultValueString="0.0"/>
+        <attribute name="insulinSensitivity" optional="YES" attributeType="Decimal" defaultValueString="0.0"/>
+        <attribute name="iob" optional="YES" attributeType="Decimal" defaultValueString="0.0"/>
+        <attribute name="manualBolusErrorString" optional="YES" attributeType="Decimal" defaultValueString="0.0"/>
+        <attribute name="minDelta" optional="YES" attributeType="Decimal" defaultValueString="0.0"/>
+        <attribute name="rate" optional="YES" attributeType="Decimal" defaultValueString="0.0"/>
+        <attribute name="reason" optional="YES" attributeType="String"/>
+        <attribute name="received" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
+        <attribute name="reservoir" optional="YES" attributeType="Decimal" defaultValueString="0.0"/>
+        <attribute name="scheduledBasal" optional="YES" attributeType="Decimal" defaultValueString="0.0"/>
+        <attribute name="sensitivityRatio" optional="YES" attributeType="Decimal" defaultValueString="0.0"/>
+        <attribute name="smbToDeliver" optional="YES" attributeType="Decimal" defaultValueString="0.0"/>
+        <attribute name="temp" optional="YES" attributeType="String"/>
+        <attribute name="tempBasal" optional="YES" attributeType="Decimal" defaultValueString="0.0"/>
+        <attribute name="timestamp" optional="YES" attributeType="Date" usesScalarValueType="NO"/>
+        <attribute name="timestampEnacted" optional="YES" attributeType="Date" usesScalarValueType="NO"/>
+        <attribute name="totalDailyDose" optional="YES" attributeType="Decimal" defaultValueString="0.0"/>
+        <attribute name="treshold" optional="YES" attributeType="Decimal" defaultValueString="0.0"/>
+    </entity>
     <entity name="Override" representedClassName="Override" syncable="YES" codeGenerationType="class">
         <attribute name="advancedSettings" optional="YES" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
         <attribute name="cr" optional="YES" attributeType="Boolean" defaultValueString="YES" usesScalarValueType="YES"/>

+ 22 - 0
Model/Determination+helper.swift

@@ -0,0 +1,22 @@
+import CoreData
+import Foundation
+
+extension OrefDetermination {
+    static func fetch(_ predicate: NSPredicate = .all) -> NSFetchRequest<OrefDetermination> {
+        let request = OrefDetermination.fetchRequest()
+        request.sortDescriptors = [NSSortDescriptor(keyPath: \OrefDetermination.deliverAt, ascending: false)]
+        request.predicate = predicate
+        request.fetchLimit = 1
+        return request
+    }
+}
+
+extension OrefDetermination {
+    var reasonParts: [String] {
+        reason?.components(separatedBy: "; ").first?.components(separatedBy: ", ") ?? []
+    }
+
+    var reasonConclusion: String {
+        reason?.components(separatedBy: "; ").last ?? ""
+    }
+}

+ 5 - 0
Model/NSPredicates.swift

@@ -46,6 +46,11 @@ extension NSPredicate {
         return NSPredicate(format: "date >= %@", date as NSDate)
     }
 
+    static var predicateFor30MinAgoForDetermination: NSPredicate {
+        let date = Date.halfHourAgo
+        return NSPredicate(format: "deliverAt >= %@", date as NSDate)
+    }
+
     static var predicateFor120MinAgo: NSPredicate {
         let date = Date.twoHoursAgo
         return NSPredicate(format: "date >= %@", date as NSDate)