Explorar el Código

Adding the files from PR #617 which were lost during GitHub merge

This PR adds a missing error check to our swift implementation of meal
and updates our fuzzy matching logic. In particular:
- Add error check for meal when glucose.count < 4 to match JS
- Remove slopeFrom(Min/Max)Deviation from fuzzy matching logic for meal

It also includes an updated testing meal.js file that avoids an
exception when there aren't any glucose readings.

Note: we removed slopeFrom(Min/Max)Deviation from our fuzzy matching
logic because small rounding errors can make big differences in a few
rare cases. In our testing of 133k meal inputs we have 10 cases where
this type of error occurs but were within the fuzzy matching
thresholds for the rest.
Sam King hace 4 meses
padre
commit
551b55c8c9

+ 4 - 0
Trio.xcodeproj/project.pbxproj

@@ -319,6 +319,7 @@
 		3BAE87702E480BE100FCA8D2 /* ForecastResults.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3BAE876F2E480BE100FCA8D2 /* ForecastResults.swift */; };
 		3BBB76AA2E01C70B0040977D /* MealCob.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3BBB76A92E01C7070040977D /* MealCob.swift */; };
 		3BBC22632DF5B94100169236 /* AutosensTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3BBC22622DF5B93900169236 /* AutosensTests.swift */; };
+		3BBE323C2F12AB22005F9665 /* meal-prepare.js in Resources */ = {isa = PBXBuildFile; fileRef = 3BBE323B2F12AB22005F9665 /* meal-prepare.js */; };
 		3BC0AA3B2DA74C87000DF7B7 /* iob-total.js in Resources */ = {isa = PBXBuildFile; fileRef = 3BC0AA3A2DA74C87000DF7B7 /* iob-total.js */; };
 		3BC0AA3E2DA817EC000DF7B7 /* iob-calculate.js in Resources */ = {isa = PBXBuildFile; fileRef = 3BC0AA3C2DA817EC000DF7B7 /* iob-calculate.js */; };
 		3BC0AA3F2DA817EC000DF7B7 /* iob-history.js in Resources */ = {isa = PBXBuildFile; fileRef = 3BC0AA3D2DA817EC000DF7B7 /* iob-history.js */; };
@@ -1261,6 +1262,7 @@
 		3BAE876F2E480BE100FCA8D2 /* ForecastResults.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ForecastResults.swift; sourceTree = "<group>"; };
 		3BBB76A92E01C7070040977D /* MealCob.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MealCob.swift; sourceTree = "<group>"; };
 		3BBC22622DF5B93900169236 /* AutosensTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AutosensTests.swift; sourceTree = "<group>"; };
+		3BBE323B2F12AB22005F9665 /* meal-prepare.js */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.javascript; path = "meal-prepare.js"; sourceTree = "<group>"; };
 		3BC0AA3A2DA74C87000DF7B7 /* iob-total.js */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.javascript; path = "iob-total.js"; sourceTree = "<group>"; };
 		3BC0AA3C2DA817EC000DF7B7 /* iob-calculate.js */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.javascript; path = "iob-calculate.js"; sourceTree = "<group>"; };
 		3BC0AA3D2DA817EC000DF7B7 /* iob-history.js */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.javascript; path = "iob-history.js"; sourceTree = "<group>"; };
@@ -3069,6 +3071,7 @@
 				3BC0AA3D2DA817EC000DF7B7 /* iob-history.js */,
 				3BC0AA3A2DA74C87000DF7B7 /* iob-total.js */,
 				3BF92F292D86DEE9006B545A /* meal.js */,
+				3BBE323B2F12AB22005F9665 /* meal-prepare.js */,
 				3BF92F2A2D86DEE9006B545A /* profile.js */,
 			);
 			path = bundle;
