فهرست منبع

Temp Target Exercise and Pre Meal Slider updates

Jon Mårtensson 3 سال پیش
والد
کامیت
16a1948951

+ 8 - 0
Core_Data.xcdatamodeld/Core_Data.xcdatamodel/contents

@@ -21,6 +21,7 @@
         <attribute name="date" optional="YES" attributeType="Date" usesScalarValueType="NO"/>
         <attribute name="enteredBy" optional="YES" attributeType="String"/>
     </entity>
+    <entity name="Entity" representedClassName="Entity" syncable="YES" codeGenerationType="class"/>
     <entity name="HbA1c" representedClassName="HbA1c" syncable="YES" codeGenerationType="class">
         <attribute name="date" optional="YES" attributeType="Date" usesScalarValueType="NO"/>
         <attribute name="hba1c" optional="YES" attributeType="Decimal" defaultValueString="0.0"/>
@@ -56,9 +57,16 @@
         <attribute name="date" optional="YES" attributeType="Date" usesScalarValueType="NO"/>
         <attribute name="glucose" optional="YES" attributeType="Integer 16" defaultValueString="0" usesScalarValueType="YES"/>
     </entity>
+    <entity name="Target" representedClassName="Target" syncable="YES" codeGenerationType="class">
+        <attribute name="current" optional="YES" attributeType="Decimal" defaultValueString="100"/>
+    </entity>
     <entity name="TDD" representedClassName="TDD" syncable="YES" codeGenerationType="class">
         <attribute name="tdd" optional="YES" attributeType="Decimal" defaultValueString="0.0"/>
         <attribute name="timestamp" optional="YES" attributeType="Date" usesScalarValueType="NO"/>
         <relationship name="computed" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="Oref0Suggestion" inverseName="computedTDD" inverseEntity="Oref0Suggestion"/>
     </entity>
+    <entity name="ViewPercentage" representedClassName="ViewPercentage" syncable="YES" codeGenerationType="class">
+        <attribute name="date" optional="YES" attributeType="Date" usesScalarValueType="NO"/>
+        <attribute name="enabled" optional="YES" attributeType="Boolean" defaultValueString="100" usesScalarValueType="YES"/>
+    </entity>
 </model>

تفاوت فایلی نمایش داده نمی شود زیرا این فایل بسیار بزرگ است
+ 1 - 1
FreeAPS/Resources/javascript/bundle/determine-basal.js


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

