Jelajahi Sumber

Fix input mutation with autosens glucose bucketing algorithm in testing JS

Sam King 4 bulan lalu
induk
melakukan
042205342a

+ 8 - 0
Trio.xcodeproj/project.pbxproj

@@ -328,6 +328,8 @@
 		3BCA5F7C2DC7B16400A7EAC7 /* pumphistory-with-external.json in Resources */ = {isa = PBXBuildFile; fileRef = 3BCA5F7B2DC7B15400A7EAC7 /* pumphistory-with-external.json */; };
 		3BCE75B32D4B38AE009E9453 /* InsulinSensitivities+Convert.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3BCE75B22D4B38A0009E9453 /* InsulinSensitivities+Convert.swift */; };
 		3BCE75B52D4B391F009E9453 /* Decimal+rounding.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3BCE75B42D4B3917009E9453 /* Decimal+rounding.swift */; };
+		3BD433AE2F01CDE600897F7D /* autosens-prepare-24.js in Resources */ = {isa = PBXBuildFile; fileRef = 3BD433AD2F01CDD900897F7D /* autosens-prepare-24.js */; };
+		3BD433B02F01CDF500897F7D /* autosens-prepare-8.js in Resources */ = {isa = PBXBuildFile; fileRef = 3BD433AF2F01CDEC00897F7D /* autosens-prepare-8.js */; };
 		3BD6CE262DC24CFD00FA0472 /* pumphistory-24h-zoned.json in Resources */ = {isa = PBXBuildFile; fileRef = 3BD6CE252DC24CFD00FA0472 /* pumphistory-24h-zoned.json */; };
 		3BD9687C2D8DDD4600899469 /* SlideButton in Frameworks */ = {isa = PBXBuildFile; productRef = 3BD9687B2D8DDD4600899469 /* SlideButton */; };
 		3BD9687F2D8DDD8800899469 /* CryptoSwift in Frameworks */ = {isa = PBXBuildFile; productRef = 3BD9687E2D8DDD8800899469 /* CryptoSwift */; };
@@ -1268,6 +1270,8 @@
 		3BCA5F7B2DC7B15400A7EAC7 /* pumphistory-with-external.json */ = {isa = PBXFileReference; lastKnownFileType = text.json; path = "pumphistory-with-external.json"; sourceTree = "<group>"; };
 		3BCE75B22D4B38A0009E9453 /* InsulinSensitivities+Convert.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "InsulinSensitivities+Convert.swift"; sourceTree = "<group>"; };
 		3BCE75B42D4B3917009E9453 /* Decimal+rounding.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Decimal+rounding.swift"; sourceTree = "<group>"; };
+		3BD433AD2F01CDD900897F7D /* autosens-prepare-24.js */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.javascript; path = "autosens-prepare-24.js"; sourceTree = "<group>"; };
+		3BD433AF2F01CDEC00897F7D /* autosens-prepare-8.js */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.javascript; path = "autosens-prepare-8.js"; sourceTree = "<group>"; };
 		3BD6CE252DC24CFD00FA0472 /* pumphistory-24h-zoned.json */ = {isa = PBXFileReference; lastKnownFileType = text.json; path = "pumphistory-24h-zoned.json"; sourceTree = "<group>"; };
 		3BDEA2DC60EDE0A3CA54DC73 /* TargetsEditorProvider.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = TargetsEditorProvider.swift; sourceTree = "<group>"; };
 		3BE2F1E72E030E2F009E2900 /* MealCobTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MealCobTests.swift; sourceTree = "<group>"; };
@@ -3051,6 +3055,8 @@
 			children = (
 				3B214EE92E29631F00046304 /* determine-basal-prepare.js */,
 				3B16C39B2DF75BCB00C5C801 /* autosens-prepare.js */,
