Procházet zdrojové kódy

Ensure consistency for determineBasal `result` strings

This commit includes a number of small fixes to make the Swift reason
string construction consistent with Javascript.

We did have to update the testing JS to:
- Add precision to maxSafeBasalRate
- Add rounding to a value in the result string
- Remove contributions from `insulinForManualBolus`, which Trio doesn't support

And there is one tiny bug fix in the Swift implementation around our
use of `profile.sens` where we should always try to get that first and
then fall back to the lookup based on time. The issue is that the time
for profile vs determine basal might cross a sensitivity boundary.

Finally, this commit adds a few properties to our fuzzy matching logic
that Trio uses and has some small fixes to ensure consistency.
Sam King před 4 měsíci
rodič
revize
6133c17ea9

+ 36 - 8
Trio/Sources/APS/OpenAPSSwift/DetermineBasal/DetermineBasal+Helpers.swift

@@ -184,32 +184,34 @@ extension DeterminationGenerator {
         return (sensitivity / sensitivityRatio).jsRounded(scale: 1)
     }
 
+    /// Checks if current temp basal matches last temp from IOB data.
+    /// Returns nil if check passes, or the failure reason string if it fails.
     static func checkCurrentTempBasalRateSafety(
         currentTemp: TempBasal,
         lastTempTarget: IobResult.LastTemp?,
         currentTime: Date
-    ) -> Bool {
+    ) -> String? {
         guard let lastTemp = lastTempTarget, let lastTempDate = lastTemp.timestamp,
-              let lastTempDuration = lastTemp.duration else { return true }
+              let lastTempDuration = lastTemp.duration else { return nil }
         // TODO: throw error for malformed IobResult? Can this be malformed?
 
         let lastTempAge = Int((currentTime.timeIntervalSince(lastTempDate) / 60).rounded()) // in minutes
 //        let tempModulus = Int(lastTempAge + currentTemp.duration) % 30 // only used in JS as output; will leave it here for now
 
-        if currentTemp.rate != lastTemp.rate, lastTempAge > 10, currentTemp.duration > 0 {
-            // Rates dont match and temp is old: cancel temp
-            return false
+        if let lastTempRate = lastTemp.rate, currentTemp.rate != lastTempRate, lastTempAge > 10, currentTemp.duration > 0 {
+            // Rates don't match and temp is old: cancel temp
+            return "Warning: currenttemp rate \(currentTemp.rate) != lastTemp rate \(lastTempRate) from pumphistory; canceling temp"
         }
         if currentTemp.duration > 0 {
-            let lastTempEnded = lastTempAge - Int(lastTempDuration) // TODO: check if this comes in minutes
+            let lastTempEnded = Decimal(lastTempAge) - lastTempDuration
 
             if lastTempEnded > 5, lastTempAge > 10 {
                 // Last temp ended long ago but temp is running: cancel temp
-                return false
+                return "Warning: currenttemp running but lastTemp from pumphistory ended \(lastTempEnded.jsRounded(scale: 2))m ago; canceling temp"
             }
         }
 
-        return true
+        return nil
     }
 
     /// Adjust glucose targets (min, max, target) based on autosens and/or noise.
@@ -317,3 +319,29 @@ extension TrioCustomOrefVariables {
         }
     }
 }
