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

Merge pull request #265 from mountrcg/maxAbsorptionTime

Introduce configurable MaxMealAbsorptionTime setting
Deniz Cengiz 1 год назад
Родитель
Сommit
9e02c7b2c0

Разница между файлами не показана из-за своего большого размера
+ 1 - 1
Trio/Resources/javascript/bundle/meal.js


Разница между файлами не показана из-за своего большого размера
+ 1 - 1
Trio/Resources/javascript/bundle/profile.js


+ 1 - 0
Trio/Resources/json/defaults/preferences.json

@@ -14,6 +14,7 @@
   "exercise_mode" : false,
   "half_basal_exercise_target" : 160,
   "maxCOB" : 120,
+  "maxMealAbsorptionTime" : 6,
   "wide_bg_target_range" : false,
   "skip_neutral_temps" : false,
   "unsuspend_if_no_temp" : false,

+ 1 - 0
Trio/Sources/Models/DecimalPickerSettings.swift

@@ -77,6 +77,7 @@ struct DecimalPickerSettings {
         type: PickerSetting.PickerSettingType.glucose
     )
     var maxCOB = PickerSetting(value: 120, step: 5, min: 0, max: 300, type: PickerSetting.PickerSettingType.gram)
+    var maxMealAbsorptionTime = PickerSetting(value: 6, step: 1, min: 4, max: 10, type: PickerSetting.PickerSettingType.hour)
     var min5mCarbimpact = PickerSetting(value: 8, step: 1, min: 1, max: 20, type: PickerSetting.PickerSettingType.glucose)
     var remainingCarbsFraction = PickerSetting(
         value: 1.0,

+ 6 - 0
Trio/Sources/Models/Preferences.swift

@@ -16,6 +16,7 @@ struct Preferences: JSON, Equatable {
     var exerciseMode: Bool = false
     var halfBasalExerciseTarget: Decimal = 160
     var maxCOB: Decimal = 120
+    var maxMealAbsorptionTime: Decimal = 6
     var wideBGTargetRange: Bool = false
     var skipNeutralTemps: Bool = false
     var unsuspendIfNoTemp: Bool = false
@@ -72,6 +73,7 @@ extension Preferences {
         case exerciseMode = "exercise_mode"
         case halfBasalExerciseTarget = "half_basal_exercise_target"
         case maxCOB
+        case maxMealAbsorptionTime
         case wideBGTargetRange = "wide_bg_target_range"
         case skipNeutralTemps = "skip_neutral_temps"
         case unsuspendIfNoTemp = "unsuspend_if_no_temp"
@@ -184,6 +186,10 @@ extension Preferences: Decodable {
             preferences.maxCOB = maxCOB
         }
 
+        if let maxMealAbsorptionTime = try? container.decode(Decimal.self, forKey: .maxMealAbsorptionTime) {
+            preferences.maxMealAbsorptionTime = maxMealAbsorptionTime
+        }
+
         if let wideBGTargetRange = try? container.decode(Bool.self, forKey: .wideBGTargetRange) {
             preferences.wideBGTargetRange = wideBGTargetRange
         }

+ 4 - 0
Trio/Sources/Modules/MealSettings/MealSettingsStateModel.swift

@@ -11,6 +11,7 @@ extension MealSettings {
         @Published var timeCap: Decimal = 8
         @Published var minuteInterval: Decimal = 30
         @Published var delay: Decimal = 60
+        @Published var maxMealAbsorptionTime: Decimal = 6
 
         override func subscribe() {
             units = settingsManager.settings.units
@@ -19,12 +20,15 @@ extension MealSettings {
             subscribeSetting(\.maxCarbs, on: $maxCarbs) { maxCarbs = $0 }
             subscribeSetting(\.maxFat, on: $maxFat) { maxFat = $0 }
             subscribeSetting(\.maxProtein, on: $maxProtein) { maxProtein = $0 }
+
             subscribeSetting(\.timeCap, on: $timeCap.map(Int.init), initial: {
                 timeCap = Decimal($0)
             }, map: {
                 $0
             })
 
+            subscribePreferencesSetting(\.maxMealAbsorptionTime, on: $maxMealAbsorptionTime) { maxMealAbsorptionTime = $0 }
+
             subscribeSetting(\.minuteInterval, on: $minuteInterval.map(Int.init), initial: {
                 minuteInterval = Decimal($0)
             }, map: {

+ 31 - 1
Trio/Sources/Modules/MealSettings/View/MealSettingsRootView.swift

@@ -162,7 +162,7 @@ extension MealSettings {
                                             AnyView(
                                                 VStack(alignment: .leading, spacing: 5) {
                                                     Text("Max Carbs:").bold()
-                                                    Text("Enter the largest carbohydrate value allowed per meal entry")
+                                                    Text("Enter the largest carb value allowed per meal entry")
                                                     Text("Max Fat:").bold()
                                                     Text("Enter the largest fat value allowed per meal entry")
                                                     Text("Max Protein:").bold()
@@ -183,6 +183,36 @@ extension MealSettings {
                 ).listRowBackground(Color.chart)
 
                 SettingInputSection(
+                    decimalValue: $state.maxMealAbsorptionTime,
+                    booleanValue: $booleanPlaceholder,
+                    shouldDisplayHint: $shouldDisplayHint,
+                    selectedVerboseHint: Binding(
+                        get: { selectedVerboseHint },
+                        set: {
+                            selectedVerboseHint = $0.map { AnyView($0) }
+                            hintLabel = "Maximum Meal Absorption Time"
+                        }
+                    ),
+                    units: state.units,
+                    type: .decimal("maxMealAbsorptionTime"),
+                    label: "Max Meal Absorption Time",
+                    miniHint: "The maximum duration for tracking carb entries in estimating Carbs on Board (COB)",
+                    verboseHint:
+                    VStack(alignment: .leading, spacing: 10) {
+                        Text("Default: 6 hours").bold()
+                        Text(
+                            "Carb entries will be fully decayed by the number of hours specified as Max Meal Absorption Time. Meals that are high in fat and/or protein can have long lasting effects on BG levels. To allow such late meal effects to be considered by the carb decay model, a longer Max Meal Absorption Time than the default 6 hours can be set."
+                        )
+                        Text(
+                            "If carb entries decay too slowly, it is possible to set a lower than default setting. But this should typically be adressed by tuning ISF and CR settings instead, which in combination determines the rate of carb decay."
+                        )
+                        Text(
+                            "Min 4 hours, max 10 hours."
+                        )
+                    }
+                )
+
+                SettingInputSection(
                     decimalValue: $decimalPlaceholder,
                     booleanValue: $state.useFPUconversion,
                     shouldDisplayHint: $shouldDisplayHint,

+ 1 - 0
Trio/Sources/Modules/Settings/SettingItems.swift

@@ -171,6 +171,7 @@ enum SettingItems {
             view: .mealSettings,
             searchContents: [
                 "Max Carbs",
+                "Max Meal Absorption Time",
                 "Max Fat",
                 "Max Protein",
                 "Display and Allow Fat and Protein Entries",

+ 2 - 0
Trio/Sources/Views/SettingInputSection.swift

@@ -107,6 +107,8 @@ struct SettingInputSection<VerboseHint: View>: View {
             return pickerSettingsProvider.settings.hours
         case "maxCarbs":
             return pickerSettingsProvider.settings.maxCarbs
+        case "maxMealAbsorptionTime":
+            return pickerSettingsProvider.settings.maxMealAbsorptionTime
         case "maxFat":
             return pickerSettingsProvider.settings.maxFat
         case "maxProtein":

+ 7 - 1
oref0_source_version.txt

@@ -1,6 +1,12 @@
-oref0 branch: tdd-pumpdataCheck - git version: 46deecb
+oref0 branch: maxAbsorptionTime - git version: a542ed3
 
 Last commits:
+a542ed3 use guarded maxAbsorptionTime
+4c77757 Revert "reduce dynISF logging"
+1567c76 use variable name maxMealAbsorptionTime
+61b4f85 reduce dynISF logging
+0fe81c1 introduce maxAbsorptionTime to orefmaxAbsorptionTime
+ade267d Merge pull request #37 from mountrcg/tdd-pumpdataCheck
 46deecb remove dynisf check on pumpdata calc
 2f258b2 Merge pull request #36 from mountrcg/fixTDDcheck
 4998a09 fix condition  to use weightedAverage as TDD

+ 3 - 2
trio-oref/lib/determine-basal/cob.js

@@ -12,7 +12,8 @@ function detectCarbAbsorption(inputs) {
     });
     var iob_inputs = inputs.iob_inputs;
     var basalprofile = inputs.basalprofile;
-    /* TODO why does declaring profile break tests-command-behavior.tests.sh? */ profile = inputs.iob_inputs.profile;
+    /* TODO why does declaring profile break tests-command-behavior.tests.sh? */
+    profile = inputs.iob_inputs.profile;
     var mealTime = new Date(inputs.mealTime);
     var ciTime = new Date(inputs.ciTime);
 
@@ -50,7 +51,7 @@ function detectCarbAbsorption(inputs) {
         }
         // only consider BGs for 6h after a meal for calculating COB
         var hoursAfterMeal = (bgTime-mealTime)/(60*60*1000);
-        if (hoursAfterMeal > 6 || foundPreMealBG) {
+        if (hoursAfterMeal > profile.maxMealAbsorptionTime || foundPreMealBG) {
             continue;
         } else if (hoursAfterMeal < 0) {
 //console.error("Found pre-meal BG:",glucose_data[i].glucose, bgTime, Math.round(hoursAfterMeal*100)/100);

+ 21 - 6
trio-oref/lib/meal/total.js

@@ -1,6 +1,12 @@
 var tz = require('moment-timezone');
 var calcMealCOB = require('../determine-basal/cob');
 
+function round(value, digits) {
+    if (! digits) { digits = 0; }
+    var scale = Math.pow(10, digits);
+    return Math.round(value * scale) / scale;
+}
+
 function recentCarbs(opts, time) {
     var treatments = opts.treatments;
     var profile_data = opts.profile;
@@ -41,10 +47,18 @@ function recentCarbs(opts, time) {
     var nsCarbsToRemove = 0;
     var bwCarbsToRemove = 0;
     var journalCarbsToRemove = 0;
+    var maxMealAbsorptionTime = 6;
+
+    if (typeof(profile_data.maxMealAbsorptionTime) === 'number' && ! isNaN(profile_data.maxMealAbsorptionTime)) {
+        maxMealAbsorptionTime = profile_data.maxMealAbsorptionTime;
+    } else {
+        console.error("Bad profile.maxMealAbsorptionTime:",profile_data.maxMealAbsorptionTime);
+    }
+
     treatments.forEach(function(treatment) {
         var now = time.getTime();
-        // consider carbs from up to 6 hours ago in calculating COB
-        var carbWindow = now - 6 * 60*60*1000;
+        // consider carbs from up to the meal preference maxMealAbsorptionTime hours ago in calculating COB
+        var carbWindow = now - maxMealAbsorptionTime * 60*60*1000;
         var treatmentDate = new Date(tz(treatment.timestamp));
         var treatmentTime = treatmentDate.getTime();
         if (treatmentTime > carbWindow && treatmentTime <= now) {
@@ -98,15 +112,16 @@ function recentCarbs(opts, time) {
     // calculate the current deviation and steepest deviation downslope over the last hour
     COB_inputs.ciTime = time.getTime();
     // set mealTime to 6h ago for Deviation calculations
-    COB_inputs.mealTime = time.getTime() - 6 * 60 * 60 * 1000;
+    COB_inputs.mealTime = time.getTime() - maxMealAbsorptionTime * 60 * 60 * 1000;
     var c = calcMealCOB(COB_inputs);
     //console.error(c.currentDeviation, c.slopeFromMaxDeviation);
 
     // set a hard upper limit on COB to mitigate impact of erroneous or malicious carb entry
-    if (typeof(profile.maxCOB) === 'number' && ! isNaN(profile.maxCOB)) {
-        mealCOB = Math.min( profile.maxCOB, mealCOB );
+    if (typeof(profile_data.maxCOB) === 'number' && ! isNaN(profile_data.maxCOB)) {
+        mealCOB = Math.min( profile_data.maxCOB, mealCOB );
+        console.error("mealCOB: " + round(mealCOB,1) + " with maxCOB " + profile_data.maxCOB + "g and maxMealAbsorptionTime " + maxMealAbsorptionTime + "hrs.");
     } else {
-        console.error("Bad profile.maxCOB:",profile.maxCOB);
+        console.error("Bad profile.maxCOB:",profile_data.maxCOB);
     }
 
     // if currentDeviation is null or maxDeviation is 0, set mealCOB to 0 for zombie-carb safety

+ 4 - 1
trio-oref/lib/profile/index.js

@@ -25,6 +25,7 @@ function defaults ( ) {
     // (If someone enters more carbs or stacks more; OpenAPS will just truncate dosing based on 120.
     // Essentially, this just limits AMA/SMB as a safety cap against excessive COB entry)
     , maxCOB: 120
+    , maxMealAbsorptionTime: 6 // Handling of long lasting effects of "heavy meals" containing large cqantities of fat and protein might be improved by letting the system consider meal effects for longer than the default six hours.
     , skip_neutral_temps: false // if true, don't set neutral temps
     , unsuspend_if_no_temp: false // if true, pump will un-suspend after a zero temp finishes
     , min_5m_carbimpact: 8 // mg/dL per 5m (8 mg/dL/5m corresponds to 24g/hr at a CSF of 4 mg/dL/g (x/5*60/4))
@@ -74,7 +75,7 @@ function defaults ( ) {
     , useNewFormula: false
     , enableDynamicCR: false
     , sigmoid: false
-    , weightPercentage: 0.65 
+    , weightPercentage: 0.65
     , tddAdjBasal: false // Enable adjustment of basal based on the ratio of 24 h : 10 day average TDD
     , threshold_setting: 60 // Use a configurable threshold setting
   }
@@ -111,6 +112,8 @@ function displayedDefaults () {
     profile.threshold_setting = allDefaults.threshold_setting;
     profile.enableSMB_high_bg = allDefaults.enableSMB_high_bg;
     profile.enableSMB_high_bg_target = allDefaults.enableSMB_high_bg_target;
+    profile.maxCOB = allDefaults.maxCOB;
+    profile.maxMealAbsorptionTime = allDefaults.maxMealAbsorptionTime;
 
     console.error(profile);
     return profile