Просмотр исходного кода

Merge pull request #1219 from AndreasStokholm/hotfix/delivery-limits

Trio v0.8.3 Hotfix: Fix Delivery Limit Regression on Silent PumpManagerRawState Migration
Deniz Cengiz 22 часов назад
Родитель
Сommit
7b8db13aca

+ 2 - 2
Config.xcconfig

@@ -18,8 +18,8 @@ BUNDLE_IDENTIFIER = org.nightscout.$(DEVELOPMENT_TEAM).trio
 TRIO_APP_GROUP_ID = group.org.nightscout.$(DEVELOPMENT_TEAM).trio.trio-app-group
 
 // The developers set the version numbers, please leave them alone
-APP_VERSION = 0.8.2
-APP_DEV_VERSION = 0.8.2
+APP_VERSION = 0.8.3
+APP_DEV_VERSION = 0.8.3
 APP_BUILD_NUMBER = 1
 COPYRIGHT_NOTICE =
 

+ 1 - 1
OmnipodKit

@@ -1 +1 @@
-Subproject commit 80069aa0d7562814187e6b0eed499277f5841cf5
+Subproject commit 4d254498cf016806b3e0ec33dd5fbfdf6e2135a6

+ 4 - 0
Trio.xcodeproj/project.pbxproj

@@ -525,6 +525,7 @@
 		CEE9A6592BBB418300EB5194 /* CalibrationsDataFlow.swift in Sources */ = {isa = PBXBuildFile; fileRef = CEE9A6542BBB418300EB5194 /* CalibrationsDataFlow.swift */; };
 		CEE9A65C2BBB41C800EB5194 /* CalibrationService.swift in Sources */ = {isa = PBXBuildFile; fileRef = CEE9A65B2BBB41C800EB5194 /* CalibrationService.swift */; };
 		CEE9A65E2BBC9F6500EB5194 /* CalibrationsTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = CEE9A65D2BBC9F6500EB5194 /* CalibrationsTests.swift */; };
+		CA02000000000000000010C2 /* DeliveryLimitsSyncTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = CA02000000000000000010C1 /* DeliveryLimitsSyncTests.swift */; };
 		CEF1ED6B2D58FB5800FAF41E /* CGMOptions.swift in Sources */ = {isa = PBXBuildFile; fileRef = CEF1ED6A2D58FB4600FAF41E /* CGMOptions.swift */; };
 		D6D02515BBFBE64FEBE89856 /* HistoryRootView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 881E04BA5E0A003DE8E0A9C6 /* HistoryRootView.swift */; };
 		D6DEC113821A7F1056C4AA1E /* NightscoutConfigDataFlow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2F2A13DF0EDEEEDC4106AA2A /* NightscoutConfigDataFlow.swift */; };
@@ -1387,6 +1388,7 @@
 		CEE9A6542BBB418300EB5194 /* CalibrationsDataFlow.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CalibrationsDataFlow.swift; sourceTree = "<group>"; };
 		CEE9A65B2BBB41C800EB5194 /* CalibrationService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CalibrationService.swift; sourceTree = "<group>"; };
 		CEE9A65D2BBC9F6500EB5194 /* CalibrationsTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CalibrationsTests.swift; sourceTree = "<group>"; };
+		CA02000000000000000010C1 /* DeliveryLimitsSyncTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DeliveryLimitsSyncTests.swift; sourceTree = "<group>"; };
 		CEF1ED6A2D58FB4600FAF41E /* CGMOptions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CGMOptions.swift; sourceTree = "<group>"; };
 		CFCFE0781F9074C2917890E8 /* ManualTempBasalStateModel.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = ManualTempBasalStateModel.swift; sourceTree = "<group>"; };
 		D0BDC6993C1087310EDFC428 /* CarbRatioEditorRootView.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = CarbRatioEditorRootView.swift; sourceTree = "<group>"; };
@@ -2732,6 +2734,7 @@
 				BD8FC0552D66187700B95AED /* CoreDataTests */,
 				38FCF3F125E9028E0078B0D1 /* Info.plist */,
 				CEE9A65D2BBC9F6500EB5194 /* CalibrationsTests.swift */,
+				CA02000000000000000010C1 /* DeliveryLimitsSyncTests.swift */,
 				3BAAE60B2DE776630049589B /* DynamicISFEnableTests.swift */,
 				38FCF3F825E902C20078B0D1 /* FileStorageTests.swift */,
 				3B997DCE2DC00A3A006B6BB2 /* JSONImporterTests.swift */,
