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

Merge pull request #126 from AndreasStokholm/feat/move-autosens

Move autosens from therapy->isf to algorithm->autosens
polscm32 1 год назад
Родитель
Сommit
f22841606b

+ 3 - 1
FreeAPS/Sources/Modules/AutosensSettings/AutosensSettingsDataFlow.swift

@@ -2,4 +2,6 @@ enum AutosensSettings {
     enum Config {}
 }
 
-protocol AutosensSettingsProvider: Provider {}
+protocol AutosensSettingsProvider: Provider {
+    var autosense: Autosens { get }
+}

+ 7 - 1
FreeAPS/Sources/Modules/AutosensSettings/AutosensSettingsProvider.swift

@@ -1,3 +1,9 @@
 extension AutosensSettings {
-    final class Provider: BaseProvider, AutosensSettingsProvider {}
+    final class Provider: BaseProvider, AutosensSettingsProvider {
+        var autosense: Autosens {
+            storage.retrieve(OpenAPS.Settings.autosense, as: Autosens.self)
+                ?? Autosens(from: OpenAPS.defaults(for: OpenAPS.Settings.autosense))
+                ?? Autosens(ratio: 1, newisf: nil, timestamp: nil)
+        }
+    }
 }

+ 42 - 0
FreeAPS/Sources/Modules/AutosensSettings/AutosensSettingsStateModel.swift

@@ -1,3 +1,4 @@
+import CoreData
 import Observation
 import SwiftUI
 
@@ -5,19 +6,60 @@ extension AutosensSettings {
     final class StateModel: BaseStateModel<Provider> {
         @Injected() var settings: SettingsManager!
         @Injected() var storage: FileStorage!
+        @Injected() var determinationStorage: DeterminationStorage!
 
         var units: GlucoseUnits = .mgdL
 
+        private(set) var autosensISF: Decimal?
+        private(set) var autosensRatio: Decimal = 0
+        var determinationsFromPersistence: [OrefDetermination] = []
+
+        let viewContext = CoreDataStack.shared.persistentContainer.viewContext
+
         @Published var autosensMax: Decimal = 1.2
         @Published var autosensMin: Decimal = 0.7
         @Published var rewindResetsAutosens: Bool = true
 
+        var preferences: Preferences {
+            settingsManager.preferences
+        }
+
         override func subscribe() {
             units = settingsManager.settings.units
 
             subscribePreferencesSetting(\.autosensMax, on: $autosensMax) { autosensMax = $0 }
             subscribePreferencesSetting(\.autosensMin, on: $autosensMin) { autosensMin = $0 }
             subscribePreferencesSetting(\.rewindResetsAutosens, on: $rewindResetsAutosens) { rewindResetsAutosens = $0 }
+
+            if let newISF = provider.autosense.newisf {
+                autosensISF = newISF
+            }
+
+            autosensRatio = provider.autosense.ratio
+            setupDeterminationsArray()
+        }
+
+        private func setupDeterminationsArray() {
+            Task {
+                let ids = await determinationStorage.fetchLastDeterminationObjectID(
+                    predicate: NSPredicate.enactedDetermination
+                )
+                await updateDeterminationsArray(with: ids)
+            }
+        }
+
+        @MainActor private func updateDeterminationsArray(with IDs: [NSManagedObjectID]) {
+            do {
+                let objects = try IDs.compactMap { id in
+                    try viewContext.existingObject(with: id) as? OrefDetermination
+                }
+                determinationsFromPersistence = objects
+
+            } catch {
+                debugPrint(
+                    "Home State: \(#function) \(DebuggingIdentifiers.failed) error while updating the glucose array: \(error.localizedDescription)"
+                )
+            }
         }
     }
 }

+ 108 - 0
FreeAPS/Sources/Modules/AutosensSettings/View/AutosensSettingsRootView.swift