@@ -4495,6 +4498,7 @@
 			files = (
 				3BF92F2D2D86DEE9006B545A /* autosens.js in Resources */,
 				3BF92F2E2D86DEE9006B545A /* autotune-prep.js in Resources */,
+				3BBE323C2F12AB22005F9665 /* meal-prepare.js in Resources */,
 				3BF92F2F2D86DEE9006B545A /* profile.js in Resources */,
 				3BF92F302D86DEE9006B545A /* determine-basal.js in Resources */,
 				3BD433AE2F01CDE600897F7D /* autosens-prepare-24.js in Resources */,

+ 15 - 3
Trio/Sources/APS/OpenAPSSwift/Logging/OrefFunction.swift

@@ -39,7 +39,21 @@ enum OrefFunction: String, Codable {
         case .meal:
             // These aren't used by downstream calculations, so we
             // can ignore them in our comparison
-            return Set(["maxDeviation", "minDeviation", "allDeviations", "bwCarbs", "bwFound", "journalCarbs", "nsCarbs"])
+            // However, slopeFromMaxDeviation and slopeFromMinDeviation
+            // are used but we had to include them here because small
+            // changes in iob.activity can lead to large changes in these
+            // values in rare cases
+            return Set([
+                "maxDeviation",
+                "minDeviation",
+                "allDeviations",
+                "bwCarbs",
+                "bwFound",
+                "journalCarbs",
+                "nsCarbs",
+                "slopeFromMaxDeviation",
+                "slopeFromMinDeviation"
+            ])
         case .autosens:
             return Set(["deviationsUnsorted", "debugInfo"])
         case .determineBasal:
@@ -91,8 +105,6 @@ enum OrefFunction: String, Codable {
                 "carbs": 0.1,
                 "mealCOB": 10,
                 "currentDeviation": 1,
-                "slopeFromMaxDeviation": 0.25,
-                "slopeFromMinDeviation": 0.25,
                 "lastCarbTime": 1
             ]
         case .autosens:

+ 18 - 1
Trio/Sources/APS/OpenAPSSwift/Meal/MealGenerator.swift

@@ -11,7 +11,7 @@ enum MealGenerator {
     ) throws -> ComputedCarbs? {
         let treatments: [MealInput] = MealHistory.findMealInputs(pumpHistory: pumpHistory, carbHistory: carbHistory)
 
-        return try MealTotal.recentCarbs(
+        let recentCarbs = try MealTotal.recentCarbs(
             treatments: treatments,
             pumpHistory: pumpHistory,
             profile: profile,
@@ -19,5 +19,22 @@ enum MealGenerator {
             glucose: glucoseHistory,
             time: clock
         )
+
+        // copy the glucose check from prepare/meal.js in Trio
+        guard glucoseHistory.count >= 4 else {
+            return ComputedCarbs(
+                carbs: 0,
+                mealCOB: 0,
+                currentDeviation: 0,
+                maxDeviation: 0,
+                minDeviation: 0,
+                slopeFromMaxDeviation: 0,
+                slopeFromMinDeviation: 0,
+                allDeviations: [],
+                lastCarbTime: 0
+            )
+        }
+
+        return recentCarbs
     }
 }

+ 14 - 0
TrioTests/OpenAPSSwiftTests/MealJsonTests.swift

@@ -146,6 +146,20 @@ import Testing
             print(error.localizedDescription)
         }
 
+        let comparison = JSONCompare.createComparison(
+            function: .meal,
+            swift: mealResultSwift,
+            swiftDuration: 0.1,
+            javascript: mealResultJavascript,
+            javascriptDuration: 0.1,
+            iobInputs: nil,
+            mealInputs: nil,
+            autosensInputs: nil,
+            determineBasalInputs: nil
+        )
+
+        #expect(comparison.resultType == .matching)
+
         timeZoneForTests.resetTimezone()
     }
 }

+ 51 - 0
TrioTests/OpenAPSSwiftTests/javascript/bundle/meal-prepare.js

@@ -0,0 +1,51 @@
+//для monitor/meal.json параметры: monitor/pumphistory-24h-zoned.json settings/profile.json monitor/clock-zoned.json monitor/glucose.json settings/basal_profile.json monitor/carbhistory.json
+
+function generate(pumphistory_data, profile_data, clock_data, glucose_data, basalprofile_data, carbhistory = false) {
+    if (typeof(profile_data.carb_ratio) === 'undefined' || profile_data.carb_ratio < 0.1) {
+        return {"error":"Error: carb_ratio " + profile_data.carb_ratio + " out of bounds"};
+    }
+
+    var carb_data = { };
+    if (carbhistory) {
+        carb_data = carbhistory;
+    }
+
+    if (typeof basalprofile_data[0] === 'undefined') {
+        return { "error":"Error: bad basalprofile_data: " + JSON.stringify(basalprofile_data) };
+    }
+
+    var inputs = {
+      history: pumphistory_data
+    , profile: profile_data
+    , basalprofile: basalprofile_data
+    , clock: clock_data
+    , carbs: carb_data
+    , glucose: glucose_data
+    };
+
+    var recentCarbs = trio_meal(inputs);
+
+    if (glucose_data.length < 4) {
+        console.error("Not enough glucose data to calculate carb absorption; found:", glucose_data.length);
+        
+        // We're returning a sensable default to match Swift
+        /*
+        recentCarbs.mealCOB = 0;
+        recentCarbs.reason = "not enough glucose data to calculate carb absorption";
+         */
+        return {
+            carbs: 0,
+            mealCOB: 0,
+            currentDeviation: 0,
+            maxDeviation: 0,
+            minDeviation: 0,
+            slopeFromMaxDeviation: 0,
+            slopeFromMinDeviation: 0,
+            allDeviations: [],
+            lastCarbTime: 0
+        };
+    }
+
+    return recentCarbs;
+}
+

+ 1 - 1
TrioTests/OpenAPSSwiftTests/utils/OpenAPSFixed.swift

@@ -124,7 +124,7 @@ final class OpenAPSFixed {
                     worker.evaluateBatch(scripts: [
                         Script(name: "prepare/log.js"),
                         Script.fromTestingBundle(name: "meal.js", bundle: testBundle),
-                        Script(name: "prepare/meal.js")
+                        Script.fromTestingBundle(name: "meal-prepare.js", bundle: testBundle)
                     ])
                     let result = worker.call(function: "generate", with: [
                         pumphistory,