@@ -725,23 +725,27 @@ final class BaseAPSManager: APSManager, Injectable {
             var indeces: Int = 0
             var nrOfIndeces: Int = 0
 
+            var booleanArray = [ViewPercentage]()
+            var isPercentageEnabled = false
+
             coredataContext.performAndWait {
                 let requestTDD = TDD.fetchRequest() as NSFetchRequest<TDD>
-
                 requestTDD.predicate = NSPredicate(format: "timestamp > %@ AND tdd > 0", tenDaysAgo as NSDate)
-
                 let sortTDD = NSSortDescriptor(key: "timestamp", ascending: true)
                 requestTDD.sortDescriptors = [sortTDD]
-
                 try? uniqEvents = coredataContext.fetch(requestTDD)
 
+                let requestIsEnbled = ViewPercentage.fetchRequest() as NSFetchRequest<ViewPercentage>
+                let sortIsEnabled = NSSortDescriptor(key: "date", ascending: false)
+                requestIsEnbled.sortDescriptors = [sortIsEnabled]
+                requestIsEnbled.fetchLimit = 1
+                try? booleanArray = coredataContext.fetch(requestIsEnbled)
+
                 total = uniqEvents.compactMap({ each in each.tdd as? Decimal ?? 0 }).reduce(0, +)
                 indeces = uniqEvents.count
-
                 // Only fetch once. Use same (previous) fetch
                 let twoHoursArray = uniqEvents.filter({ ($0.timestamp ?? Date()) >= twoHoursAgo })
                 nrOfIndeces = twoHoursArray.count
-
                 totalAmount = twoHoursArray.compactMap({ each in each.tdd as? Decimal ?? 0 }).reduce(0, +)
             }
 
@@ -754,14 +758,19 @@ final class BaseAPSManager: APSManager, Injectable {
 
             let average2hours = totalAmount / Decimal(nrOfIndeces)
             let average14 = total / Decimal(indeces)
-
             let weight = preferences.weightPercentage
             let weighted_average = weight * average2hours + (1 - weight) * average14
+
+            if !booleanArray.isEmpty {
+                isPercentageEnabled = booleanArray[0].enabled
+            }
+
             let averages = TDD_averages(
                 average_total_data: roundDecimal(average14, 1),
                 weightedAverage: roundDecimal(weighted_average, 1),
                 past2hoursAverage: roundDecimal(average2hours, 1),
-                date: Date()
+                date: Date(),
+                isEnabled: isPercentageEnabled
             )
             storage.save(averages, as: OpenAPS.Monitor.tdd_averages)
 

+ 3 - 0
FreeAPS/Sources/APS/OpenAPS/OpenAPS.swift

@@ -90,7 +90,10 @@ final class OpenAPS {
 
                             saveToTDD.timestamp = suggestion.timestamp ?? Date()
                             saveToTDD.tdd = (suggestion.tdd ?? 0) as NSDecimalNumber?
+                            try? self.coredataContext.save()
 
+                            let saveTarget = Target(context: self.coredataContext)
+                            saveTarget.current = (suggestion.current_target ?? 100) as NSDecimalNumber?
                             try? self.coredataContext.save()
                         }
 

+ 2 - 0
FreeAPS/Sources/Models/Suggestion.swift

@@ -21,6 +21,7 @@ struct Suggestion: JSON, Equatable {
     var recieved: Bool?
     let tdd: Decimal?
     let insulin: Insulin?
+    let current_target: Decimal?
 }
 
 struct Predictions: JSON, Equatable {
@@ -59,6 +60,7 @@ extension Suggestion {
         case isf = "ISF"
         case tdd = "TDD"
         case insulin
+        case current_target
     }
 }
 

+ 5 - 1
FreeAPS/Sources/Models/TDD_averages.swift

@@ -5,17 +5,20 @@ struct TDD_averages: JSON, Equatable {
     var weightedAverage: Decimal
     var past2hoursAverage: Decimal
     var date: Date
+    var isEnabled: Bool
 
     init(
         average_total_data: Decimal,
         weightedAverage: Decimal,
         past2hoursAverage: Decimal,
-        date: Date
+        date: Date,
+        isEnabled: Bool
     ) {
         self.average_total_data = average_total_data
         self.weightedAverage = weightedAverage
         self.past2hoursAverage = past2hoursAverage
         self.date = date
+        self.isEnabled = isEnabled
     }
 }
 
@@ -25,5 +28,6 @@ extension TDD_averages {
         case weightedAverage
         case past2hoursAverage
         case date
+        case isEnabled
     }
 }

+ 1 - 1
FreeAPS/Sources/Modules/AddCarbs/AddCarbsStateModel.swift

@@ -15,7 +15,7 @@ extension AddCarbs {
         @Published var dish: String = ""
         @Published var selection: Presets?
 
-        let coredataContext = CoreDataStack.shared.persistentContainer.viewContext // .newBackgroundContext()
+        let coredataContext = CoreDataStack.shared.persistentContainer.viewContext
         @Environment(\.managedObjectContext) var moc
 
         override func subscribe() {

+ 38 - 18
FreeAPS/Sources/Modules/AddTempTarget/AddTempTargetStateModel.swift

@@ -1,3 +1,4 @@
+import CoreData
 import SwiftUI
 
 extension AddTempTarget {
@@ -14,6 +15,7 @@ extension AddTempTarget {
         @Published var percentage = 100.0
         @Published var maxValue: Decimal = 1.2
         @Published var halfBasal: Decimal = 160
+        @Published var viewPercantage = false
 
         private(set) var units: GlucoseUnits = .mmolL
 
@@ -25,16 +27,26 @@ extension AddTempTarget {
         }
 
         func enact() {
-            let diff = Double(halfBasal - 100)
-            let multiplier = percentage - (diff * (percentage / 100))
-            let ratio = min(Decimal(percentage / 100), maxValue)
-            var target = Decimal(diff + multiplier) / ratio
+            var lowTarget = low
 
-            if (halfBasal + (halfBasal + target - 100)) <= 0 {
-                target = (halfBasal - 100 + (halfBasal - 100) * maxValue) / maxValue
+            if viewPercantage {
+                var ratio = Decimal(percentage / 100)
+                let hB = halfBasal
+                let c = hB - 100
+                var target = (c / ratio) - c + 100
+
+                if c * (c + target - 100) <= 0 {
+                    ratio = maxValue
+                    target = (c / ratio) - c + 100
+                }
+                lowTarget = target
+            }
+            var highTarget = lowTarget
+
+            if units == .mmolL {
+                lowTarget = lowTarget.asMgdL
+                highTarget = highTarget.asMgdL
             }
-            let lowTarget = target
-            let highTarget = lowTarget
 
             let entry = TempTarget(
                 name: TempTarget.custom,
@@ -46,7 +58,6 @@ extension AddTempTarget {
                 reason: TempTarget.custom
             )
             storage.storeTempTargets([entry])
-
             showModal(for: nil)
         }
 
@@ -56,16 +67,26 @@ extension AddTempTarget {
         }
 
         func save() {
-            let diff = Double(halfBasal - 100)
-            let multiplier = percentage - (diff * (percentage / 100))
-            let ratio = min(Decimal(percentage / 100), maxValue)
-            var target = Decimal(diff + multiplier) / ratio
+            var lowTarget = low
 
-            if (halfBasal + (halfBasal + target - 100)) <= 0 {
-                target = (halfBasal - 100 + (halfBasal - 100) * maxValue) / maxValue
+            if viewPercantage {
+                var ratio = Decimal(percentage / 100)
+                let hB = halfBasal
+                let c = hB - 100
+                var target = (c / ratio) - c + 100
+
+                if c * (c + target - 100) <= 0 {
+                    ratio = maxValue
+                    target = (c / ratio) - c + 100
+                }
+                lowTarget = target
+            }
+            var highTarget = lowTarget
+
+            if units == .mmolL, !viewPercantage {
+                lowTarget = lowTarget.asMgdL
+                highTarget = highTarget.asMgdL
             }
-            let lowTarget = target
-            let highTarget = lowTarget
 
             let entry = TempTarget(
                 name: newPresetName.isEmpty ? TempTarget.custom : newPresetName,
@@ -76,7 +97,6 @@ extension AddTempTarget {
                 enteredBy: TempTarget.manual,
                 reason: newPresetName.isEmpty ? TempTarget.custom : newPresetName
             )
-
             presets.append(entry)
             storage.storePresets(presets)
         }

+ 97 - 50
FreeAPS/Sources/Modules/AddTempTarget/View/AddTempTargetRootView.swift

@@ -1,3 +1,4 @@
+import CoreData
 import SwiftUI
 import Swinject
 
@@ -10,6 +11,13 @@ extension AddTempTarget {
         @State private var removeAlert: Alert?
         @State private var isEditing = false
 
+        @FetchRequest(
+            entity: ViewPercentage.entity(),
+            sortDescriptors: [NSSortDescriptor(key: "enabled", ascending: false)]
+        ) var isEnabledArray: FetchedResults<ViewPercentage>
+
+        @Environment(\.managedObjectContext) var moc
+
         private var formatter: NumberFormatter {
             let formatter = NumberFormatter()
             formatter.numberStyle = .decimal
@@ -27,54 +35,79 @@ extension AddTempTarget {
                     }
                 }
 
-                Section(
-                    header: Text("Basal Insulin and Sensitivity ratio"),
-                    footer: Text(
-                        NSLocalizedString(
-                            "A lower 'Half Basal Target' setting will reduce the basal and raise the ISF earlier, at a lower target glucose.",
-                            comment: ""
-                        ) +
-                            NSLocalizedString(" Your setting: ", comment: "") + "\(state.halfBasal) " +
-                            NSLocalizedString("mg/dl. Autosens.max limits the max endpoint", comment: "") +
-                            " (\(state.maxValue * 100) %)"
-                    )
-                ) {
-                    VStack {
-                        Slider(
-                            value: $state.percentage,
-                            in: 15 ...
-                                Double(state.maxValue * 100),
-                            step: 1,
-                            onEditingChanged: { editing in
-                                isEditing = editing
-                            }
+                Toggle(isOn: $state.viewPercantage) {
+                    Text("Exercise / Pre Meal Slider")
+                }
+
+                if state.viewPercantage {
+                    Section(
+                        header: Text("TT Effect on Basal and Sensitivity"),
+                        footer: Text(
+                            NSLocalizedString(
+                                "'Half Basal Target' (HBT) setting adjusts how a temp target affects basal and ISF.\n     A lower HBT will allow Basal to be reduced earlier (at a less high TT).\n",
+                                comment: ""
+                            ) +
+                                NSLocalizedString("     HBT setting: ", comment: "") + "\(state.halfBasal) " +
+                                NSLocalizedString("mg/dl. Autosens.max setting determines the max endpoint", comment: "") +
+                                " (\(state.maxValue): \(state.maxValue * 100) %)"
                         )
-                        Text("\(state.percentage.formatted(.number)) %")
-                            .foregroundColor(isEditing ? .orange : .blue)
-                            .font(.largeTitle)
-                        Divider()
-                        Text(
-                            NSLocalizedString("Target", comment: "") +
-                                (
-                                    state
-                                        .units == .mmolL ?
-                                        ": \(computeTarget().asMmolL.formatted(.number.grouping(.never).rounded(rule: .towardZero).precision(.fractionLength(1)))) mmol/L" :
-                                        ": \(computeTarget().formatted(.number.grouping(.never).rounded(rule: .towardZero).precision(.fractionLength(0)))) mg/dl"
-                                )
-                        ).foregroundColor(.secondary).italic()
+                    ) {
+                        VStack {
+                            Slider(
+                                value: $state.percentage,
+                                in: 15 ...
+                                    Double(state.maxValue * 100),
+                                step: 1,
+                                onEditingChanged: { editing in
+                                    isEditing = editing
+                                }
+                            )
+                            Text("\(state.percentage.formatted(.number)) %")
+                                .foregroundColor(isEditing ? .orange : .blue)
+                                .font(.largeTitle)
+                            Divider()
+                            Text(
+                                NSLocalizedString("Temp Target to Save", comment: "") +
+                                    (
+                                        state
+                                            .units == .mmolL ?
+                                            ": \(computeTarget().asMmolL.formatted(.number.grouping(.never).rounded().precision(.fractionLength(1)))) mmol/L" :
+                                            ": \(computeTarget().formatted(.number.grouping(.never).rounded().precision(.fractionLength(0)))) mg/dl"
+                                    )
+                            ).foregroundColor(.primary).italic()
+                        }
+                    }
+                } else {
+                    Section(header: Text("Custom")) {
+                        HStack {
+                            Text("Target")
+                            Spacer()
+                            DecimalTextField("0", value: $state.low, formatter: formatter, cleanInput: true)
+                            Text(state.units.rawValue).foregroundColor(.secondary)
+                        }
+                        HStack {
+                            Text("Duration")
+                            Spacer()
+                            DecimalTextField("0", value: $state.duration, formatter: formatter, cleanInput: true)
+                            Text("minutes").foregroundColor(.secondary)
+                        }
+                        DatePicker("Date", selection: $state.date)
+                        Button { isPromtPresented = true }
+                        label: { Text("Save as preset") }
                     }
                 }
-
-                Section {
-                    HStack {
-                        Text("Duration")
-                        Spacer()
-                        DecimalTextField("0", value: $state.duration, formatter: formatter, cleanInput: true)
-                        Text("minutes").foregroundColor(.secondary)
+                if state.viewPercantage {
+                    Section {
+                        HStack {
+                            Text("Duration")
+                            Spacer()
+                            DecimalTextField("0", value: $state.duration, formatter: formatter, cleanInput: true)
+                            Text("minutes").foregroundColor(.secondary)
+                        }
+                        DatePicker("Date", selection: $state.date)
+                        Button { isPromtPresented = true }
+                        label: { Text("Save as preset") }
                     }
-                    DatePicker("Date", selection: $state.date)
-                    Button { isPromtPresented = true }
-                    label: { Text("Save as preset") }
                 }
 
                 Section {
@@ -102,16 +135,30 @@ extension AddTempTarget {
             .navigationTitle("Enact Temp Target")
             .navigationBarTitleDisplayMode(.automatic)
             .navigationBarItems(leading: Button("Close", action: state.hideModal))
+            .onDisappear {
+                if state.viewPercantage {
+                    let isEnabledMoc = ViewPercentage(context: moc)
+                    isEnabledMoc.enabled = true
+                    isEnabledMoc.date = Date()
+                    try? moc.save()
+                } else {
+                    let isEnabledMoc = ViewPercentage(context: moc)
+                    isEnabledMoc.enabled = false
+                    isEnabledMoc.date = Date()
+                    try? moc.save()
+                }
+            }
         }
 
         func computeTarget() -> Decimal {
-            let ratio = min(Decimal(state.percentage / 100), state.maxValue)
-            let diff = Double(state.halfBasal - 100)
-            let multiplier = state.percentage - (diff * (state.percentage / 100))
-            var target = Decimal(diff + multiplier) / ratio
+            var ratio = Decimal(state.percentage / 100)
+            let hB = state.halfBasal
+            let c = hB - 100
+            var target = (c / ratio) - c + 100
 
-            if (state.halfBasal + (state.halfBasal + target - 100)) <= 0 {
-                target = (state.halfBasal - 100 + (state.halfBasal - 100) * state.maxValue) / state.maxValue
+            if c * (c + target - 100) <= 0 {
+                ratio = state.maxValue
+                target = (c / ratio) - c + 100
             }
             return target
         }

+ 30 - 2
FreeAPS/Sources/Modules/PreferencesEditor/PreferencesEditorStateModel.swift

@@ -271,7 +271,10 @@ extension PreferencesEditor {
                     settable: self
                 ),
                 Field(
-                    displayName: NSLocalizedString("Allow SMB With High Temptarget", comment: "Allow SMB With High Temptarget"),
+                    displayName: NSLocalizedString(
+                        "Allow SMB With High Temptarget",
+                        comment: "Allow SMB With High Temptarget"
+                    ),
                     type: .boolean(keypath: \.allowSMBWithHighTemptarget),
                     infoText: NSLocalizedString(
                         "Defaults to false. When true, allows supermicrobolus (if otherwise enabled) even with high temp targets (> 100 mg/dl).",
@@ -360,6 +363,30 @@ extension PreferencesEditor {
 
             let targetSettings = [
                 Field(
+                    displayName: NSLocalizedString(
+                        "High Temptarget Raises Sensitivity",
+                        comment: "High Temptarget Raises Sensitivity"
+                    ),
+                    type: .boolean(keypath: \.highTemptargetRaisesSensitivity),
+                    infoText: NSLocalizedString(
+                        "Defaults to false. When set to true, raises sensitivity (lower sensitivity ratio) for temp targets set to >= 111. Synonym for exercise_mode. The higher your temp target above 110 will result in more sensitive (lower) ratios, e.g., temp target of 120 results in sensitivity ratio of 0.75, while 140 results in 0.6 (with default halfBasalTarget of 160).",
+                        comment: "High Temptarget Raises Sensitivity"
+                    ),
+                    settable: self
+                ),
+                Field(
+                    displayName: NSLocalizedString(
+                        "Low Temptarget Lowers Sensitivity",
+                        comment: "Low Temptarget Lowers Sensitivity"
+                    ),
+                    type: .boolean(keypath: \.lowTemptargetLowersSensitivity),
+                    infoText: NSLocalizedString(
+                        "Defaults to false. When set to true, can lower sensitivity (higher sensitivity ratio) for temptargets <= 99. The lower your temp target below 100 will result in less sensitive (higher) ratios, e.g., temp target of 95 results in sensitivity ratio of 1.09, while 85 results in 1.33 (with default halfBasalTarget of 160).",
+                        comment: "Low Temptarget Lowers Sensitivity"
+                    ),
+                    settable: self
+                ),
+                Field(
                     displayName: NSLocalizedString("Sensitivity Raises Target", comment: "Sensitivity Raises Target"),
                     type: .boolean(keypath: \.sensitivityRaisesTarget),
                     infoText: NSLocalizedString(
@@ -506,7 +533,8 @@ extension PreferencesEditor {
                     fields: dynamicISF
                 ),
                 FieldSection(
-                    displayName: NSLocalizedString("OpenAPS SMB settings", comment: "OpenAPS SMB settings"), fields: smbFields
+                    displayName: NSLocalizedString("OpenAPS SMB settings", comment: "OpenAPS SMB settings"),
+                    fields: smbFields
                 ),
                 FieldSection(
                     displayName: NSLocalizedString("OpenAPS targets settings", comment: "OpenAPS targets settings"),