Преглед на файлове

Add logging for the oref-swift meal call

Sam King преди 11 месеца
родител
ревизия
900eb8fa08

+ 66 - 21
Trio/Sources/APS/OpenAPS/OpenAPS.swift

@@ -332,7 +332,8 @@ final class OpenAPS {
             basalProfile: basalProfile,
             clock: clock,
             carbs: carbsAsJSON,
-            glucose: glucoseAsJSON
+            glucose: glucoseAsJSON,
+            useSwiftOref: useSwiftOref
         )
 
         // IOB calculation
@@ -659,11 +660,50 @@ final class OpenAPS {
         basalProfile: JSON,
         clock: JSON,
         carbs: JSON,
-        glucose: JSON
+        glucose: JSON,
+        useSwiftOref: Bool
     ) async throws -> RawJSON {
-        
+        let startJavascriptAt = Date()
+        let jsResult = await mealJavascript(
+            pumphistory: pumphistory,
+            profile: profile,
+            basalProfile: basalProfile,
+            clock: clock,
+            carbs: carbs,
+            glucose: glucose
+        )
+        let javascriptDuration = Date().timeIntervalSince(startJavascriptAt)
+
+        // Important: we want to make sure that this flag ensures that none
+        // of the native code runs
+        guard useSwiftOref else {
+            return try jsResult.returnOrThrow()
+        }
+
+        let startSwiftAt = Date()
+        let (swiftResult, mealInputs) = OpenAPSSwift
+            .meal(
+                pumphistory: pumphistory,
+                profile: profile,
+                basalProfile: basalProfile,
+                clock: clock,
+                carbs: carbs,
+                glucose: glucose
+            )
+        let swiftDuration = Date().timeIntervalSince(startSwiftAt)
+
+        JSONCompare.logDifferences(
+            function: .meal,
+            swift: swiftResult,
+            swiftDuration: swiftDuration,
+            javascript: jsResult,
+            javascriptDuration: javascriptDuration,
+            mealInputs: mealInputs
+        )
+
+        return try jsResult.returnOrThrow()
     }
