ソースを参照

Merge branch 'oref-swift' of github.com:nightscout/Trio-dev into oref-swift

Deniz Cengiz 3 ヶ月 前
コミット
0eed262531
26 ファイル変更403 行追加67 行削除
  1. 8 0
      Trio.xcodeproj/project.pbxproj
  2. 12 0
      Trio/Resources/InfoPlist.xcstrings
  3. 12 6
      Trio/Sources/APS/OpenAPS/OpenAPS.swift
  4. 17 0
      Trio/Sources/APS/OpenAPSSwift/Logging/AlgorithmComparison.swift
  5. 14 7
      Trio/Sources/APS/OpenAPSSwift/Logging/JSONCompare.swift
  6. 21 5
      Trio/Sources/APS/OpenAPSSwift/OpenAPSSwift.swift
  7. 2 2
      Trio/Sources/APS/OpenAPSSwift/Profile/Basal.swift
  8. 1 1
      Trio/Sources/APS/OpenAPSSwift/Profile/Carbs.swift
  9. 2 2
      Trio/Sources/APS/OpenAPSSwift/Profile/Isf.swift
  10. 12 9
      Trio/Sources/APS/OpenAPSSwift/Profile/ProfileGenerator.swift
  11. 1 1
      Trio/Sources/APS/OpenAPSSwift/Profile/Targets.swift
  12. 0 1
      Trio/Sources/Localizations/Main/Localizable.xcstrings
  13. 2 1
      TrioTests/OpenAPSSwiftTests/AutosensJsonTests.swift
  14. 4 2
      TrioTests/OpenAPSSwiftTests/DetermineBasalJsonTests.swift
  15. 4 2
      TrioTests/OpenAPSSwiftTests/IobJsonTests.swift
  16. 4 2
      TrioTests/OpenAPSSwiftTests/MealJsonTests.swift
  17. 3 3
      TrioTests/OpenAPSSwiftTests/ProfileBasalTests.swift
  18. 1 1
      TrioTests/OpenAPSSwiftTests/ProfileCarbsTests.swift
  19. 1 1
      TrioTests/OpenAPSSwiftTests/ProfileIsfTests.swift
  20. 12 10
      TrioTests/OpenAPSSwiftTests/ProfileJavascriptTests.swift
  21. 17 9
      TrioTests/OpenAPSSwiftTests/ProfileJsNativeCompareTests.swift
  22. 93 0
      TrioTests/OpenAPSSwiftTests/ProfileJsonTests.swift
  23. 1 1
      TrioTests/OpenAPSSwiftTests/ProfileTargetsTests.swift
  24. 113 0
      TrioTests/OpenAPSSwiftTests/javascript/bundle/profile-prepare.js
  25. 1 1
      TrioTests/OpenAPSSwiftTests/javascript/bundle/profile.js
  26. 45 0
      TrioTests/OpenAPSSwiftTests/utils/OpenAPSFixed.swift

+ 8 - 0
Trio.xcodeproj/project.pbxproj

@@ -319,6 +319,8 @@
 		3BC26E552D7418830066ACD6 /* IobSuspendTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3BC26E542D7418830066ACD6 /* IobSuspendTests.swift */; };
 		3BC4053B2D931620006A03E9 /* IobJsonTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3BC4053A2D931620006A03E9 /* IobJsonTests.swift */; };
 		3BCA5F7C2DC7B16400A7EAC7 /* pumphistory-with-external.json in Resources */ = {isa = PBXBuildFile; fileRef = 3BCA5F7B2DC7B15400A7EAC7 /* pumphistory-with-external.json */; };
+		3BCDDD312F219AF900496A94 /* ProfileJsonTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3BCDDD302F219AF300496A94 /* ProfileJsonTests.swift */; };
+		3BCDDD772F21A5F300496A94 /* profile-prepare.js in Resources */ = {isa = PBXBuildFile; fileRef = 3BCDDD762F21A5F300496A94 /* profile-prepare.js */; };
 		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 */; };
@@ -1260,6 +1262,8 @@
 		3BC26E542D7418830066ACD6 /* IobSuspendTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IobSuspendTests.swift; sourceTree = "<group>"; };
 		3BC4053A2D931620006A03E9 /* IobJsonTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IobJsonTests.swift; sourceTree = "<group>"; };
 		3BCA5F7B2DC7B15400A7EAC7 /* pumphistory-with-external.json */ = {isa = PBXFileReference; lastKnownFileType = text.json; path = "pumphistory-with-external.json"; sourceTree = "<group>"; };
+		3BCDDD302F219AF300496A94 /* ProfileJsonTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProfileJsonTests.swift; sourceTree = "<group>"; };
+		3BCDDD762F21A5F300496A94 /* profile-prepare.js */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.javascript; path = "profile-prepare.js"; 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>"; };
@@ -3003,6 +3007,7 @@
 				3B5CD2C12D4AECD500CE213C /* ProfileBasalTests.swift */,
 				3B5CD2C32D4AECD500CE213C /* ProfileCarbsTests.swift */,
 				3B5CD2C42D4AECD500CE213C /* ProfileIsfTests.swift */,
+				3BCDDD302F219AF300496A94 /* ProfileJsonTests.swift */,
 				3B5CD2C52D4AECD500CE213C /* ProfileJavascriptTests.swift */,
 				3B5CD2C62D4AECD500CE213C /* ProfileTargetsTests.swift */,
 				3BAC92CC2E57859600B853DA /* SetTempBasalTests.swift */,
@@ -3056,6 +3061,7 @@
 				3BF92F292D86DEE9006B545A /* meal.js */,
 				3BBE323B2F12AB22005F9665 /* meal-prepare.js */,
 				3BF92F2A2D86DEE9006B545A /* profile.js */,
