From 7ef3ad38c6b13ac95b7b00b8101b6794088e1d4a Mon Sep 17 00:00:00 2001 From: marionbarker Date: Wed, 14 Dec 2022 09:58:09 -0800 Subject: [PATCH] DRAFT addition of safety features: maxCarbEntry, maxAutoIOB --- Loop.xcodeproj/project.pbxproj | 4 ++ Loop/Managers/DoseMath.swift | 44 +++++++++++-- Loop/Managers/LoopDataManager.swift | 12 +++- .../CarbEntryViewController.swift | 6 ++ Settings.bundle/Root.plist | 61 ++++++++++++++++++ Settings.bundle/en.lproj/Root.strings | Bin 0 -> 546 bytes 6 files changed, 118 insertions(+), 9 deletions(-) create mode 100644 Settings.bundle/Root.plist create mode 100644 Settings.bundle/en.lproj/Root.strings diff --git a/Loop.xcodeproj/project.pbxproj b/Loop.xcodeproj/project.pbxproj index f4cdd7cd33..5e324d05bc 100644 --- a/Loop.xcodeproj/project.pbxproj +++ b/Loop.xcodeproj/project.pbxproj @@ -455,6 +455,7 @@ B4E96D5D248A82A2002DABAD /* StatusBarHUDView.xib in Resources */ = {isa = PBXBuildFile; fileRef = B4E96D5C248A82A2002DABAD /* StatusBarHUDView.xib */; }; B4F3D25124AF890C0095CE44 /* BluetoothStateManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = B4F3D25024AF890C0095CE44 /* BluetoothStateManager.swift */; }; B4FEEF7D24B8A71F00A8DF9B /* DeviceDataManager+DeviceStatus.swift in Sources */ = {isa = PBXBuildFile; fileRef = B4FEEF7C24B8A71F00A8DF9B /* DeviceDataManager+DeviceStatus.swift */; }; + B633A70E291011EE00A39E1D /* Settings.bundle in Resources */ = {isa = PBXBuildFile; fileRef = B633A70D291011EE00A39E1D /* Settings.bundle */; }; C10B28461EA9BA5E006EA1FC /* far_future_high_bg_forecast.json in Resources */ = {isa = PBXBuildFile; fileRef = C10B28451EA9BA5E006EA1FC /* far_future_high_bg_forecast.json */; }; C11B9D5B286778A800500CF8 /* SwiftCharts in Frameworks */ = {isa = PBXBuildFile; productRef = C11B9D5A286778A800500CF8 /* SwiftCharts */; }; C11B9D5E286778D000500CF8 /* LoopKitUI.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C11B9D5D286778D000500CF8 /* LoopKitUI.framework */; }; @@ -1438,6 +1439,7 @@ B4E96D5C248A82A2002DABAD /* StatusBarHUDView.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = StatusBarHUDView.xib; sourceTree = ""; }; B4F3D25024AF890C0095CE44 /* BluetoothStateManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BluetoothStateManager.swift; sourceTree = ""; }; B4FEEF7C24B8A71F00A8DF9B /* DeviceDataManager+DeviceStatus.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "DeviceDataManager+DeviceStatus.swift"; sourceTree = ""; }; + B633A70D291011EE00A39E1D /* Settings.bundle */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.plug-in"; path = Settings.bundle; sourceTree = ""; }; C101947127DD473C004E7EB8 /* MockKitUI.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = MockKitUI.framework; sourceTree = BUILT_PRODUCTS_DIR; }; C10B28451EA9BA5E006EA1FC /* far_future_high_bg_forecast.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = far_future_high_bg_forecast.json; sourceTree = ""; }; C11AA5C7258736CF00BDE12F /* DerivedAssetsBase.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = DerivedAssetsBase.xcassets; sourceTree = ""; }; @@ -1883,6 +1885,7 @@ 43776F831B8022E90074EA36 = { isa = PBXGroup; children = ( + B633A70D291011EE00A39E1D /* Settings.bundle */, C18A491122FCC20B00FDA733 /* Scripts */, 4FF4D0FA1E1834BD00846527 /* Common */, 43776F8E1B8022E90074EA36 /* Loop */, @@ -3295,6 +3298,7 @@ A966152723EA5A26005D8B29 /* DerivedAssets.xcassets in Resources */, 7D70764F1FE06EE1004AC8EA /* InfoPlist.strings in Resources */, 7D7076631FE06EE4004AC8EA /* Localizable.strings in Resources */, + B633A70E291011EE00A39E1D /* Settings.bundle in Resources */, 43776F971B8022E90074EA36 /* Main.storyboard in Resources */, ); runOnlyForDeploymentPostprocessing = 0; diff --git a/Loop/Managers/DoseMath.swift b/Loop/Managers/DoseMath.swift index 01c9bedfe5..f6fd378a07 100644 --- a/Loop/Managers/DoseMath.swift +++ b/Loop/Managers/DoseMath.swift @@ -234,7 +234,9 @@ extension Collection where Element: GlucoseValue { at date: Date, suspendThreshold: HKQuantity, sensitivity: HKQuantity, - model: InsulinModel + model: InsulinModel, + maxAutoIOB: Double?, + insulinOnBoard: Double? ) -> InsulinCorrection? { var minGlucose: GlucoseValue? var eventualGlucose: GlucoseValue? @@ -322,14 +324,32 @@ extension Collection where Element: GlucoseValue { return nil } + // Question: this is the entirelyBelow section of code + // I did not add maxAutoIOB check here as I don't think it is needed + return .entirelyBelowRange( min: min, minTarget: minGlucoseTargets.lowerBound, units: units ) } else if eventual.quantity > eventualGlucoseTargets.upperBound, - let minCorrectionUnits = minCorrectionUnits, let correctingGlucose = correctingGlucose + var minCorrectionUnits = minCorrectionUnits, let correctingGlucose = correctingGlucose { + // Limit automatic dosing to prevent insulinOnBoard > maxAutoIOB + if minCorrectionUnits > 0 && + insulinOnBoard != nil && + maxAutoIOB != nil && + maxAutoIOB! > 0 { + let checkMaxAutoIOB = maxAutoIOB! - (insulinOnBoard! + minCorrectionUnits) + if checkMaxAutoIOB < 0 { + print("*** maxAutoIOB Feature enacted in aboveRange") + print("*** Initial minCorrectionUnits ", round(100*minCorrectionUnits)/100) + print("*** maxAutoIOB, insulinOnBoard, checkMaxAutoIOB ", round(100*maxAutoIOB!)/100, round(100*insulinOnBoard!)/100, round(100*checkMaxAutoIOB)/100) + minCorrectionUnits = Swift.max(minCorrectionUnits+checkMaxAutoIOB, 0) + print("*** Adjusted minCorrectionUnits ", round(100*minCorrectionUnits)/100) + } + } + return .aboveRange( min: min, correcting: correctingGlucose, @@ -371,14 +391,18 @@ extension Collection where Element: GlucoseValue { rateRounder: ((Double) -> Double)? = nil, isBasalRateScheduleOverrideActive: Bool = false, duration: TimeInterval = .minutes(30), - continuationInterval: TimeInterval = .minutes(11) + continuationInterval: TimeInterval = .minutes(11), + insulinOnBoard: Double?, + maxAutoIOB: Double? ) -> TempBasalRecommendation? { let correction = self.insulinCorrection( to: correctionRange, at: date, suspendThreshold: suspendThreshold ?? correctionRange.quantityRange(at: date).lowerBound, sensitivity: sensitivity.quantity(at: date), - model: model + model: model, + maxAutoIOB: maxAutoIOB, + insulinOnBoard: insulinOnBoard ) let scheduledBasalRate = basalRates.value(at: date) @@ -439,14 +463,18 @@ extension Collection where Element: GlucoseValue { rateRounder: ((Double) -> Double)? = nil, isBasalRateScheduleOverrideActive: Bool = false, duration: TimeInterval = .minutes(30), - continuationInterval: TimeInterval = .minutes(11) + continuationInterval: TimeInterval = .minutes(11), + insulinOnBoard: Double?, + maxAutoIOB: Double? ) -> AutomaticDoseRecommendation? { guard let correction = self.insulinCorrection( to: correctionRange, at: date, suspendThreshold: suspendThreshold ?? correctionRange.quantityRange(at: date).lowerBound, sensitivity: sensitivity.quantity(at: date), - model: model + model: model, + maxAutoIOB: maxAutoIOB, + insulinOnBoard: insulinOnBoard ) else { return nil } @@ -516,7 +544,9 @@ extension Collection where Element: GlucoseValue { at: date, suspendThreshold: suspendThreshold ?? correctionRange.quantityRange(at: date).lowerBound, sensitivity: sensitivity.quantity(at: date), - model: model + model: model, + maxAutoIOB: nil, + insulinOnBoard: nil ) else { return ManualBolusRecommendation(amount: 0, pendingInsulin: pendingInsulin) } diff --git a/Loop/Managers/LoopDataManager.swift b/Loop/Managers/LoopDataManager.swift index bc422867fc..2e7d7685e6 100644 --- a/Loop/Managers/LoopDataManager.swift +++ b/Loop/Managers/LoopDataManager.swift @@ -1616,6 +1616,10 @@ extension LoopDataManager { let dosingRecommendation: AutomaticDoseRecommendation? + // DRAFT - cheat with Settings Bundle: later - add maxAutoIOB properly to Loop: + // if not entered in iOS Settings, maxAutoIOB is 0.0 + let maxAutoIOB = UserDefaults.standard.double(forKey: "maxAutoIOB") + switch settings.automaticDosingStrategy { case .automaticBolus: let volumeRounder = { (_ units: Double) in @@ -1634,7 +1638,9 @@ extension LoopDataManager { lastTempBasal: lastTempBasal, volumeRounder: volumeRounder, rateRounder: rateRounder, - isBasalRateScheduleOverrideActive: settings.scheduleOverride?.isBasalRateScheduleOverriden(at: startDate) == true + isBasalRateScheduleOverrideActive: settings.scheduleOverride?.isBasalRateScheduleOverriden(at: startDate) == true, + insulinOnBoard: dosingDecision.insulinOnBoard?.value, + maxAutoIOB: maxAutoIOB ) case .tempBasalOnly: let temp = predictedGlucose.recommendedTempBasal( @@ -1647,7 +1653,9 @@ extension LoopDataManager { maxBasalRate: maxBasal!, lastTempBasal: lastTempBasal, rateRounder: rateRounder, - isBasalRateScheduleOverrideActive: settings.scheduleOverride?.isBasalRateScheduleOverriden(at: startDate) == true + isBasalRateScheduleOverrideActive: settings.scheduleOverride?.isBasalRateScheduleOverriden(at: startDate) == true, + insulinOnBoard: dosingDecision.insulinOnBoard?.value, + maxAutoIOB: maxAutoIOB ) dosingRecommendation = AutomaticDoseRecommendation(basalAdjustment: temp) } diff --git a/Loop/View Controllers/CarbEntryViewController.swift b/Loop/View Controllers/CarbEntryViewController.swift index d553682a9e..a4c79f30ad 100644 --- a/Loop/View Controllers/CarbEntryViewController.swift +++ b/Loop/View Controllers/CarbEntryViewController.swift @@ -526,6 +526,12 @@ final class CarbEntryViewController: LoopChartsTableViewController, Identifiable } guard let quantity = quantity, quantity.doubleValue(for: preferredCarbUnit) > 0 else { return false } + + // DRAFT - cheat with Settings Bundle: later - add maxCarbEntry properly to Loop: + let maxCarbEntry = UserDefaults.standard.double(forKey: "maxCarbEntry") + if (maxCarbEntry > 0) { + maxQuantity = HKQuantity(unit: .gram(), doubleValue: maxCarbEntry) + } guard quantity.compare(maxQuantity) != .orderedDescending else { navigationDelegate.showMaxQuantityValidationWarning(for: self, maxQuantityGrams: maxQuantity.doubleValue(for: .gram())) return false diff --git a/Settings.bundle/Root.plist b/Settings.bundle/Root.plist new file mode 100644 index 0000000000..a584b45ca3 --- /dev/null +++ b/Settings.bundle/Root.plist @@ -0,0 +1,61 @@ + + + + + StringsTable + Root + PreferenceSpecifiers + + + Type + PSGroupSpecifier + Title + Max Allowed Carb Entry + + + Type + PSMultiValueSpecifier + Title + Max Allowed Carb Entry + Key + maxCarbEntry + DefaultValue + 250g + Values + + 50 + 99 + 150 + 200 + 250 + 300 + + Titles + + 50g + 99g + 150g + 200g + 250g + 300g + + + + Type + PSGroupSpecifier + Title + max Auto IOB + + + Type + PSTextFieldSpecifier + Title + maxAutoIOB + Key + maxAutoIOB + KeyboardType + NumbersAndPunctuation + + + + diff --git a/Settings.bundle/en.lproj/Root.strings b/Settings.bundle/en.lproj/Root.strings new file mode 100644 index 0000000000000000000000000000000000000000..8cd87b9d6b20c1fbf87bd4db3db267fca5ad4df9 GIT binary patch literal 546 zcmaixOHRW;5JYRuDMndFh#Ua1V1d}N;sVAV2TO?uC3a9aJn*VxFrY}tnon0(S66#J z-d9>G>6W!ur(SDqlp`9nn~*(m%iWnv?yq`Qfp6XbK1?+om~~#r)ZnhkYQU_VbfjuT zHNn`CX<0sd*m1A}>&5sU$akD=GTXJ1e literal 0 HcmV?d00001