-    
+
     private func mealJavascript(
         pumphistory: JSON,
         profile: JSON,
@@ -671,24 +711,29 @@ final class OpenAPS {
         clock: JSON,
         carbs: JSON,
         glucose: JSON
-    ) async throws -> RawJSON {
-        try await withCheckedThrowingContinuation { continuation in
-            jsWorker.inCommonContext { worker in
-                worker.evaluateBatch(scripts: [
-                    Script(name: Prepare.log),
-                    Script(name: Bundle.meal),
-                    Script(name: Prepare.meal)
-                ])
-                let result = worker.call(function: Function.generate, with: [
-                    pumphistory,
-                    profile,
-                    clock,
-                    glucose,
-                    basalProfile,
-                    carbs
-                ])
-                continuation.resume(returning: result)
+    ) async -> OrefFunctionResult {
+        do {
+            let result = try await withCheckedThrowingContinuation { continuation in
+                jsWorker.inCommonContext { worker in
+                    worker.evaluateBatch(scripts: [
+                        Script(name: Prepare.log),
+                        Script(name: Bundle.meal),
+                        Script(name: Prepare.meal)
+                    ])
+                    let result = worker.call(function: Function.generate, with: [
+                        pumphistory,
+                        profile,
+                        clock,
+                        glucose,
+                        basalProfile,
+                        carbs
+                    ])
+                    continuation.resume(returning: result)
+                }
             }
+            return .success(result)
+        } catch {
+            return .failure(error)
         }
     }
 

+ 5 - 1
Trio/Sources/APS/OpenAPSSwift/JSONBridge.swift

@@ -44,10 +44,14 @@ enum JSONBridge {
         try JSONBridge.from(string: from.rawJSON)
     }
 
+    static func glucose(from: JSON) throws -> [BloodGlucose] {
+        try JSONBridge.from(string: from.rawJSON)
+    }
+
     static func carbs(from: JSON) throws -> [CarbsEntry] {
         try JSONBridge.from(string: from.rawJSON)
     }
-    
+
     static func pumpHistory(from: JSON) throws -> [PumpHistoryEvent] {
         do {
             return try JSONBridge.from(string: from.rawJSON)

+ 4 - 1
Trio/Sources/APS/OpenAPSSwift/Logging/AlgorithmComparison.swift

@@ -106,6 +106,7 @@ struct AlgorithmComparison: Codable {
 
     // Inputs for mismatches
     let iobInput: IobInputs?
+    let mealInput: MealInputs?
 
     init(
         function: OrefFunction,
@@ -117,6 +118,7 @@ struct AlgorithmComparison: Codable {
         swiftException: AlgorithmException? = nil,
         comparisonError: AlgorithmException? = nil,
         iobInputs: IobInputs? = nil,
+        mealInputs: MealInputs? = nil,
         id: UUID = UUID(),
         createdAt: Date = Date()
     ) {
@@ -131,8 +133,9 @@ struct AlgorithmComparison: Codable {
         self.swiftException = swiftException
         self.comparisonError = comparisonError
         iobInput = iobInputs
+        mealInput = mealInputs
         timezone = TimeZone.current.identifier
-        version = "2"
+        version = "3"
 
         #if targetEnvironment(simulator)
             isSimulator = true

+ 12 - 6
Trio/Sources/APS/OpenAPSSwift/Logging/JSONCompare.swift

@@ -84,7 +84,8 @@ enum JSONCompare {
         swiftDuration: TimeInterval,
         javascript: OrefFunctionResult,
         javascriptDuration: TimeInterval,
-        iobInputs: IobInputs? = nil
+        iobInputs: IobInputs? = nil,
+        mealInputs: MealInputs? = nil
     ) {
         let comparison = createComparison(
             function: function,
@@ -92,7 +93,8 @@ enum JSONCompare {
             swiftDuration: swiftDuration,
             javascript: javascript,
             javascriptDuration: javascriptDuration,
-            iobInputs: iobInputs
+            iobInputs: iobInputs,
+            mealInputs: mealInputs
         )
 
         Task {
@@ -110,7 +112,8 @@ enum JSONCompare {
         swiftDuration: TimeInterval,
         javascript: OrefFunctionResult,
         javascriptDuration: TimeInterval,
-        iobInputs: IobInputs?
+        iobInputs: IobInputs?,
+        mealInputs: MealInputs?
     ) -> AlgorithmComparison {
         switch (swift, javascript) {
         case let (.success(swiftJson), .success(javascriptJson)):
@@ -123,7 +126,8 @@ enum JSONCompare {
                     jsDuration: javascriptDuration,
                     swiftDuration: swiftDuration,
                     differences: differences.isEmpty ? nil : differences,
-                    iobInputs: differences.isEmpty ? nil : iobInputs
+                    iobInputs: differences.isEmpty ? nil : iobInputs,
+                    mealInputs: differences.isEmpty ? nil : mealInputs
                 )
             } catch {
                 return AlgorithmComparison(
@@ -149,7 +153,8 @@ enum JSONCompare {
                 resultType: .swiftOnlyException,
                 jsDuration: javascriptDuration,
                 swiftException: AlgorithmException(error: swiftError),
-                iobInputs: iobInputs
+                iobInputs: iobInputs,
+                mealInputs: mealInputs
             )
 
         case let (.success, .failure(jsError)):
@@ -158,7 +163,8 @@ enum JSONCompare {
                 resultType: .jsOnlyException,
                 swiftDuration: swiftDuration,
                 jsException: AlgorithmException(error: jsError),
-                iobInputs: iobInputs
+                iobInputs: iobInputs,
+                mealInputs: mealInputs
             )
         }
     }

+ 16 - 0
Trio/Sources/APS/OpenAPSSwift/Logging/OrefFunction.swift

@@ -23,6 +23,7 @@ enum OrefFunction: String, Codable {
 
     case makeProfile
     case iob
+    case meal
 
     // since we're removing some keys from our Profile that exist in Javascript
     // we need to let the difference function know which keys to ignore when
@@ -34,6 +35,10 @@ enum OrefFunction: String, Codable {
         case .iob:
             // we're only checking the first result for now
             return Set(stride(from: 1, to: 48, by: 1).map { String("[\($0)]") })
+        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"])
         }
     }
 
@@ -57,6 +62,15 @@ enum OrefFunction: String, Codable {
                 // https://github.com/nightscout/Trio-dev/issues/453
                 "duration": 120
             ]
+        case .meal:
+            return [
+                "carbs": 0.1,
+                "mealCOB": 10,
+                "currentDeviation": 1,
+                "slopeFromMaxDeviation": 0.25,
+                "slopeFromMinDeviation": 0.25,
+                "lastCarbTime": 1
+            ]
         }
     }
 
@@ -66,6 +80,8 @@ enum OrefFunction: String, Codable {
             return .dictionary
         case .iob:
             return .array
+        case .meal:
+            return .dictionary
         }
     }
 }

+ 2 - 1
Trio/Sources/APS/OpenAPSSwift/Meal/MealCob.swift

@@ -192,8 +192,9 @@ struct MealCob {
                 }
             } else if let carbImpactDate = carbImpactDate, carbImpactDate > glucoseTime {
                 let avgDeviation = ((avgDelta - glucoseImpact) * 1000).rounded() / 1000
+                // we remove the * 1000 because we're already using seconds, not ms
                 let deviationSlope = (avgDeviation - currentDeviation) / Decimal(glucoseTime.timeIntervalSince(carbImpactDate)) *
-                    1000 * 60 * 5
+                    60 * 5
 
                 if avgDeviation > maxDeviation {
                     slopeFromMaxDeviation = min(0, deviationSlope)

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

@@ -1,6 +1,6 @@
 import Foundation
 
-enum MealGeneratorError {
+enum MealGenerator {
     static func generate(
         pumpHistory: [PumpHistoryEvent],
         profile: Profile,
@@ -11,8 +11,6 @@ enum MealGeneratorError {
     ) throws -> ComputedCarbs? {
         let treatments: [MealInput] = MealHistory.findMealInputs(pumpHistory: pumpHistory, carbHistory: carbHistory)
 
-        // TODO: do we need to handle the clock timezone handling? We'll parse in a proper Swift Date anyhow
-
         return try MealTotal.recentCarbs(
             treatments: treatments,
             pumpHistory: pumpHistory,

+ 1 - 1
Trio/Sources/APS/OpenAPSSwift/Meal/MealTotal.swift

@@ -165,7 +165,7 @@ enum MealTotal {
             slopeFromMaxDeviation: finalCobResult.slopeFromMaxDeviation.rounded(scale: 3),
             slopeFromMinDeviation: finalCobResult.slopeFromMinDeviation.rounded(scale: 3),
             allDeviations: finalCobResult.allDeviations,
-            lastCarbTime: lastCarbTime
+            lastCarbTime: (lastCarbTime * 1000).rounded()
         )
     }
 }

+ 20 - 11
Trio/Sources/APS/OpenAPSSwift/OpenAPSSwift.swift

@@ -48,32 +48,41 @@ struct OpenAPSSwift {
         clock: JSON,
         carbs: JSON,
         glucose: JSON
-    ) async throws -> (OrefFunctionResult, MealInputs?) {
+    ) -> (OrefFunctionResult, MealInputs?) {
         var mealInputs: MealInputs?
 
         do {
             let pumpHistory = try JSONBridge.pumpHistory(from: pumphistory)
             let profile = try JSONBridge.profile(from: profile)
-            let basalprofile = try JSONBridge.basalProfile(from: basalProfile)
+            let basalProfile = try JSONBridge.basalProfile(from: basalProfile)
             let clock = try JSONBridge.clock(from: clock)
             let carbs = try JSONBridge.carbs(from: carbs)
-            let glucose = try JSONBridge.gluc
-            
-            iobInputs = IobInputs(history: pumpHistory, profile: profile, clock: clock, autosens: autosens)
+            let glucose = try JSONBridge.glucose(from: glucose)
 
-            let iobResult = try IobGenerator.generate(
-                history: pumpHistory,
+            mealInputs = MealInputs(
+                pumpHistory: pumpHistory,
                 profile: profile,
+                basalProfile: basalProfile,
                 clock: clock,
-                autosens: autosens
+                carbs: carbs,
+                glucose: glucose
             )
 
-            return try (.success(JSONBridge.to(iobResult)), iobInputs)
+            let mealResult = try MealGenerator.generate(
+                pumpHistory: pumpHistory,
+                profile: profile,
+                basalProfile: basalProfile,
+                clock: clock,
+                carbHistory: carbs,
+                glucoseHistory: glucose
+            )
+
+            return try (.success(JSONBridge.to(mealResult)), mealInputs)
         } catch {
-            return (.failure(error), iobInputs)
+            return (.failure(error), mealInputs)
         }
     }
-    
+
     static func iob(pumphistory: JSON, profile: JSON, clock: JSON, autosens: JSON) -> (OrefFunctionResult, IobInputs?) {
         var iobInputs: IobInputs?
 

+ 4 - 2
TrioTests/OpenAPSSwiftTests/IobJsonTests.swift

@@ -95,7 +95,8 @@ import Testing
             swiftDuration: 0.1,
             javascript: iobResultJavascript,
             javascriptDuration: 0.1,
-            iobInputs: nil
+            iobInputs: nil,
+            mealInputs: nil
         )
 
         if comparison.resultType == .valueDifference {
@@ -131,7 +132,8 @@ import Testing
             swiftDuration: 0.1,
             javascript: iobResultJavascript,
             javascriptDuration: 0.1,
-            iobInputs: nil
+            iobInputs: nil,
+            mealInputs: nil
         )
 
         if comparison.resultType != .valueDifference {

+ 3 - 1
TrioTests/OpenAPSSwiftTests/MealJsonTests.swift

@@ -26,7 +26,7 @@ import Testing
         jsonData = (jsonInputs["meal"] as! String).data(using: .utf8)!
         let mealResultFromJs = try decoder.decode(ComputedCarbs.self, from: jsonData)
 
-        let mealResult = try MealGeneratorError.generate(
+        let mealResult = try MealGenerator.generate(
             pumpHistory: pumpHistory,
             profile: profile,
             basalProfile: basalProfile,
@@ -42,6 +42,8 @@ import Testing
         // Ignore this check due to Issue 539
         // #expect(mealResult?.allDeviations == mealResultFromJs.allDeviations)
         #expect(mealResult?.maxDeviation == mealResultFromJs.maxDeviation)
+        #expect(mealResult?.slopeFromMaxDeviation == mealResultFromJs.slopeFromMaxDeviation)
         #expect(mealResult?.minDeviation == mealResultFromJs.minDeviation)
+        #expect(mealResult!.slopeFromMinDeviation.isWithin(0.01, of: mealResultFromJs.slopeFromMinDeviation))
     }
 }

+ 2 - 1
TrioTests/OpenAPSSwiftTests/ProfileJavascriptTests.swift

@@ -327,7 +327,8 @@ struct ProfileGeneratorTests {
             swiftDuration: 1.0,
             javascript: jsResult,
             javascriptDuration: 1.0,
-            iobInputs: nil
+            iobInputs: nil,
+            mealInputs: nil
         )
 
         if comparison.resultType == .valueDifference {

+ 14 - 7
TrioTests/OpenAPSSwiftTests/ProfileJsNativeCompareTests.swift

@@ -95,7 +95,8 @@ import Testing
             swiftDuration: 0.1,
             javascript: profileJs,
             javascriptDuration: 0.1,
-            iobInputs: nil
+            iobInputs: nil,
+            mealInputs: nil
         )
 
         #expect(comparison.resultType == .matching)
@@ -125,7 +126,8 @@ import Testing
             swiftDuration: 0.1,
             javascript: .success(matchingJSON),
             javascriptDuration: 0.2,
-            iobInputs: nil
+            iobInputs: nil,
+            mealInputs: nil
         )
 
         #expect(comparison.resultType == .matching)
@@ -144,7 +146,8 @@ import Testing
             swiftDuration: 0.1,
             javascript: .success(matchingJSON),
             javascriptDuration: 0.2,
-            iobInputs: nil
+            iobInputs: nil,
+            mealInputs: nil
         )
 
         #expect(comparison.resultType == .valueDifference)
@@ -165,7 +168,8 @@ import Testing
             swiftDuration: 0.1,
             javascript: .failure(error),
             javascriptDuration: 0.2,
-            iobInputs: nil
+            iobInputs: nil,
+            mealInputs: nil
         )
 
         #expect(comparison.resultType == .matchingExceptions)
@@ -183,7 +187,8 @@ import Testing
             swiftDuration: 0.1,
             javascript: .success(matchingJSON),
             javascriptDuration: 0.2,
-            iobInputs: nil
+            iobInputs: nil,
+            mealInputs: nil
         )
 
         #expect(comparison.resultType == .swiftOnlyException)
@@ -203,7 +208,8 @@ import Testing
             swiftDuration: 0.1,
             javascript: .failure(error),
             javascriptDuration: 0.2,
-            iobInputs: nil
+            iobInputs: nil,
+            mealInputs: nil
         )
 
         #expect(comparison.resultType == .jsOnlyException)
@@ -222,7 +228,8 @@ import Testing
             swiftDuration: 0.1,
             javascript: .success(matchingJSON),
             javascriptDuration: 0.2,
-            iobInputs: nil
+            iobInputs: nil,
+            mealInputs: nil
         )
 
         #expect(comparison.resultType == .comparisonError)