+				3BD433AF2F01CDEC00897F7D /* autosens-prepare-8.js */,
+				3BD433AD2F01CDD900897F7D /* autosens-prepare-24.js */,
 				3BC0AA402DA8B8F7000DF7B7 /* iob-history-prepare.js */,
 				3BF92F212D86DEE9006B545A /* autosens.js */,
 				3BF92F222D86DEE9006B545A /* autotune-core.js */,
@@ -4491,6 +4497,7 @@
 				3BF92F2E2D86DEE9006B545A /* autotune-prep.js in Resources */,
 				3BF92F2F2D86DEE9006B545A /* profile.js in Resources */,
 				3BF92F302D86DEE9006B545A /* determine-basal.js in Resources */,
+				3BD433AE2F01CDE600897F7D /* autosens-prepare-24.js in Resources */,
 				3BF92F312D86DEE9006B545A /* meal.js in Resources */,
 				3BF92F322D86DEE9006B545A /* glucose-get-last.js in Resources */,
 				3BF92F332D86DEE9006B545A /* iob.js in Resources */,
@@ -4505,6 +4512,7 @@
 				3BD6CE262DC24CFD00FA0472 /* pumphistory-24h-zoned.json in Resources */,
 				DDD78A912DC4064800AC63F3 /* carbhistory.json in Resources */,
 				3B997DD32DC02AEF006B6BB2 /* glucose.json in Resources */,
+				3BD433B02F01CDF500897F7D /* autosens-prepare-8.js in Resources */,
 				DDD78AD92DC421B500AC63F3 /* enacted.json in Resources */,
 				3B8B5D402DF52D0E00365ED3 /* deviationsUnsorted.json in Resources */,
 				DD3C47B32DC5608A003DD20D /* newerSuggested.json in Resources */,

+ 5 - 1
Trio/Sources/APS/OpenAPSSwift/Autosens/AutosensGenerator.swift

@@ -115,7 +115,11 @@ struct AutosensGenerator {
                 iobActivity: iob.activity,
                 deltaGlucose: deltaGlucose,
                 deviation: deviation,
-                stateType: state.type.rawValue
+                stateType: state.type.rawValue,
+                mealCOB: state.mealCOB,
+                absorbing: state.absorbing,
+                mealCarbs: state.mealCarbs,
+                mealStartCounter: state.mealStartCounter
             ))
 
             if state.type == .nonMeal {

+ 5 - 0
Trio/Sources/Models/Autosens.swift

@@ -8,6 +8,11 @@ struct Autosens: JSON {
         let deltaGlucose: Decimal
         let deviation: Decimal
         let stateType: String
+        // COB state for debugging state transitions
+        var mealCOB: Decimal?
+        var absorbing: Bool?
+        var mealCarbs: Decimal?
+        var mealStartCounter: Int?
     }
 
     let ratio: Decimal

+ 76 - 4
TrioTests/OpenAPSSwiftTests/AutosensJsonTests.swift

@@ -25,7 +25,8 @@ import Testing
             profile: try JSONBridge.to(autosensInputs.profile),
             carbs: autosensInputs.carbs,
             temptargets: autosensInputs.tempTargets,
-            clock: autosensInputs.clock
+            clock: autosensInputs.clock,
+            prepareFile: OpenAPSFixed.prepare
         )
 
         let comparison = JSONCompare.createComparison(
@@ -158,7 +159,7 @@ import Testing
                 print("Skipping, known issue with JS and currently suspended pumps")
                 continue
             }
-            
+
             timeZoneForTests.setTimezone(identifier: algorithmComparison.timezone)
 
             try await checkFixedJsAgainstSwift(autosensInputs: autosensInputs)
@@ -238,7 +239,7 @@ import Testing
         timeZoneForTests.resetTimezone()
     }
 