@@ -16,8 +16,116 @@ extension AutosensSettings {
         @EnvironmentObject var appIcons: Icons
         @Environment(AppState.self) var appState
 
+        private var rateFormatter: NumberFormatter {
+            let formatter = NumberFormatter()
+            formatter.numberStyle = .decimal
+            formatter.maximumFractionDigits = 2
+            return formatter
+        }
+
+        var autosensVerboseHint: some View {
+            VStack(alignment: .leading, spacing: 15) {
+                Text(
+                    "Autosens automatically adjusts insulin delivery based on how sensitive or resistant you are to insulin at the time of the current loop cycle by analyzing past data to keep blood sugar levels stable."
+                )
+
+                VStack(alignment: .leading, spacing: 5) {
+                    Text("How it Works").bold()
+                    Text(
+                        "It looks at the last 8-24 hours of data, excluding meal-related changes, and adjusts insulin settings like basal rates and targets when needed to match your sensitivity or resistance to insulin."
+                    )
+                }
+
+                VStack(alignment: .leading, spacing: 5) {
+                    Text("What it Adjusts").bold()
+                    Text(
+                        "Autosens modifies Insulin Sensitivity Factor (ISF), basal rates, and target blood sugar levels. It doesn’t account for carbs but adjusts for insulin effectiveness based on patterns in your glucose data."
+                    )
+                }
+
+                VStack(alignment: .leading, spacing: 5) {
+                    Text("Key Limitations").bold()
+                    Text(
+                        "Autosens has safety limits determined by your Autosens Max and Autosens Min settings. These settings prevent over-adjusting."
+                    )
+                }
+
+                Text(
+                    "Autosens functions alongside certain settings, like Super Micro Bolus (SMB). Other settings, like Dynamic ISF, alter portions of the Autosens formula. Please review the in-app hints for the Algorithm Settings prior to enabling them to understand how they may influence it."
+                )
+            }
+        }
+
+        var AutosensView: some View {
+            Section(
+                header: !state.settingsManager.preferences
+                    .useNewFormula ? Text("Autosens") : Text("Dynamic Sensitivity")
+            ) {
+                VStack {
+                    let dynamicRatio = state.determinationsFromPersistence.first?.sensitivityRatio
+                    let dynamicISF = state.determinationsFromPersistence.first?.insulinSensitivity
+                    let newISF = state.autosensISF
+                    HStack {
+                        Text("Sensitivity Ratio")
+                        Spacer()
+                        Text(
+                            rateFormatter
+                                .string(from: (
+                                    (
+                                        !state.settingsManager.preferences.useNewFormula ? state
+                                            .autosensRatio as NSDecimalNumber : dynamicRatio
+                                    ) ?? 1
+                                ) as NSNumber) ?? "1"
+                        )
+                    }.padding(.vertical)
+                    HStack {
+                        Text("Calculated Sensitivity")
+                        Spacer()
+                        if state.units == .mgdL {
+                            Text(
+                                !state.settingsManager.preferences
+                                    .useNewFormula ? newISF!.description : (dynamicISF ?? 0).description
+                            )
+                        } else {
+                            Text((
+                                !state.settingsManager.preferences
+                                    .useNewFormula ? newISF!.formattedAsMmolL : dynamicISF?.decimalValue.formattedAsMmolL
+                            ) ?? "0")
+                        }
+                        Text(state.units.rawValue + "/U").foregroundColor(.secondary)
+                    }
+
+                    HStack(alignment: .top) {
+                        Text(
+                            "This adjusted ISF is temporary, will change with the next loop cycle, and should not be directly used as your profile ISF value."
+                        )
+                        .font(.footnote)
+                        .foregroundColor(.secondary)
+                        .lineLimit(nil)
+                        Spacer()
+                        Button(
+                            action: {
+                                hintLabel = "Autosens"
+                                selectedVerboseHint = AnyView(autosensVerboseHint)
+                                shouldDisplayHint.toggle()
+                            },
+                            label: {
+                                HStack {
+                                    Image(systemName: "questionmark.circle")
+                                }
+                            }
+                        ).buttonStyle(BorderlessButtonStyle())
+                    }.padding(.top)
+                }.padding(.bottom)
+            }.listRowBackground(Color.chart)
+        }
+
         var body: some View {
             List {
+                if state.autosensISF != nil {
+                    AutosensView
+                }
+
                 SettingInputSection(
                     decimalValue: $state.autosensMax,
                     booleanValue: $booleanPlaceholder,

+ 0 - 1
FreeAPS/Sources/Modules/ISFEditor/ISFEditorDataFlow.swift

@@ -27,6 +27,5 @@ enum ISFEditor {
 protocol ISFEditorProvider: Provider {
     var profile: InsulinSensitivities { get }
     func saveProfile(_ profile: InsulinSensitivities)
-    var autosense: Autosens { get }
     var autotune: Autotune? { get }
 }

+ 0 - 6
FreeAPS/Sources/Modules/ISFEditor/ISFEditorProvider.swift

@@ -35,12 +35,6 @@ extension ISFEditor {
             storage.save(profile, as: OpenAPS.Settings.insulinSensitivities)
         }
 
-        var autosense: Autosens {
-            storage.retrieve(OpenAPS.Settings.autosense, as: Autosens.self)
-                ?? Autosens(from: OpenAPS.defaults(for: OpenAPS.Settings.autosense))
-                ?? Autosens(ratio: 1, newisf: nil, timestamp: nil)
-        }
-
         var autotune: Autotune? {
             storage.retrieve(OpenAPS.Settings.autotune, as: Autotune.self)
         }

+ 0 - 34
FreeAPS/Sources/Modules/ISFEditor/ISFEditorStateModel.swift

@@ -10,13 +10,9 @@ extension ISFEditor {
         var items: [Item] = []
         var initialItems: [Item] = []
         var shouldDisplaySaving: Bool = false
-        private(set) var autosensISF: Decimal?
-        private(set) var autosensRatio: Decimal = 0
         var autotune: Autotune?
-        var determinationsFromPersistence: [OrefDetermination] = []
 
         let context = CoreDataStack.shared.newTaskContext()
-        let viewContext = CoreDataStack.shared.persistentContainer.viewContext
 
         let timeValues = stride(from: 0.0, to: 1.days.timeInterval, by: 30.minutes.timeInterval).map { $0 }
 
@@ -55,13 +51,6 @@ extension ISFEditor {
             initialItems = items.map { Item(rateIndex: $0.rateIndex, timeIndex: $0.timeIndex) }
 
             autotune = provider.autotune
-
-            if let newISF = provider.autosense.newisf {
-                autosensISF = newISF
-            }
-
-            autosensRatio = provider.autosense.ratio
-            setupDeterminationsArray()
         }
 
         func add() {
@@ -119,29 +108,6 @@ extension ISFEditor {
                 }
             }
         }
-
-        private func setupDeterminationsArray() {
-            Task {
-                let ids = await determinationStorage.fetchLastDeterminationObjectID(
-                    predicate: NSPredicate.enactedDetermination
-                )
-                await updateDeterminationsArray(with: ids)
-            }
-        }
-
-        @MainActor private func updateDeterminationsArray(with IDs: [NSManagedObjectID]) {
-            do {
-                let objects = try IDs.compactMap { id in
-                    try viewContext.existingObject(with: id) as? OrefDetermination
-                }
-                determinationsFromPersistence = objects
-
-            } catch {
-                debugPrint(
-                    "Home State: \(#function) \(DebuggingIdentifiers.failed) error while updating the glucose array: \(error.localizedDescription)"
-                )
-            }
-        }
     }
 }
 

+ 0 - 45
FreeAPS/Sources/Modules/ISFEditor/View/ISFEditorRootView.swift

@@ -18,13 +18,6 @@ extension ISFEditor {
             return formatter
         }
 
-        private var rateFormatter: NumberFormatter {
-            let formatter = NumberFormatter()
-            formatter.numberStyle = .decimal
-            formatter.maximumFractionDigits = 2
-            return formatter
-        }
-
         var saveButton: some View {
             ZStack {
                 let shouldDisableButton = state.items.isEmpty || !state.hasChanges
@@ -82,44 +75,6 @@ extension ISFEditor {
                         }
                     }.listRowBackground(Color.chart)
                 }
-                if let newISF = state.autosensISF {
-                    Section(
-                        header: !state.settingsManager.preferences
-                            .useNewFormula ? Text("Autosens") : Text("Dynamic Sensitivity")
-                    ) {
-                        let dynamicRatio = state.determinationsFromPersistence.first?.sensitivityRatio
-                        let dynamicISF = state.determinationsFromPersistence.first?.insulinSensitivity
-                        HStack {
-                            Text("Sensitivity Ratio")
-                            Spacer()
-                            Text(
-                                rateFormatter
-                                    .string(from: (
-                                        (
-                                            !state.settingsManager.preferences.useNewFormula ? state
-                                                .autosensRatio as NSDecimalNumber : dynamicRatio
-                                        ) ?? 1
-                                    ) as NSNumber) ?? "1"
-                            )
-                        }
-                        HStack {
-                            Text("Calculated Sensitivity")
-                            Spacer()
-                            if state.units == .mgdL {
-                                Text(
-                                    !state.settingsManager.preferences
-                                        .useNewFormula ? newISF.description : (dynamicISF ?? 0).description
-                                )
-                            } else {
-                                Text((
-                                    !state.settingsManager.preferences
-                                        .useNewFormula ? newISF.formattedAsMmolL : dynamicISF?.decimalValue.formattedAsMmolL
-                                ) ?? "0")
-                            }
-                            Text(state.units.rawValue + "/U").foregroundColor(.secondary)
-                        }
-                    }.listRowBackground(Color.chart)
-                }
 
                 if !state.canAdd {
                     Section {