+
+extension Date {
+    /// Formats date like JavaScript's Date.toString()
+    /// Example: "Sat Nov 22 2025 14:22:58 GMT-0700 (Mountain Standard Time)"
+    func jsDateString() -> String {
+        let formatter = DateFormatter()
+        formatter.dateFormat = "EEE MMM dd yyyy HH:mm:ss"
+        formatter.locale = Locale(identifier: "en_US_POSIX")
+
+        let dateString = formatter.string(from: self)
+
+        // Get GMT offset string like "GMT-0700"
+        let seconds = TimeZone.current.secondsFromGMT(for: self)
+        let hours = abs(seconds) / 3600
+        let minutes = (abs(seconds) % 3600) / 60
+        let sign = seconds >= 0 ? "+" : "-"
+        let gmtOffset = String(format: "GMT%@%02d%02d", sign, hours, minutes)
+
+        // Get timezone name like "Mountain Standard Time" or "Mountain Daylight Time"
+        let style: TimeZone.NameStyle = TimeZone.current.isDaylightSavingTime(for: self) ? .daylightSaving : .standard
+        let timezoneName = TimeZone.current.localizedName(for: style, locale: Locale(identifier: "en_US")) ?? TimeZone.current
+            .identifier
+
+        return "\(dateString) \(gmtOffset) (\(timezoneName))"
+    }
+}

+ 65 - 20
Trio/Sources/APS/OpenAPSSwift/DetermineBasal/DetermineBasalGenerator.swift