-    @Test("Format autosens inputs for running in JS", .enabled(if: true)) func formatInputs() async throws {
+    @Test("Format autosens inputs for running in JS", .enabled(if: false)) func formatInputs() async throws {
         // this test is meant for one-off analysis so it's ok to hard code
         // a file, just make sure to _not_ check in updates to this to
         // avoid polluting our change logs
@@ -276,7 +277,8 @@ import Testing
             profile: try JSONBridge.to(autosensInputs.profile),
             carbs: autosensInputs.carbs,
             temptargets: autosensInputs.tempTargets,
-            clock: autosensInputs.clock
+            clock: autosensInputs.clock,
+            prepareFile: OpenAPSFixed.prepare
         )
 
         if case let .success(swiftJson) = autosensResultSwift, case let .success(jsJson) = autosensResultJavascript {
@@ -287,4 +289,74 @@ import Testing
 
         timeZoneForTests.resetTimezone()
     }
+
+    @Test(
+        "Format autosens inputs for running in JS, 24 hours only",
+        .enabled(if: false)
+    ) func formatInputsFixedTime() async throws {
+        // this test is meant for one-off analysis so it's ok to hard code
+        // a file, just make sure to _not_ check in updates to this to
+        // avoid polluting our change logs
+        let algorithmComparison = try await HttpFiles.downloadFile(at: "/files/2084152d-a95e-4d0e-9254-e0951f7aa519.0.json")
+        let autosensInputs = algorithmComparison.autosensInput!
+
+        // change these variables to switch between 24 and 8 hours
+        // 288 for 24 hours, 96 for 8 hours
+        let maxDeviations = 288
+        // OpenAPSFixed.prepare24 and OpenAPSFixed.prepare8
+        let prepareFile = OpenAPSFixed.prepare24
+
+        let encoder = JSONCoding.encoder
+        let output = try encoder.encode(autosensInputs)
+
+        let sharedDir = FileManager.default.temporaryDirectory
+        let outputURL = sharedDir.appendingPathComponent("autosens_error_inputs.json")
+        try output.write(to: outputURL)
+
+        // Print the path so you can find it
+        print("Writing to: \(outputURL.path)")
+
+        timeZoneForTests.setTimezone(identifier: algorithmComparison.timezone)
+
+        let openAps = OpenAPSFixed()
+
+        let glucose = try JSONBridge.glucose(from: autosensInputs.glucose)
+        let pumpHistory = try JSONBridge.pumpHistory(from: autosensInputs.history)
+        let basalProfile = try JSONBridge.basalProfile(from: autosensInputs.basalProfile)
+        let profile = autosensInputs.profile
+        let carbs = try JSONBridge.carbs(from: autosensInputs.carbs)
+        let tempTargets = try JSONBridge.tempTargets(from: autosensInputs.tempTargets)
+        let clock = autosensInputs.clock
+
+        let autosensResultSwift = try AutosensGenerator.generate(
+            glucose: glucose,
+            pumpHistory: pumpHistory,
+            basalProfile: basalProfile,
+            profile: profile,
+            carbs: carbs,
+            tempTargets: tempTargets,
+            maxDeviations: maxDeviations,
+            clock: clock,
+            includeDeviationsForTesting: true
+        )
+
+        let autosensResultJavascript = await openAps.autosenseJavascript(
+            glucose: autosensInputs.glucose,
+            pumpHistory: autosensInputs.history,
+            basalprofile: autosensInputs.basalProfile,
+            profile: try JSONBridge.to(autosensInputs.profile),
+            carbs: autosensInputs.carbs,
+            temptargets: autosensInputs.tempTargets,
+            clock: autosensInputs.clock,
+            prepareFile: prepareFile
+        )
+
+        if case let .success(jsJson) = autosensResultJavascript {
+            try compareDeviations(swiftJson: JSONBridge.to(autosensResultSwift), jsJson: jsJson)
+        }
+
+        try await checkFixedJsAgainstSwift(autosensInputs: autosensInputs)
+
+        timeZoneForTests.resetTimezone()
+    }
 }

+ 1 - 1
TrioTests/OpenAPSSwiftTests/IobJsonTests.swift

@@ -48,7 +48,7 @@ import Testing
         }
         return false
     }