@@ -4939,6 +4942,7 @@
 				BD8FC0542D66186000B95AED /* TestError.swift in Sources */,
 				DDDD0FFB2F4E22C000F9C645 /* GlucoseSmoothingTests.swift in Sources */,
 				CEE9A65E2BBC9F6500EB5194 /* CalibrationsTests.swift in Sources */,
+				CA02000000000000000010C2 /* DeliveryLimitsSyncTests.swift in Sources */,
 				BD8FC0622D6619E600B95AED /* OverrideStorageTests.swift in Sources */,
 				BD8FC0592D66189700B95AED /* TestAssembly.swift in Sources */,
 				DDC6CA6D2DD90A2A0060EE25 /* LocalizationTests.swift in Sources */,

+ 20 - 0
Trio/Sources/APS/DeviceDataManager.swift

@@ -3,6 +3,7 @@ import Combine
 import CoreData
 import DanaKit
 import Foundation
+import HealthKit
 import LoopKit
 import LoopKitUI
 import MedtrumKit
@@ -106,6 +107,25 @@ final class BaseDeviceDataManager: DeviceDataManager, Injectable {
                     .bolusIncrement = bolusIncrement > 0 ? bolusIncrement : 0.1
                 storage.save(modifiedPreferences, as: OpenAPS.Settings.preferences)
 
+                // Ensure the pump manager's delivery limits always reflect the user's
+                // current settings. Without this, the active pump manager instance
+                // may use a stale or default value from deserialized state
+                // — silently rejecting temp basals that oref correctly
+                // determines are within the user's configured maxBasal.
+                let pumpSettings = settingsManager.pumpSettings
+                let deliveryLimits = DeliveryLimits(
+                    maximumBasalRate: HKQuantity(unit: .internationalUnitsPerHour, doubleValue: Double(pumpSettings.maxBasal)),
+                    maximumBolus: HKQuantity(unit: .internationalUnit(), doubleValue: Double(pumpSettings.maxBolus))
+                )
+
+                processQueue.async {
+                    pumpManager.syncDeliveryLimits(limits: deliveryLimits) { result in
+                        if case let .failure(error) = result {
+                            debug(.deviceManager, "syncDeliveryLimits on pump manager init failed: \(error)")
+                        }
+                    }
+                }
+
                 if let medtrumPump = pumpManager as? MedtrumPumpManager {
                     // Medtrum's state.patchExpiresAt is actually lifespan + grace
                     // keeping this in line with omnipod, we will use just the lifetime

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

@@ -183433,6 +183433,7 @@
       }
     },
     "Note to Medtronic Pump Users: You must also manually set the max basal rate on the pump to this value or higher." : {
+      "extractionState" : "stale",
       "localizations" : {
         "bg" : {
           "stringUnit" : {

+ 0 - 3
Trio/Sources/Modules/GeneralSettings/View/UnitsLimitsSettingsRootView.swift

@@ -129,9 +129,6 @@ extension UnitsLimitsSettings {
                         Text(
                             "This is the maximum basal rate allowed to be set or scheduled. This applies to both automatic and manual basal rates."
                         )
-                        Text(
-                            "Note to Medtronic Pump Users: You must also manually set the max basal rate on the pump to this value or higher."
-                        )
                     }
                 )
 

+ 0 - 3
Trio/Sources/Modules/Onboarding/View/OnboardingView+Util.swift

@@ -435,9 +435,6 @@ enum DeliveryLimitSubstep: Int, CaseIterable, Identifiable {
                 Text(
                     "This is the maximum basal rate allowed to be set or scheduled. This applies to both automatic and manual basal rates."
                 )
-                Text(
-                    "Note to Medtronic Pump Users: You must also manually set the max basal rate on the pump to this value or higher."
-                )
             }
         case .maxCOB:
             return VStack(alignment: .leading, spacing: 8) {

+ 67 - 0
TrioTests/DeliveryLimitsSyncTests.swift

@@ -0,0 +1,67 @@
+import Foundation
+import HealthKit
+import LoopKit
+import Testing
+
+@testable import Trio
+
+/// Pins the `PumpSettings` → `DeliveryLimits` mapping that
+/// `BaseDeviceDataManager.pumpManager.didSet` performs inline before calling
+/// `pumpManager.syncDeliveryLimits`. Keep this builder identical to the inline
+/// expression in `DeviceDataManager.swift` so a regression that re-introduces
+/// the default-collapse bug breaks here too.
+@Suite("Delivery Limits Sync Tests") struct DeliveryLimitsSyncTests {
+    private let basalUnit = HKUnit.internationalUnitsPerHour
+    private let bolusUnit = HKUnit.internationalUnit()
+
+    private func makeSettings(maxBasal: Decimal, maxBolus: Decimal) -> PumpSettings {
+        PumpSettings(insulinActionCurve: 6, maxBolus: maxBolus, maxBasal: maxBasal)
+    }
+
+    private func deliveryLimits(from settings: PumpSettings) -> DeliveryLimits {
+        DeliveryLimits(
+            maximumBasalRate: HKQuantity(unit: .internationalUnitsPerHour, doubleValue: Double(settings.maxBasal)),
+            maximumBolus: HKQuantity(unit: .internationalUnit(), doubleValue: Double(settings.maxBolus))
+        )
+    }
+
+    @Test("maxBasal maps to maximumBasalRate in U/hr") func testMaxBasalMapping() {
+        let settings = makeSettings(maxBasal: 5.0, maxBolus: 10.0)
+
+        let limits = deliveryLimits(from: settings)
+
+        #expect(limits.maximumBasalRate?.doubleValue(for: basalUnit) == 5.0)
+    }
+
+    @Test("maxBolus maps to maximumBolus in U") func testMaxBolusMapping() {
+        let settings = makeSettings(maxBasal: 5.0, maxBolus: 10.0)
+
+        let limits = deliveryLimits(from: settings)
+
+        #expect(limits.maximumBolus?.doubleValue(for: bolusUnit) == 10.0)
+    }
+
+    /// The derived limit must be the user's configured value, not the `PumpInitialSettings` default.
+    @Test("User-configured limit is preserved, not collapsed to the 2 U/hr default") func testDoesNotFallBackToDefault() {
+        let configuredMaxBasal: Decimal = 3.0
+        let defaultMaxBasal = PumpConfig.PumpInitialSettings.default.maxBasalRateUnitsPerHour
+
+        let settings = makeSettings(maxBasal: configuredMaxBasal, maxBolus: 10.0)
+        let limits = deliveryLimits(from: settings)
+
+        #expect(limits.maximumBasalRate?.doubleValue(for: basalUnit) == Double(configuredMaxBasal))
+        #expect(limits.maximumBasalRate?.doubleValue(for: basalUnit) != defaultMaxBasal)
+    }
+
+    /// The derived limit must be the user's configured value, not the `PumpInitialSettings` default.
+    @Test("User-configured bolus limit is preserved, not collapsed to the default") func testBolusDoesNotFallBackToDefault() {
+        let configuredMaxBolus: Decimal = 25.0
+        let defaultMaxBolus = PumpConfig.PumpInitialSettings.default.maxBolusUnits
+
+        let settings = makeSettings(maxBasal: 3.0, maxBolus: configuredMaxBolus)
+        let limits = deliveryLimits(from: settings)
+
+        #expect(limits.maximumBolus?.doubleValue(for: bolusUnit) == Double(configuredMaxBolus))
+        #expect(limits.maximumBolus?.doubleValue(for: bolusUnit) != defaultMaxBolus)
+    }
+}

+ 45 - 0
blacklisted-versions.json

@@ -5,6 +5,51 @@
         },
         {
             "version": "0.0.2"
+        },
+        {
+            "version": "0.2.0"
+        },
+        {
+            "version": "0.2.1"
+        },
+        {
+            "version": "0.2.2"
+        },
+        {
+            "version": "0.2.3"
+        },
+        {
+            "version": "0.2.4"
+        },
+        {
+            "version": "0.2.5"
+        },
+        {
+            "version": "0.2.6"
+        },
+        {
+            "version": "0.2.7"
+        },
+        {
+            "version": "0.2.8"
+        },
+        {
+            "version": "0.2.9"
+        },
+        {
+            "version": "0.5.0"
+        },
+        {
+            "version": "0.5.1"
+        },
+        {
+            "version": "0.6.0"
+        },
+        {
+            "version": "0.8.1"
+        },
+        {
+            "version": "0.8.2"
         }
     ]
 }