@@ -84,13 +84,11 @@ enum DeterminationGenerator {
         guard let lastTempTarget = iobData.first?.lastTemp else {
             throw DeterminationError.missingIob
         }
-        if !checkCurrentTempBasalRateSafety(
+        if let reason = checkCurrentTempBasalRateSafety(
             currentTemp: currentTemp,
             lastTempTarget: lastTempTarget,
             currentTime: currentTime
         ) {
-            let reason =
-                "Safety check: currentTemp does not match lastTemp in IOB or lastTemp ended long ago; canceling temp basal."
             return Determination(
                 id: UUID(),
                 reason: reason,
@@ -230,7 +228,10 @@ enum DeterminationGenerator {
                         (
                             currentIob *
                                 min(
-                                    profile.profileSensitivity(at: currentTime, trioCustomOrefVaribales: trioCustomOrefVariables),
+                                    // Use same sensitivity source as adjustedSensitivity (matches JS `sensitivity` variable)
+                                    trioCustomOrefVariables.override(
+                                        sensitivity: profile.sens ?? profile.sensitivityFor(time: currentTime)
+                                    ),
                                     adjustedSensitivity
                                 )
                         )
@@ -273,6 +274,47 @@ enum DeterminationGenerator {
             glucoseImpact: currentGlucoseImpact
         )
 
+        // Build isfReason: "Autosens ratio: X, ISF: Y→Z"
+        // Use same sensitivity source as adjustedSensitivity (profile.sens ?? sensitivityFor) with override applied
+        let originalSensitivity = trioCustomOrefVariables.override(
+            sensitivity: profile.sens ?? profile.sensitivityFor(time: currentTime)
+        )
+        let isfReason =
+            "Autosens ratio: \(sensitivityRatio.jsRounded(scale: 2)), ISF: \(originalSensitivity.jsRounded())→\(adjustedSensitivity.jsRounded())"
+
+        // Build targetLog: "X" or "X→Y" if target was adjusted
+        let profileTarget = profile.profileTarget(trioCustomOrefVariables: trioCustomOrefVariables) ?? 100
+        let targetLog: String
+        if adjustedGlucoseTargets.targetGlucose != profileTarget {
+            targetLog = "\(profileTarget.jsRounded())→\(adjustedGlucoseTargets.targetGlucose.jsRounded())"
+        } else {
+            targetLog = "\(adjustedGlucoseTargets.targetGlucose.jsRounded())"
+        }
+
+        // Build tddReason: ", Dynamic ISF: On, Sigmoid function, AF: X, Basal ratio: Y, SMB Ratio: Z"
+        var tddReason = ""
+        if let dynamicIsfResult = dynamicIsfResult {
+            tddReason = ", Dynamic ISF: On"
+            if preferences.sigmoid {
+                tddReason += ", Sigmoid function"
+            } else {
+                tddReason += ", Logarithmic formula"
+            }
+            if let limitValue = dynamicIsfResult.limitValue {
+                tddReason +=
+                    ", Autosens/Dynamic Limit: \(limitValue) (\(dynamicIsfResult.uncappedRatio.jsRounded(scale: 2)))"
+            }
+            let af = preferences.sigmoid ? preferences.adjustmentFactorSigmoid : preferences.adjustmentFactor
+            tddReason += ", AF: \(af)"
+            if profile.tddAdjBasal {
+                tddReason += ", Basal ratio: \(dynamicIsfResult.tddRatio)"
+            }
+        }
+        // SMB Ratio is added if not default (0.5)
+        if profile.smbDeliveryRatio != 0.5 {
+            tddReason += ", SMB Ratio: \(min(profile.smbDeliveryRatio, 1))"
+        }
+
         let dosingInputs = DosingEngine.prepareDosingInputs(
             profile: profile,
             mealData: mealData,
@@ -284,9 +326,9 @@ enum DeterminationGenerator {
             currentBasal: profile.currentBasal ?? profile.basalFor(time: currentTime),
             overrideFactor: trioCustomOrefVariables.overrideFactor(),
             adjustedSensitivity: adjustedSensitivity,
-            isfReason: "", // Placeholder
-            tddReason: "", // Placeholder
-            targetLog: "" // Placeholder
+            isfReason: isfReason,
+            tddReason: tddReason,
+            targetLog: targetLog
         )
 
         let smbDecision = try DosingEngine.makeSMBDosingDecision(
@@ -306,6 +348,10 @@ enum DeterminationGenerator {
         if let smbReason = smbDecision.reason {
             reason += smbReason
         }
+        // Add carbs message after smbReason to match JS order
+        if let carbsReq = dosingInputs.carbsRequired {
+            reason += "\(carbsReq.carbs) add'l carbs req w/in \(carbsReq.minutes)m; "
+        }
 
         var determination = Determination(
             id: UUID(),
@@ -332,7 +378,7 @@ enum DeterminationGenerator {
             isf: nil,
             timestamp: currentTime,
             tdd: nil,
-            current_target: nil,
+            current_target: adjustedGlucoseTargets.targetGlucose,
             minDelta: nil,
             expectedDelta: expectedDelta,
             minGuardBG: smbDecision.minGuardGlucose ?? forecastResult.minGuardGlucose,
@@ -563,13 +609,12 @@ enum DeterminationGenerator {
         if glucose <= 10 || glucose == 38 || noise >= 3 {
             reason = "CGM is calibrating, in ??? state, or noise is high"
         }
-        // minAgo (BG age) > 12 or < -5 = old/future BG
+        // minAgo (BG age) > 12 or < -5 = old/future BG - can overwrite calibration reason (matches JS)
         if minAgo > 12 || minAgo < -5 {
             reason =
-                "If current system time \(currentTime) is correct, then BG data is too old. The last BG data was read \(minAgo) min ago at \(bgTime)"
-        }
-        // CGM data unchanged (flat)
-        if shortAvgDelta == 0 && longAvgDelta == 0 {
+                "If current system time \(currentTime.jsDateString()) is correct, then BG data is too old. The last BG data was read \(minAgo.jsRounded(scale: 1))m ago at \(bgTime.jsDateString())"
+        } else if shortAvgDelta == 0 && longAvgDelta == 0 {
+            // CGM data unchanged (flat) - only checked if BG is not too old
             if glucoseStatus.lastCalIndex != nil, glucoseStatus.lastCalIndex! < 3 {
                 reason = "CGM was just calibrated"
             } else {
@@ -591,7 +636,7 @@ enum DeterminationGenerator {
         if currentTemp.rate >= basal { // high temp is running
             // Replace high temp with neutral temp at scheduled basal rate for 30min
             let reasonWithAction = reason +
-                ". Replacing high temp basal of \(currentTemp.rate)U/hr with neutral temp of \(basal)U/hr"
+                ". Replacing high temp basal of \(currentTemp.rate) with neutral temp of \(basal)"
             return Determination(
                 id: UUID(),
                 reason: reasonWithAction,
@@ -612,7 +657,7 @@ enum DeterminationGenerator {
                 isf: profile.sens,
                 timestamp: currentTime,
                 tdd: nil,
-                current_target: profile.targetBg,
+                current_target: nil,
                 minDelta: minDelta,
                 expectedDelta: nil,
                 minGuardBG: nil,
@@ -623,7 +668,7 @@ enum DeterminationGenerator {
             )
         } else if currentTemp.rate == 0, currentTemp.duration > 30 {
             // Shorten long zero temp to 30m
-            let reasonWithAction = reason + ". Shortening \(currentTemp.duration)m long zero temp to 30m."
+            let reasonWithAction = reason + ". Shortening \(currentTemp.duration)m long zero temp to 30m. "
             return Determination(
                 id: UUID(),
                 reason: reasonWithAction,
@@ -644,7 +689,7 @@ enum DeterminationGenerator {
                 isf: profile.sens,
                 timestamp: currentTime,
                 tdd: nil,
-                current_target: profile.targetBg,
+                current_target: nil,
                 minDelta: minDelta,
                 expectedDelta: nil,
                 minGuardBG: nil,
@@ -655,7 +700,7 @@ enum DeterminationGenerator {
             )
         } else {
             // Do nothing (temp already safe)
-            let reasonWithAction = reason + ". Temp \(currentTemp.rate) <= current basal \(basal)U/hr; doing nothing."
+            let reasonWithAction = reason + ". Temp \(currentTemp.rate) <= current basal \(basal)U/hr; doing nothing. "
             return Determination(
                 id: UUID(),
                 reason: reasonWithAction,
@@ -668,7 +713,7 @@ enum DeterminationGenerator {
                 iob: nil,
                 cob: nil,
                 predictions: nil,
-                deliverAt: currentTime,
+                deliverAt: nil,
                 carbsReq: nil,
                 temp: currentTemp.temp,
                 bg: nil,
@@ -676,7 +721,7 @@ enum DeterminationGenerator {
                 isf: profile.sens,
                 timestamp: currentTime,
                 tdd: nil,
-                current_target: profile.targetBg,
+                current_target: nil,
                 minDelta: minDelta,
                 expectedDelta: nil,
                 minGuardBG: nil,

+ 24 - 13
Trio/Sources/APS/OpenAPSSwift/DetermineBasal/DosingEngine.swift

@@ -161,12 +161,12 @@ enum DosingEngine {
         tddReason: String,
         targetLog: String // This is a pre-formatted string from the JS
     ) -> DosingInputs {
-        let lastIOBpredBG = forecast.iob.last ?? 0
-        let lastCOBpredBG = forecast.cob?.last
-        let lastUAMpredBG = forecast.uam?.last
+        let lastIOBpredBG = (forecast.iob.last ?? 0).jsRounded()
+        let lastCOBpredBG = forecast.cob?.last?.jsRounded()
+        let lastUAMpredBG = forecast.uam?.last?.jsRounded()
 
         var reason =
-            "\(isfReason), COB: \(mealData.mealCOB), Dev: \(deviation), BGI: \(glucoseImpact), CR: \(forecast.adjustedCarbRatio), Target: \(targetLog), minPredBG \(forecast.minForecastedGlucose), minGuardBG \(forecast.minGuardGlucose), IOBpredBG \(lastIOBpredBG)"
+            "\(isfReason), COB: \(mealData.mealCOB), Dev: \(deviation.jsRounded()), BGI: \(glucoseImpact.jsRounded()), CR: \(forecast.adjustedCarbRatio.jsRounded(scale: 1)), Target: \(targetLog), minPredBG \(forecast.minForecastedGlucose.jsRounded()), minGuardBG \(forecast.minGuardGlucose.jsRounded()), IOBpredBG \(lastIOBpredBG)"
 
         if let lastCOB = lastCOBpredBG {
             reason += ", COBpredBG \(lastCOB)"
@@ -194,7 +194,7 @@ enum DosingEngine {
 
         var carbsRequired: (carbs: Decimal, minutes: Decimal)?
         if carbsRequiredResult.carbs >= profile.carbsReqThreshold, carbsRequiredResult.minutes <= 45 {
-            reason += "\(carbsRequiredResult.carbs) add'l carbs req w/in \(carbsRequiredResult.minutes)m; "
+            // Note: carbs message is added in DetermineBasalGenerator after smbReason to match JS order
             carbsRequired = carbsRequiredResult
         }
 
@@ -297,7 +297,7 @@ enum DosingEngine {
         } else if currentGlucose < threshold || minGuardGlucose < threshold {
             let minGuardGlucoseString = String(describing: convertGlucose(profile: profile, glucose: minGuardGlucose))
             let thresholdString = String(describing: convertGlucose(profile: profile, glucose: threshold))
-            newDetermination.reason += "minGuardBG \(minGuardGlucoseString) < \(thresholdString)"
+            newDetermination.reason += "minGuardBG \(minGuardGlucoseString)<\(thresholdString)"
 
             let glucoseUndershoot = targetGlucose - minGuardGlucose
             if minGuardGlucose < threshold {
@@ -421,9 +421,10 @@ enum DosingEngine {
                     .reason +=
                     ", but Delta \(convertGlucose(profile: profile, glucose: glucoseStatus.delta)) > expectedDelta \(convertGlucose(profile: profile, glucose: expectedDelta))"
             } else {
+                let minDeltaFormatted = String(format: "%.2f", Double(truncating: minDelta.jsRounded(scale: 2) as NSNumber))
                 newDetermination
                     .reason +=
-                    ", but Min. Delta \(minDelta.jsRounded(scale: 2)) > Exp. Delta \(convertGlucose(profile: profile, glucose: expectedDelta))"
+                    ", but Min. Delta \(minDeltaFormatted) > Exp. Delta \(convertGlucose(profile: profile, glucose: expectedDelta))"
             }
 
             let roundedBasal = TempBasalFunctions.roundBasal(profile: profile, basalRate: basal)
@@ -463,8 +464,9 @@ enum DosingEngine {
         let minInsulinRequired = min(insulinRequired, naiveInsulinRequired)
 
         if insulinScheduled < minInsulinRequired - basal * 0.3 {
+            let rateFormatted = String(format: "%.2f", Double(truncating: currentTemp.rate.jsRounded(scale: 2) as NSNumber))
             newDetermination
-                .reason += ", \(currentTemp.duration)m@\(currentTemp.rate.jsRounded(scale: 2)) is a lot less than needed. "
+                .reason += ", \(currentTemp.duration)m@\(rateFormatted) is a lot less than needed. "
             let finalDetermination = try TempBasalFunctions.setTempBasal(
                 rate: rate,
                 duration: 30,
@@ -549,9 +551,10 @@ enum DosingEngine {
                     .reason +=
                     "Eventual BG \(convertGlucose(profile: profile, glucose: eventualGlucose)) > \(convertGlucose(profile: profile, glucose: minGlucose)) but Delta \(convertGlucose(profile: profile, glucose: glucoseStatus.delta)) < Exp. Delta \(convertGlucose(profile: profile, glucose: expectedDelta))"
             } else {
+                let minDeltaFormatted = String(format: "%.2f", Double(truncating: minDelta.jsRounded(scale: 2) as NSNumber))
                 newDetermination
                     .reason +=
-                    "Eventual BG \(convertGlucose(profile: profile, glucose: eventualGlucose)) > \(convertGlucose(profile: profile, glucose: minGlucose)) but Min. Delta \(minDelta.jsRounded(scale: 2)) < Exp. Delta \(convertGlucose(profile: profile, glucose: expectedDelta))"
+                    "Eventual BG \(convertGlucose(profile: profile, glucose: eventualGlucose)) > \(convertGlucose(profile: profile, glucose: minGlucose)) but Min. Delta \(minDeltaFormatted) < Exp. Delta \(convertGlucose(profile: profile, glucose: expectedDelta))"
             }
 
             let roundedBasal = TempBasalFunctions.roundBasal(profile: profile, basalRate: basal)
@@ -825,8 +828,15 @@ enum DosingEngine {
         }
 
         if let lastBolusAge {
-            let nextBolusMinutes = smbInterval - lastBolusAge
-            let nextBolusSeconds = (Int(smbInterval - lastBolusAge) * 60) % 60
+            // BUG: JS rounds minutes independently from seconds, causing double-counting when
+            // minutes rounds up. E.g., 0.6 min = 36 sec, but JS outputs "1m 36s" (96 sec).
+            // Correct logic would be:
+            //   let totalSeconds = Int(((smbInterval - lastBolusAge) * 60).jsRounded())
+            //   let nextBolusMinutes = totalSeconds / 60
+            //   let nextBolusSeconds = totalSeconds % 60
+            // Keeping JS behavior for now to match outputs.
+            let nextBolusMinutes = (smbInterval - lastBolusAge).jsRounded()
+            let nextBolusSeconds = Int(((smbInterval - lastBolusAge) * 60).jsRounded()) % 60
 
             if lastBolusAge > smbInterval {
                 if microBolus > 0 {
@@ -864,14 +874,15 @@ enum DosingEngine {
         let maxSafeBasal = try TempBasalFunctions.getMaxSafeBasalRate(profile: profile)
 
         if rate > maxSafeBasal {
-            newDetermination.reason += "adj. req. rate: \(rate) to maxSafeBasal: \(maxSafeBasal), "
+            newDetermination.reason += "adj. req. rate: \(rate) to maxSafeBasal: \(maxSafeBasal.jsRounded(scale: 2)), "
             rate = TempBasalFunctions.roundBasal(profile: profile, basalRate: maxSafeBasal)
         }
 
         let insulinScheduled = Decimal(currentTemp.duration) * (currentTemp.rate - basal) / 60
         if insulinScheduled >= insulinRequired * 2 {
+            let rateFormatted = String(format: "%.2f", Double(truncating: currentTemp.rate.jsRounded(scale: 2) as NSNumber))
             newDetermination.reason +=
-                "\(currentTemp.duration)m@\(currentTemp.rate.jsRounded(scale: 2)) > 2 * insulinReq. Setting temp basal of \(rate)U/hr. "
+                "\(currentTemp.duration)m@\(rateFormatted) > 2 * insulinReq. Setting temp basal of \(rate)U/hr. "
             let finalDetermination = try TempBasalFunctions.setTempBasal(
                 rate: rate,
                 duration: 30,

+ 17 - 2
Trio/Sources/APS/OpenAPSSwift/DetermineBasal/DynamicISF.swift

@@ -8,6 +8,10 @@ struct DynamicISFResult {
     let tddRatio: Decimal
     /// The calculated insulin factor (120 - peak time), used in the logarithmic formula.
     let insulinFactor: Decimal
+    /// The ratio before clamping was applied.
+    let uncappedRatio: Decimal
+    /// The limit value if the ratio was clamped, nil otherwise.
+    let limitValue: Decimal?
 }
 
 enum DynamicISF {
@@ -89,10 +93,21 @@ enum DynamicISF {
             newRatio = sensitivity * preferences.adjustmentFactor * tdd * (Decimal.log((bg / insulinFactor) + 1) / 1800)
         }
 
+        let clampedRatio = newRatio.clamp(lowerBound: minLimit, upperBound: maxLimit)
+        let limitValue: Decimal? = if newRatio > maxLimit {
+            maxLimit
+        } else if newRatio < minLimit {
+            minLimit
+        } else {
+            nil
+        }
+
         return DynamicISFResult(
-            ratio: newRatio.clamp(lowerBound: minLimit, upperBound: maxLimit),
+            ratio: clampedRatio,
             tddRatio: clampedTddRatio,
-            insulinFactor: insulinFactor
+            insulinFactor: insulinFactor,
+            uncappedRatio: newRatio,
+            limitValue: limitValue
         )
     }
 }

+ 3 - 3
Trio/Sources/APS/OpenAPSSwift/DetermineBasal/TempBasalFunctions.swift

@@ -89,18 +89,18 @@ enum TempBasalFunctions {
                 if currentTemp.duration > 0 {
                     determination
                         .reason = determination.reason +
-                        "Suggested rate is same as profile rate, a temp basal is active, canceling current temp"
+                        ". Suggested rate is same as profile rate, a temp basal is active, canceling current temp"
                     determination.duration = 0
                     determination.rate = 0
                     return determination
                 } else {
                     determination
                         .reason = determination.reason +
-                        "Suggested rate is same as profile rate, no temp basal is active, doing nothing"
+                        ". Suggested rate is same as profile rate, no temp basal is active, doing nothing"
                     return determination
                 }
             } else {
-                determination.reason = determination.reason + "Setting neutral temp basal of \(profile.currentBasal ?? 0)U/hr"
+                determination.reason = determination.reason + ". Setting neutral temp basal of \(profile.currentBasal ?? 0)U/hr"
                 determination.duration = duration
                 determination.rate = suggestedRate
                 return determination

+ 2 - 10
Trio/Sources/APS/OpenAPSSwift/Logging/OrefFunction.swift

@@ -43,24 +43,16 @@ enum OrefFunction: String, Codable {
         case .autosens:
             return Set(["deviationsUnsorted", "debugInfo"])
         case .determineBasal:
-            // FIXME: Adjust as we go
+            // We ignore some properties that aren't used downstream
             return Set([
-                // Final dosing calculation
-                // "units",
-                // "insulinReq",
-                // "rate",
-                // "duration",
-                // Not calculating yet
+                // Not used, ignore
                 "id",
-                "deliverAt",
                 "temp",
                 "reservoir",
                 "ISF",
-                "current_target",
                 "TDD",
                 "minDelta",
                 "received",
-                "reason",
                 // intentionally removed from Swift, but in JS
                 "insulinForManualBolus",
                 "manualBolusErrorString",

+ 2 - 3
TrioTests/OpenAPSSwiftTests/DetermineBasalEarlyExitTests.swift

@@ -393,7 +393,7 @@ import Testing
         #expect(
             result?
                 .reason ==
-                "Safety check: currentTemp does not match lastTemp in IOB or lastTemp ended long ago; canceling temp basal."
+                "Warning: currenttemp rate 1.5 != lastTemp rate 1 from pumphistory; canceling temp"
         )
     }
 
@@ -445,8 +445,7 @@ import Testing
         // Note: In swift we use a different reason then JS
         #expect(
             result?
-                .reason ==
-                "Safety check: currentTemp does not match lastTemp in IOB or lastTemp ended long ago; canceling temp basal."
+                .reason == "Warning: currenttemp running but lastTemp from pumphistory ended 10m ago; canceling temp"
         )
     }
 

+ 1 - 1
TrioTests/OpenAPSSwiftTests/SetTempBasalTests.swift

@@ -298,6 +298,6 @@ import Testing
 
         #expect(requestedTemp.rate == 0.8)
         #expect(requestedTemp.duration == 30)
-        #expect(requestedTemp.reason == "Setting neutral temp basal of 0.8U/hr")
+        #expect(requestedTemp.reason == ". Setting neutral temp basal of 0.8U/hr")
     }
 }

Rozdílová data souboru nebyla zobrazena, protože soubor je příliš velký
+ 1 - 1
TrioTests/OpenAPSSwiftTests/javascript/bundle/determine-basal.js