-    
+
     // Note: This test case has a memory leak so limit your inputs
     // to about 250 files at a time
     @Test(

+ 32 - 0
TrioTests/OpenAPSSwiftTests/javascript/bundle/autosens-prepare-24.js

@@ -0,0 +1,32 @@
+// для settings/autosens.json параметры: monitor/glucose.json monitor/pumphistory-24h-zoned.json settings/basal_profile.json settings/profile.json monitor/carbhistory.json settings/temptargets.json
+
+function generate(glucose_data, pumphistory_data, basalprofile, profile_data, carb_data = {}, temptarget_data = {}, now = null) {
+    if (glucose_data.length < 72) {
+        return { "ratio": 1, "error": "not enough glucose data to calculate autosens" };
+    };
+    
+    if (now) {
+        now = new Date(now);
+    } else {
+        now = new Date();
+    }
+    
+    var iob_inputs = {
+        history: pumphistory_data,
+        profile: profile_data,
+        clock: now
+    };
+
+    var detection_inputs = {
+        iob_inputs: iob_inputs,
+        carbs: carb_data,
+        glucose_data: glucose_data,
+        basalprofile: basalprofile,
+        temptargets: temptarget_data
+    };
+    
+    // 24 hours only
+    detection_inputs.deviations = 288;
+    return trio_autosens(detection_inputs, now);
+}
+

+ 32 - 0
TrioTests/OpenAPSSwiftTests/javascript/bundle/autosens-prepare-8.js

@@ -0,0 +1,32 @@
+// для settings/autosens.json параметры: monitor/glucose.json monitor/pumphistory-24h-zoned.json settings/basal_profile.json settings/profile.json monitor/carbhistory.json settings/temptargets.json
+
+function generate(glucose_data, pumphistory_data, basalprofile, profile_data, carb_data = {}, temptarget_data = {}, now = null) {
+    if (glucose_data.length < 72) {
+        return { "ratio": 1, "error": "not enough glucose data to calculate autosens" };
+    };
+    
+    if (now) {
+        now = new Date(now);
+    } else {
+        now = new Date();
+    }
+    
+    var iob_inputs = {
+        history: pumphistory_data,
+        profile: profile_data,
+        clock: now
+    };
+
+    var detection_inputs = {
+        iob_inputs: iob_inputs,
+        carbs: carb_data,
+        glucose_data: glucose_data,
+        basalprofile: basalprofile,
+        temptargets: temptarget_data
+    };
+    
+    // 8 hours only
+    detection_inputs.deviations = 96;
+    return trio_autosens(detection_inputs, now);
+}
+

File diff ditekan karena terlalu besar
+ 1 - 1
TrioTests/OpenAPSSwiftTests/javascript/bundle/autosens.js


+ 7 - 2
TrioTests/OpenAPSSwiftTests/utils/OpenAPSFixed.swift

@@ -143,6 +143,10 @@ final class OpenAPSFixed {
         }
     }
 
+    static let prepare = "autosens-prepare.js"
+    static let prepare24 = "autosens-prepare-24.js"
+    static let prepare8 = "autosens-prepare-8.js"
+
     func autosenseJavascript(
         glucose: JSON,
         pumpHistory: JSON,
@@ -150,7 +154,8 @@ final class OpenAPSFixed {
         profile: JSON,
         carbs: JSON,
         temptargets: JSON,
-        clock: JSON
+        clock: JSON,
+        prepareFile: String
     ) async -> OrefFunctionResult {
         do {
             let result = try await withCheckedThrowingContinuation { continuation in
@@ -160,7 +165,7 @@ final class OpenAPSFixed {
                     worker.evaluateBatch(scripts: [
                         Script(name: "prepare/log.js"),
                         Script.fromTestingBundle(name: "autosens.js", bundle: testBundle),
-                        Script.fromTestingBundle(name: "autosens-prepare.js", bundle: testBundle)
+                        Script.fromTestingBundle(name: prepareFile, bundle: testBundle)
                     ])
                     let result = worker.call(function: "generate", with: [
                         glucose,