+				3BCDDD762F21A5F300496A94 /* profile-prepare.js */,
 			);
 			path = bundle;
 			sourceTree = "<group>";
@@ -4518,6 +4524,7 @@
 				3BD6CE262DC24CFD00FA0472 /* pumphistory-24h-zoned.json in Resources */,
 				DDD78A912DC4064800AC63F3 /* carbhistory.json in Resources */,
 				3B997DD32DC02AEF006B6BB2 /* glucose.json in Resources */,
+				3BCDDD772F21A5F300496A94 /* profile-prepare.js in Resources */,
 				3BD433B02F01CDF500897F7D /* autosens-prepare-8.js in Resources */,
 				DDD78AD92DC421B500AC63F3 /* enacted.json in Resources */,
 				DD3C47B32DC5608A003DD20D /* newerSuggested.json in Resources */,
@@ -5314,6 +5321,7 @@
 				3B8B5D3C2DF523C000365ED3 /* AutosensJsonTests.swift in Sources */,
 				3B2A3BC32E2B19F700658FB9 /* DynamicISFTests.swift in Sources */,
 				BD8FC0662D661A0000B95AED /* GlucoseStorageTests.swift in Sources */,
+				3BCDDD312F219AF900496A94 /* ProfileJsonTests.swift in Sources */,
 				BD8FC05B2D6618AF00B95AED /* DeterminationStorageTests.swift in Sources */,
 				3BAAE60C2DE7766C0049589B /* DynamicISFEnableTests.swift in Sources */,
 				3BAC929B2E55FF5300B853DA /* DetermineBasalEnableSmbTests.swift in Sources */,

+ 12 - 0
Trio/Resources/InfoPlist.xcstrings

@@ -457,6 +457,18 @@
         }
       }
     },
+    "NSCalendarsFullAccessUsageDescription" : {
+      "comment" : "Privacy - Calendars Full Access Usage Description",
+      "extractionState" : "extracted_with_value",
+      "localizations" : {
+        "en" : {
+          "stringUnit" : {
+            "state" : "new",
+            "value" : "To create events with BG reading values, so that they can be viewed on Apple Watch and CarPlay"
+          }
+        }
+      }
+    },
     "NSCalendarsUsageDescription" : {
       "comment" : "Privacy - Calendars Usage Description",
       "extractionState" : "extracted_with_value",

+ 12 - 6
Trio/Sources/APS/OpenAPS/OpenAPS.swift

@@ -570,6 +570,7 @@ final class OpenAPS {
             }
         }
 
+        let clock = Date()
         do {
             let pumpProfile = try await makeProfile(
                 preferences: adjustedPreferences,
@@ -582,7 +583,8 @@ final class OpenAPS {
                 model: model,
                 autotune: RawJSON.null,
                 trioSettings: trioSettings,
-                useSwiftOref: useSwiftOref
+                useSwiftOref: useSwiftOref,
+                clock: clock
             )
 
             let profile = try await makeProfile(
@@ -596,7 +598,8 @@ final class OpenAPS {
                 model: model,
                 autotune: RawJSON.null,
                 trioSettings: trioSettings,
-                useSwiftOref: useSwiftOref
+                useSwiftOref: useSwiftOref,
+                clock: clock
             )
 
             // Save the profiles
@@ -1026,7 +1029,8 @@ final class OpenAPS {
         model: JSON,
         autotune: JSON,
         trioSettings: JSON,
-        useSwiftOref: Bool
+        useSwiftOref: Bool,
+        clock: Date
     ) async throws -> RawJSON {
         let startJavascriptAt = Date()
         let jsResult = await makeProfileJavascript(
@@ -1050,7 +1054,7 @@ final class OpenAPS {
         }
 
         let startSwiftAt = Date()
-        let swiftResult = OpenAPSSwift.makeProfile(
+        let (swiftResult, makeProfileInputs) = OpenAPSSwift.makeProfile(
             preferences: preferences,
             pumpSettings: pumpSettings,
             bgTargets: bgTargets,
@@ -1059,7 +1063,8 @@ final class OpenAPS {
             carbRatio: carbRatio,
             tempTargets: tempTargets,
             model: model,
-            trioSettings: trioSettings
+            trioSettings: trioSettings,
+            clock: clock
         )
         let swiftDuration = Date().timeIntervalSince(startSwiftAt)
 
@@ -1068,7 +1073,8 @@ final class OpenAPS {
             swift: swiftResult,
             swiftDuration: swiftDuration,
             javascript: jsResult,
-            javascriptDuration: javascriptDuration
+            javascriptDuration: javascriptDuration,
+            makeProfileInputs: makeProfileInputs
         )
 
         return try jsResult.returnOrThrow()

+ 17 - 0
Trio/Sources/APS/OpenAPSSwift/Logging/AlgorithmComparison.swift

@@ -109,6 +109,20 @@ struct DetermineBasalInputs: Codable {
     let clock: Date
 }
 
+/// For tracking inputs to `makeProfile` when there is a mismatch
+struct MakeProfileInputs: Codable {
+    let preferences: Preferences
+    let pumpSettings: PumpSettings
+    let bgTargets: BGTargets
+    let basalProfile: [BasalProfileEntry]
+    let isf: InsulinSensitivities
+    let carbRatios: CarbRatios
+    let tempTargets: [TempTarget]
+    let model: String
+    let trioSettings: TrioSettings
+    let clock: Date
+}
+
 /// Represents a complete comparison between JS and Swift implementations
 struct AlgorithmComparison: Codable {
     let id: UUID
@@ -137,6 +151,7 @@ struct AlgorithmComparison: Codable {
     let mealInput: MealInputs?
     let autosensInput: AutosensInputs?
     let determineBasalInput: DetermineBasalInputs?
+    let makeProfileInput: MakeProfileInputs?
 
     init(
         function: OrefFunction,
@@ -151,6 +166,7 @@ struct AlgorithmComparison: Codable {
         mealInputs: MealInputs? = nil,
         autosensInputs: AutosensInputs? = nil,
         determineBasalInputs: DetermineBasalInputs? = nil,
+        makeProfileInputs: MakeProfileInputs? = nil,
         id: UUID = UUID(),
         createdAt: Date = Date()
     ) {
@@ -168,6 +184,7 @@ struct AlgorithmComparison: Codable {
         mealInput = mealInputs
         autosensInput = autosensInputs
         determineBasalInput = determineBasalInputs
+        makeProfileInput = makeProfileInputs
         timezone = TimeZone.current.identifier
         version = "4"
         #if targetEnvironment(simulator)

+ 14 - 7
Trio/Sources/APS/OpenAPSSwift/Logging/JSONCompare.swift

@@ -87,7 +87,8 @@ enum JSONCompare {
         iobInputs: IobInputs? = nil,
         mealInputs: MealInputs? = nil,
         autosensInputs: AutosensInputs? = nil,
-        determineBasalInputs: DetermineBasalInputs? = nil
+        determineBasalInputs: DetermineBasalInputs? = nil,
+        makeProfileInputs: MakeProfileInputs? = nil
     ) {
         let comparison = createComparison(
             function: function,
@@ -98,7 +99,8 @@ enum JSONCompare {
             iobInputs: iobInputs,
             mealInputs: mealInputs,
             autosensInputs: autosensInputs,
-            determineBasalInputs: determineBasalInputs
+            determineBasalInputs: determineBasalInputs,
+            makeProfileInputs: makeProfileInputs
         )
 
         Task {
@@ -119,7 +121,8 @@ enum JSONCompare {
         iobInputs: IobInputs?,
         mealInputs: MealInputs?,
         autosensInputs: AutosensInputs?,
-        determineBasalInputs: DetermineBasalInputs?
+        determineBasalInputs: DetermineBasalInputs?,
+        makeProfileInputs: MakeProfileInputs?
     ) -> AlgorithmComparison {
         switch (swift, javascript) {
         case let (.success(swiftJson), .success(javascriptJson)):
@@ -135,7 +138,8 @@ enum JSONCompare {
                     iobInputs: differences.isEmpty ? nil : iobInputs,
                     mealInputs: differences.isEmpty ? nil : mealInputs,
                     autosensInputs: differences.isEmpty ? nil : autosensInputs,
-                    determineBasalInputs: differences.isEmpty ? nil : determineBasalInputs
+                    determineBasalInputs: differences.isEmpty ? nil : determineBasalInputs,
+                    makeProfileInputs: differences.isEmpty ? nil : makeProfileInputs
                 )
             } catch {
                 return AlgorithmComparison(
@@ -147,7 +151,8 @@ enum JSONCompare {
                     iobInputs: iobInputs,
                     mealInputs: mealInputs,
                     autosensInputs: autosensInputs,
-                    determineBasalInputs: determineBasalInputs
+                    determineBasalInputs: determineBasalInputs,
+                    makeProfileInputs: makeProfileInputs
                 )
             }
 
@@ -168,7 +173,8 @@ enum JSONCompare {
                 iobInputs: iobInputs,
                 mealInputs: mealInputs,
                 autosensInputs: autosensInputs,
-                determineBasalInputs: determineBasalInputs
+                determineBasalInputs: determineBasalInputs,
+                makeProfileInputs: makeProfileInputs
             )
 
         case let (.success, .failure(jsError)):
@@ -180,7 +186,8 @@ enum JSONCompare {
                 iobInputs: iobInputs,
                 mealInputs: mealInputs,
                 autosensInputs: autosensInputs,
-                determineBasalInputs: determineBasalInputs
+                determineBasalInputs: determineBasalInputs,
+                makeProfileInputs: makeProfileInputs
             )
         }
     }

+ 21 - 5
Trio/Sources/APS/OpenAPSSwift/OpenAPSSwift.swift

@@ -10,8 +10,11 @@ struct OpenAPSSwift {
         carbRatio: JSON,
         tempTargets: JSON,
         model: JSON,
-        trioSettings: JSON
-    ) -> OrefFunctionResult {
+        trioSettings: JSON,
+        clock: Date
+    ) -> (OrefFunctionResult, MakeProfileInputs?) {
+        var makeProfileInputs: MakeProfileInputs?
+
         do {
             let preferences = try JSONBridge.preferences(from: preferences)
             let pumpSettings = try JSONBridge.pumpSettings(from: pumpSettings)
@@ -23,6 +26,19 @@ struct OpenAPSSwift {
             let model = JSONBridge.model(from: model)
             let trioSettings = try JSONBridge.trioSettings(from: trioSettings)
 
+            makeProfileInputs = MakeProfileInputs(
+                preferences: preferences,
+                pumpSettings: pumpSettings,
+                bgTargets: bgTargets,
+                basalProfile: basalProfile,
+                isf: isf,
+                carbRatios: carbRatio,
+                tempTargets: tempTargets,
+                model: model,
+                trioSettings: trioSettings,
+                clock: clock
+            )
+
             let profile = try ProfileGenerator.generate(
                 pumpSettings: pumpSettings,
                 bgTargets: bgTargets,
@@ -32,12 +48,12 @@ struct OpenAPSSwift {
                 carbRatios: carbRatio,
                 tempTargets: tempTargets,
                 model: model,
-                trioSettings: trioSettings
+                clock: clock
             )
 
-            return try .success(JSONBridge.to(profile))
+            return (try .success(JSONBridge.to(profile)), makeProfileInputs)
         } catch {
-            return .failure(error)
+            return (.failure(error), makeProfileInputs)
         }
     }
 

+ 2 - 2
Trio/Sources/APS/OpenAPSSwift/Profile/Basal.swift

@@ -1,8 +1,8 @@
 import Foundation
 
 struct Basal {
-    static func basalLookup(_ basalProfile: [BasalProfileEntry], now: Date? = nil) throws -> Decimal? {
-        let nowDate = now ?? Date()
+    static func basalLookup(_ basalProfile: [BasalProfileEntry], now: Date) throws -> Decimal? {
+        let nowDate = now
 
         // Original had a sort but it was a no-op if 'i' wasn't present, so we can skip it
         let basalProfileData = basalProfile

+ 1 - 1
Trio/Sources/APS/OpenAPSSwift/Profile/Carbs.swift

@@ -1,7 +1,7 @@
 import Foundation
 
 struct Carbs {
-    static func carbRatioLookup(carbRatio: CarbRatios, now: Date = Date()) -> Decimal? {
+    static func carbRatioLookup(carbRatio: CarbRatios, now: Date) -> Decimal? {
         // Get last schedule as default
         guard let lastSchedule = carbRatio.schedule.last else { return nil }
         var currentRatio = lastSchedule.ratio

+ 2 - 2
Trio/Sources/APS/OpenAPSSwift/Profile/Isf.swift

@@ -4,9 +4,9 @@ import Foundation
 struct Isf {
     static func isfLookup(
         isfDataInput: InsulinSensitivities,
-        timestamp: Date? = nil
+        timestamp: Date
     ) throws -> (Decimal, ComputedInsulinSensitivities) {
-        let now = timestamp ?? Date()
+        let now = timestamp
 
         let isfData = isfDataInput.computedInsulinSensitivies()
 

+ 12 - 9
Trio/Sources/APS/OpenAPSSwift/Profile/ProfileGenerator.swift

@@ -74,7 +74,7 @@ enum ProfileGenerator {
         carbRatios: CarbRatios,
         tempTargets: [TempTarget],
         model: String,
-        trioSettings _: TrioSettings
+        clock: Date
     ) throws -> Profile {
         let model = model.replacingOccurrences(of: "\"", with: "").trimmingCharacters(in: .whitespacesAndNewlines)
 
@@ -97,7 +97,7 @@ enum ProfileGenerator {
             debug(.openAPS, "don't modify insulin peak time")
         }
 
-        return try generate(
+        return try generateProfile(
             pumpSettings: pumpSettings,
             bgTargets: bgTargets,
             basalProfile: basalProfile,
@@ -105,12 +105,13 @@ enum ProfileGenerator {
             preferences: preferences,
             carbRatios: carbRatios,
             tempTargets: tempTargets,
-            model: model
+            model: model,
+            clock: clock
         )
     }
 
     /// Direct port of the OpenAPS profile generate function
-    private static func generate(
+    private static func generateProfile(
         pumpSettings: PumpSettings,
         bgTargets: BGTargets,
         basalProfile: [BasalProfileEntry],
@@ -118,7 +119,8 @@ enum ProfileGenerator {
         preferences: Preferences,
         carbRatios: CarbRatios,
         tempTargets: [TempTarget],
-        model: String
+        model: String,
+        clock: Date
     ) throws -> Profile {
         var profile = Profile() // start with the defaults
 
@@ -138,7 +140,7 @@ enum ProfileGenerator {
         profile.model = model
         profile.skipNeutralTemps = preferences.skipNeutralTemps
 
-        profile.currentBasal = try Basal.basalLookup(basalProfile)
+        profile.currentBasal = try Basal.basalLookup(basalProfile, now: clock)
         profile.basalprofile = basalProfile
 
         let basalProfile = basalProfile
@@ -169,7 +171,8 @@ enum ProfileGenerator {
         }
 
         profile.outUnits = bgTargets.userPreferredUnits
-        let (updatedTargets, range) = try Targets.bgTargetsLookup(targets: bgTargets, tempTargets: tempTargets, profile: profile)
+        let (updatedTargets, range) = try Targets
+            .bgTargetsLookup(targets: bgTargets, tempTargets: tempTargets, profile: profile, now: clock)
         profile.minBg = range.minBg?.rounded()
         profile.maxBg = range.maxBg?.rounded()
         // Note: we're using updatedTargets here because in Javascript the bgTargetsLookup
@@ -195,7 +198,7 @@ enum ProfileGenerator {
         )
 
         profile.temptargetSet = range.temptargetSet
-        let (sens, isfUpdated) = try Isf.isfLookup(isfDataInput: isf)
+        let (sens, isfUpdated) = try Isf.isfLookup(isfDataInput: isf, timestamp: clock)
         profile.sens = sens
         profile.isfProfile = isfUpdated
 
@@ -208,7 +211,7 @@ enum ProfileGenerator {
         }
 
         // Handle carb ratio data
-        guard let currentCarbRatio = Carbs.carbRatioLookup(carbRatio: carbRatios) else {
+        guard let currentCarbRatio = Carbs.carbRatioLookup(carbRatio: carbRatios, now: clock) else {
             throw ProfileError.invalidCarbRatio
         }
         profile.carbRatio = currentCarbRatio

+ 1 - 1
Trio/Sources/APS/OpenAPSSwift/Profile/Targets.swift

@@ -84,7 +84,7 @@ struct Targets {
         targets: BGTargets,
         tempTargets: [TempTarget],
         profile: Profile,
-        now: Date = Date()
+        now: Date
     ) throws -> (ComputedBGTargets, ComputedBGTargetEntry) {
         var (computedBgTargets, targetIdx) = try lookup(targets: targets, tempTargets: tempTargets, profile: profile, now: now)
         let currentTarget = boundTargetRange(computedBgTargets.targets[targetIdx])

+ 0 - 1
Trio/Sources/Localizations/Main/Localizable.xcstrings

@@ -10037,7 +10037,6 @@
       }
     },
     "%lld h" : {
-      "extractionState" : "stale",
       "localizations" : {
         "bg" : {
           "stringUnit" : {

+ 2 - 1
TrioTests/OpenAPSSwiftTests/AutosensJsonTests.swift

@@ -38,7 +38,8 @@ import Testing
             iobInputs: nil,
             mealInputs: nil,
             autosensInputs: nil,
-            determineBasalInputs: nil
+            determineBasalInputs: nil,
+            makeProfileInputs: nil
         )
 
         if comparison.resultType == .valueDifference {

+ 4 - 2
TrioTests/OpenAPSSwiftTests/DetermineBasalJsonTests.swift

@@ -82,7 +82,8 @@ import Testing
             iobInputs: nil,
             mealInputs: nil,
             autosensInputs: nil,
-            determineBasalInputs: nil
+            determineBasalInputs: nil,
+            makeProfileInputs: nil
         )
 
         if comparison.resultType == .valueDifference {
@@ -173,7 +174,8 @@ import Testing
             iobInputs: nil,
             mealInputs: nil,
             autosensInputs: nil,
-            determineBasalInputs: nil
+            determineBasalInputs: nil,
+            makeProfileInputs: nil
         )
 
         if comparison.resultType == .valueDifference {

+ 4 - 2
TrioTests/OpenAPSSwiftTests/IobJsonTests.swift

@@ -134,7 +134,8 @@ import Testing
             iobInputs: nil,
             mealInputs: nil,
             autosensInputs: nil,
-            determineBasalInputs: nil
+            determineBasalInputs: nil,
+            makeProfileInputs: nil
         )
 
         if comparison.resultType == .valueDifference {
@@ -173,7 +174,8 @@ import Testing
             iobInputs: nil,
             mealInputs: nil,
             autosensInputs: nil,
-            determineBasalInputs: nil
+            determineBasalInputs: nil,
+            makeProfileInputs: nil
         )
 
         if comparison.resultType != .valueDifference {

+ 4 - 2
TrioTests/OpenAPSSwiftTests/MealJsonTests.swift

@@ -78,7 +78,8 @@ import Testing
             iobInputs: nil,
             mealInputs: nil,
             autosensInputs: nil,
-            determineBasalInputs: nil
+            determineBasalInputs: nil,
+            makeProfileInputs: nil
         )
 
         if comparison.resultType == .valueDifference {
@@ -155,7 +156,8 @@ import Testing
             iobInputs: nil,
             mealInputs: nil,
             autosensInputs: nil,
-            determineBasalInputs: nil
+            determineBasalInputs: nil,
+            makeProfileInputs: nil
         )
 
         #expect(comparison.resultType == .matching)

+ 3 - 3
TrioTests/OpenAPSSwiftTests/ProfileBasalTests.swift

@@ -40,7 +40,7 @@ import Testing
     }
 
     @Test("should return nil with an empty profile") func handleEmptyProfile() async throws {
-        let rate = try Basal.basalLookup([])
+        let rate = try Basal.basalLookup([], now: Date())
         #expect(rate == nil)
     }
 
@@ -49,7 +49,7 @@ import Testing
             BasalProfileEntry(start: "00:00", minutes: 0, rate: 1.0)
         ]
 
-        let rate = try Basal.basalLookup(basalProfile)
+        let rate = try Basal.basalLookup(basalProfile, now: Date())
         #expect(rate == 1.0)
     }
 
@@ -58,7 +58,7 @@ import Testing
             BasalProfileEntry(start: "00:00", minutes: 0, rate: 0.0)
         ]
 
-        let rate = try Basal.basalLookup(basalProfile)
+        let rate = try Basal.basalLookup(basalProfile, now: Date())
         #expect(rate == nil)
     }
 

+ 1 - 1
TrioTests/OpenAPSSwiftTests/ProfileCarbsTests.swift

@@ -31,7 +31,7 @@ import Testing
                 CarbRatioEntry(start: "00:00:00", offset: 0, ratio: 12)
             ]
         )
-        let ratio = Carbs.carbRatioLookup(carbRatio: exchangeSchedule)
+        let ratio = Carbs.carbRatioLookup(carbRatio: exchangeSchedule, now: Date())
         #expect(ratio == 1) // 12 grams per exchange
     }
 

+ 1 - 1
TrioTests/OpenAPSSwiftTests/ProfileIsfTests.swift

@@ -56,7 +56,7 @@ import Testing
                 InsulinSensitivityEntry(sensitivity: 100, offset: 30, start: "00:30:00")
             ]
         )
-        let (sensitivity, _) = try Isf.isfLookup(isfDataInput: invalidISF)
+        let (sensitivity, _) = try Isf.isfLookup(isfDataInput: invalidISF, timestamp: Date())
         #expect(sensitivity == -1)
     }
 }

+ 12 - 10
TrioTests/OpenAPSSwiftTests/ProfileJavascriptTests.swift

@@ -69,7 +69,7 @@ struct ProfileGeneratorTests {
             carbRatios: inputs.5,
             tempTargets: inputs.6,
             model: inputs.7,
-            trioSettings: inputs.8
+            clock: Date()
         )
 
         #expect(profile.maxIob == 0)
@@ -112,7 +112,7 @@ struct ProfileGeneratorTests {
             carbRatios: inputs.5,
             tempTargets: inputs.6,
             model: inputs.7,
-            trioSettings: inputs.8
+            clock: currentTime
         )
 
         #expect(profile.maxIob == 0)
@@ -156,7 +156,7 @@ struct ProfileGeneratorTests {
             carbRatios: inputs.5,
             tempTargets: inputs.6,
             model: inputs.7,
-            trioSettings: inputs.8
+            clock: currentTime
         )
 
         #expect(profile.maxIob == 0)
@@ -199,7 +199,7 @@ struct ProfileGeneratorTests {
             carbRatios: inputs.5,
             tempTargets: inputs.6,
             model: inputs.7,
-            trioSettings: inputs.8
+            clock: currentTime
         )
 
         #expect(profile.maxIob == 0)
@@ -229,7 +229,7 @@ struct ProfileGeneratorTests {
                 carbRatios: inputs.5,
                 tempTargets: inputs.6,
                 model: inputs.7,
-                trioSettings: inputs.8
+                clock: Date()
             )
         }
     }
@@ -252,7 +252,7 @@ struct ProfileGeneratorTests {
                 carbRatios: inputs.5,
                 tempTargets: inputs.6,
                 model: inputs.7,
-                trioSettings: inputs.8
+                clock: Date()
             )
         }
     }
@@ -270,7 +270,7 @@ struct ProfileGeneratorTests {
             carbRatios: inputs.5,
             tempTargets: inputs.6,
             model: inputs.7,
-            trioSettings: inputs.8
+            clock: Date()
         )
 
         #expect(profile.model == "554")
@@ -309,7 +309,7 @@ struct ProfileGeneratorTests {
             trioSettings: inputs.8
         )
 
-        let swiftResult = OpenAPSSwift.makeProfile(
+        let (swiftResult, makeProfileInputs) = OpenAPSSwift.makeProfile(
             preferences: inputs.4,
             pumpSettings: inputs.0,
             bgTargets: inputs.1,
@@ -318,7 +318,8 @@ struct ProfileGeneratorTests {
             carbRatio: inputs.5,
             tempTargets: tempTargets,
             model: inputs.7,
-            trioSettings: inputs.8
+            trioSettings: inputs.8,
+            clock: now
         )
 
         let comparison = JSONCompare.createComparison(
@@ -330,7 +331,8 @@ struct ProfileGeneratorTests {
             iobInputs: nil,
             mealInputs: nil,
             autosensInputs: nil,
-            determineBasalInputs: nil
+            determineBasalInputs: nil,
+            makeProfileInputs: makeProfileInputs
         )
 
         if comparison.resultType == .valueDifference {

+ 17 - 9
TrioTests/OpenAPSSwiftTests/ProfileJsNativeCompareTests.swift

@@ -77,7 +77,7 @@ import Testing
             trioSettings: inputs.8
         )
 
-        let profileSwift = OpenAPSSwift.makeProfile(
+        let (profileSwift, _) = OpenAPSSwift.makeProfile(
             preferences: inputs.0,
             pumpSettings: inputs.1,
             bgTargets: inputs.2,
@@ -86,7 +86,8 @@ import Testing
             carbRatio: inputs.5,
             tempTargets: inputs.6,
             model: inputs.7,
-            trioSettings: inputs.8
+            trioSettings: inputs.8,
+            clock: Date()
         )
 
         let comparison = JSONCompare.createComparison(
@@ -98,7 +99,8 @@ import Testing
             iobInputs: nil,
             mealInputs: nil,
             autosensInputs: nil,
-            determineBasalInputs: nil
+            determineBasalInputs: nil,
+            makeProfileInputs: nil
         )
 
         #expect(comparison.resultType == .matching)
@@ -131,7 +133,8 @@ import Testing
             iobInputs: nil,
             mealInputs: nil,
             autosensInputs: nil,
-            determineBasalInputs: nil
+            determineBasalInputs: nil,
+            makeProfileInputs: nil
         )
 
         #expect(comparison.resultType == .matching)
@@ -153,7 +156,8 @@ import Testing
             iobInputs: nil,
             mealInputs: nil,
             autosensInputs: nil,
-            determineBasalInputs: nil
+            determineBasalInputs: nil,
+            makeProfileInputs: nil
         )
 
         #expect(comparison.resultType == .valueDifference)
@@ -177,7 +181,8 @@ import Testing
             iobInputs: nil,
             mealInputs: nil,
             autosensInputs: nil,
-            determineBasalInputs: nil
+            determineBasalInputs: nil,
+            makeProfileInputs: nil
         )
 
         #expect(comparison.resultType == .matchingExceptions)
@@ -198,7 +203,8 @@ import Testing
             iobInputs: nil,
             mealInputs: nil,
             autosensInputs: nil,
-            determineBasalInputs: nil
+            determineBasalInputs: nil,
+            makeProfileInputs: nil
         )
 
         #expect(comparison.resultType == .swiftOnlyException)
@@ -221,7 +227,8 @@ import Testing
             iobInputs: nil,
             mealInputs: nil,
             autosensInputs: nil,
-            determineBasalInputs: nil
+            determineBasalInputs: nil,
+            makeProfileInputs: nil
         )
 
         #expect(comparison.resultType == .jsOnlyException)
@@ -243,7 +250,8 @@ import Testing
             iobInputs: nil,
             mealInputs: nil,
             autosensInputs: nil,
-            determineBasalInputs: nil
+            determineBasalInputs: nil,
+            makeProfileInputs: nil
         )
 
         #expect(comparison.resultType == .comparisonError)

+ 93 - 0
TrioTests/OpenAPSSwiftTests/ProfileJsonTests.swift

@@ -0,0 +1,93 @@
+import Foundation
+import Testing
+@testable import Trio
+
+@Suite("Profile testing using JSON inputs", .serialized) struct ProfileJsonTests {
+    let timeZoneForTests = TimeZoneForTests()
+
+    @Test(
+        "Profile should produce same results for fixed JS",
+        .enabled(if: ReplayTests.enabled)
+    ) func replayErrorInputs() async throws {
+        // Note: This test case can only test one timezone per invocation
+        // so you need to manually change this to try out errors from
+        // different timezones
+        let testingTimezone = ReplayTests.timezone
+        let files = try await HttpFiles.listFiles()
+        for filePath in files {
+            let algorithmComparison = try await HttpFiles.downloadFile(at: filePath)
+            print("Checking \(filePath) @ \(algorithmComparison.createdAt)")
+            guard algorithmComparison.timezone == testingTimezone else {
+                continue
+            }
+            guard let profileInputs = algorithmComparison.makeProfileInput else {
+                print("Skipping, no profileInputs found")
+                if let str = algorithmComparison.comparisonError {
+                    print(str)
+                }
+                if let str = algorithmComparison.swiftException {
+                    print(str)
+                }
+                continue
+            }
+
+            timeZoneForTests.setTimezone(identifier: algorithmComparison.timezone)
+            try await checkFixedJsAgainstSwift(profileInputs: profileInputs)
+            print("Checked \(filePath) \(algorithmComparison.timezone)")
+            timeZoneForTests.resetTimezone()
+        }
+    }
+
+    func checkFixedJsAgainstSwift(profileInputs: MakeProfileInputs) async throws {
+        let openAps = OpenAPSFixed()
+        let (profileResultSwift, _) = OpenAPSSwift.makeProfile(
+            preferences: profileInputs.preferences,
+            pumpSettings: profileInputs.pumpSettings,
+            bgTargets: profileInputs.bgTargets,
+            basalProfile: profileInputs.basalProfile,
+            isf: profileInputs.isf,
+            carbRatio: profileInputs.carbRatios,
+            tempTargets: profileInputs.tempTargets,
+            model: profileInputs.model,
+            trioSettings: profileInputs.trioSettings,
+            clock: profileInputs.clock
+        )
+
+        let profileResultJavascript = await openAps.makeProfileJavascript(
+            preferences: profileInputs.preferences,
+            pumpSettings: profileInputs.pumpSettings,
+            bgTargets: profileInputs.bgTargets,
+            basalProfile: profileInputs.basalProfile,
+            isf: profileInputs.isf,
+            carbRatio: profileInputs.carbRatios,
+            tempTargets: profileInputs.tempTargets,
+            model: profileInputs.model,
+            autotune: RawJSON.null,
+            trioSettings: profileInputs.trioSettings,
+            clock: profileInputs.clock
+        )
+
+        let comparison = JSONCompare.createComparison(
+            function: .makeProfile,
+            swift: profileResultSwift,
+            swiftDuration: 0.1,
+            javascript: profileResultJavascript,
+            javascriptDuration: 0.1,
+            iobInputs: nil,
+            mealInputs: nil,
+            autosensInputs: nil,
+            determineBasalInputs: nil,
+            makeProfileInputs: nil
+        )
+
+        if comparison.resultType == .valueDifference {
+            print(comparison.differences!.prettyPrintedJSON!)
+        }
+
+        if comparison.resultType != .matching {
+            print("REPLAY ERROR: Fixed JS didn't match")
+        }
+
+        #expect(comparison.resultType == .matching)
+    }
+}

+ 1 - 1
TrioTests/OpenAPSSwiftTests/ProfileTargetsTests.swift

@@ -95,7 +95,7 @@ import Testing
                 BGTargetEntry(low: 40, high: 250, start: "00:00:00", offset: 0)
             ]
         )
-        let (_, result) = try Targets.bgTargetsLookup(targets: extremeTargets, tempTargets: [], profile: profile)
+        let (_, result) = try Targets.bgTargetsLookup(targets: extremeTargets, tempTargets: [], profile: profile, now: Date())
         #expect(result.maxBg == 80)
         #expect(result.minBg == 80)
     }

+ 113 - 0
TrioTests/OpenAPSSwiftTests/javascript/bundle/profile-prepare.js

@@ -0,0 +1,113 @@
+//для pumpprofile.json параметры: settings/settings.json settings/bg_targets.json settings/insulin_sensitivities.json settings/basal_profile.json preferences.json settings/carb_ratios.json settings/temptargets.json settings/model.json
+//для profile.json параметры: settings/settings.json settings/bg_targets.json settings/insulin_sensitivities.json settings/basal_profile.json preferences.json settings/carb_ratios.json settings/temptargets.json settings/model.json settings/autotune.json
+
+function generate(pumpsettings_data, bgtargets_data, isf_data, basalprofile_data, preferences_input = false, carbratio_input = false, temptargets_input = false, model_input = false, autotune_input = false, trio_data, clock_input = false) {
+    if (bgtargets_data.units !== 'mg/dL') {
+        if (bgtargets_data.units === 'mmol/L') {
+            for (var i = 0, len = bgtargets_data.targets.length; i < len; i++) {
+                bgtargets_data.targets[i].high = bgtargets_data.targets[i].high * 18;
+                bgtargets_data.targets[i].low = bgtargets_data.targets[i].low * 18;
+            }
+            bgtargets_data.units = 'mg/dL';
+        } else {
+            return { "error" : 'BG Target data is expected to be expressed in mg/dL or mmol/L. Found '+ bgtargets_data.units };
+        }
+    }
+    
+    if (isf_data.units !== 'mg/dL') {
+        if (isf_data.units === 'mmol/L') {
+            for (var i = 0, len = isf_data.sensitivities.length; i < len; i++) {
+                isf_data.sensitivities[i].sensitivity = isf_data.sensitivities[i].sensitivity * 18;
+            }
+            isf_data.units = 'mg/dL';
+        } else {
+            return { "error" : 'ISF is expected to be expressed in mg/dL or mmol/L. Found '+ isf_data.units };
+        }
+    }
+
+    var autotune_data = { };
+    if (autotune_input) {
+        autotune_data = autotune_input;
+    }
+
+    var temptargets_data = { };
+    if (temptargets_input) {
+        temptargets_data = temptargets_input;
+    }
+    
+    var trioData = { };
+    if (trio_data) {
+        trioData = trio_data;
+    }
+
+    var clock = null;
+    if (clock_input) {
+        clock = new Date(clock_input);
+    }
+
+    var model_data = { };
+    if (model_input) {
+        model_data = model_input.replace(/"/gi, '');
+    }
+
+    var carbratio_data = { };
+    if (carbratio_input) {
+        var errors = [ ];
+        if (!(carbratio_input.schedule && carbratio_input.schedule[0].start && carbratio_input.schedule[0].ratio)) {
+          errors.push("Carb ratio data should have an array called schedule with a start and ratio fields.");
+        }
+        if (carbratio_input.units !== 'grams' && carbratio_input.units !== 'exchanges')  {
+          errors.push("Carb ratio should have units field set to 'grams' or 'exchanges'.");
+        }
+        if (errors.length) {
+          return { "error" : errors.join(' ') };
+        }
+        carbratio_data = carbratio_input;
+    }
+
+    var preferences = { };
+    if (preferences_input) {
+        preferences = preferences_input;
+        if (preferences.curve === "rapid-acting") {
+            if (preferences.useCustomPeakTime) {
+                preferences.insulinPeakTime =
+                Math.max(50, Math.min(preferences.insulinPeakTime, 120));
+            } else { preferences.insulinPeakTime = 75; }
+        } 
+        else if (preferences.curve === "ultra-rapid") {
+            if (preferences.useCustomPeakTime) {
+                preferences.insulinPeakTime =
+                Math.max(35, Math.min(preferences.insulinPeakTime, 100));
+            } else { preferences.insulinPeakTime = 55; }
+        }
+    }
+
+    var inputs = { };
+    //add all preferences to the inputs
+    for (var pref in preferences) {
+      if (preferences.hasOwnProperty(pref)) {
+        inputs[pref] = preferences[pref];
+      }
+    }
+
+    inputs.max_iob = inputs.max_iob || 0;
+    //set these after to make sure nothing happens if they are also set in preferences
+    inputs.settings = pumpsettings_data;
+    inputs.targets = bgtargets_data;
+    inputs.basals = basalprofile_data;
+    inputs.isf = isf_data;
+    inputs.carbratio = carbratio_data;
+    inputs.temptargets = temptargets_data;
+    inputs.model = model_data;
+    inputs.autotune = autotune_data;
+    inputs.clock = clock;
+
+    if (autotune_data) {
+        if (autotune_data.basalprofile) { inputs.basals = autotune_data.basalprofile; }
+        if (!trioData.onlyAutotuneBasals) {
+            if (autotune_data.isfProfile) { inputs.isf = autotune_data.isfProfile; }
+            if (autotune_data.carb_ratio) { inputs.carbratio.schedule[0].ratio = autotune_data.carb_ratio; }
+        }
+    }
+    return trio_profile(inputs);
+}

ファイルの差分が大きいため隠しています
+ 1 - 1
TrioTests/OpenAPSSwiftTests/javascript/bundle/profile.js


+ 45 - 0
TrioTests/OpenAPSSwiftTests/utils/OpenAPSFixed.swift

@@ -211,6 +211,51 @@ final class OpenAPSFixed {
             return .failure(error)
         }
     }
+
+    func makeProfileJavascript(
+        preferences: JSON,
+        pumpSettings: JSON,
+        bgTargets: JSON,
+        basalProfile: JSON,
+        isf: JSON,
+        carbRatio: JSON,
+        tempTargets: JSON,
+        model: JSON,
+        autotune: JSON,
+        trioSettings: JSON,
+        clock: JSON
+    ) async -> OrefFunctionResult {
+        do {
+            let testBundle = Bundle(for: OpenAPSFixed.self)
+            let result = try await withCheckedThrowingContinuation { continuation in
+                let jsWorker = JavaScriptWorker(poolSize: 1)
+                jsWorker.inCommonContext { worker in
+                    worker.evaluateBatch(scripts: [
+                        Script(name: "prepare/log.js"),
+                        Script.fromTestingBundle(name: "profile.js", bundle: testBundle),
+                        Script.fromTestingBundle(name: "profile-prepare.js", bundle: testBundle)
+                    ])
+                    let result = worker.call(function: "generate", with: [
+                        pumpSettings,
+                        bgTargets,
+                        isf,
+                        basalProfile,
+                        preferences,
+                        carbRatio,
+                        tempTargets,
+                        model,
+                        autotune,
+                        trioSettings,
+                        clock
+                    ])
+                    continuation.resume(returning: result)
+                }
+            }
+            return .success(result)
+        } catch {
+            return .failure(error)
+        }
+    }
 }
 
 extension Script {