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

Rename some variables in meal module for clarity

Deniz Cengiz 11 месяцев назад
Родитель
Сommit
007c2aaa8f

+ 29 - 25
Trio/Sources/APS/OpenAPSSwift/Meal/MealCob.swift

@@ -35,7 +35,7 @@ struct MealCob {
         basalProfile: [BasalProfileEntry],
         profile: Profile,
         mealDate: Date,
-        ciDate: Date?
+        carbImpactDate: Date?
     ) throws -> CobResult {
         let treatments = try IobHistory.calcTempTreatments(
             history: pumpHistory.map { $0.computedEvent() },
@@ -49,7 +49,7 @@ struct MealCob {
             glucose: glucose,
             profile: profile,
             mealDate: mealDate,
-            ciDate: ciDate
+            carbImpactDate: carbImpactDate
         )
 
         return try calculateCarbAbsorption(
@@ -58,7 +58,7 @@ struct MealCob {
             basalProfile: basalProfile,
             profile: profile,
             mealDate: mealDate,
-            ciDate: ciDate
+            carbImpactDate: carbImpactDate
         )
     }
 
@@ -88,7 +88,7 @@ struct MealCob {
         glucose: [BloodGlucose],
         profile: Profile,
         mealDate: Date,
-        ciDate: Date?
+        carbImpactDate: Date?
     ) throws -> [BucketedGlucose] {
         var glucoseData = glucose.compactMap({ (bg: BloodGlucose) -> BucketedGlucose? in
             guard let glucose = bg.glucose ?? bg.sgv else { return nil }
@@ -105,8 +105,9 @@ struct MealCob {
 
         // Only consider last ~45m of data in CI mode
         // this allows us to calculate deviations for the last ~30m
-        if let ciDate = ciDate {
-            glucoseData = glucoseData.filter { ciDate >= $0.date && ciDate.timeIntervalSince($0.date) <= 45.minutesToSeconds }
+        if let carbImpactDate = carbImpactDate {
+            glucoseData = glucoseData
+                .filter { carbImpactDate >= $0.date && carbImpactDate.timeIntervalSince($0.date) <= 45.minutesToSeconds }
         }
 
         for glucose in glucoseData {
@@ -140,7 +141,7 @@ struct MealCob {
         basalProfile: [BasalProfileEntry],
         profile: Profile,
         mealDate: Date,
-        ciDate: Date?
+        carbImpactDate: Date?
     ) throws -> CobResult {
         var carbsAbsorbed: Decimal = 0
         var currentDeviation: Decimal = 0
@@ -153,43 +154,46 @@ struct MealCob {
         // Process bucketed data (excluding last 3 entries to avoid incomplete deltas)
         // If bucketed data < 4, skips loop and just returns default values, matching JS behavior
         for bucketCount in 0 ..< max(0, bucketedData.count - 3) {
-            let bgTime = bucketedData[bucketCount].date
-            let bg = bucketedData[bucketCount].glucose
+            let glucoseTime = bucketedData[bucketCount].date
+            let glucose = bucketedData[bucketCount].glucose
 
             // Skip invalid glucose readings
-            guard bg >= 39, bucketedData[bucketCount + 3].glucose >= 39 else {
+            guard glucose >= 39, bucketedData[bucketCount + 3].glucose >= 39 else {
                 continue
             }
 
             guard let isfProfile = profile.isfProfile?.toInsulinSensitivities() else {
                 throw CobError.missingIsfProfile
             }
-            let (sensitivity, _) = try Isf.isfLookup(isfDataInput: isfProfile, timestamp: bgTime)
+            let (sensitivity, _) = try Isf.isfLookup(isfDataInput: isfProfile, timestamp: glucoseTime)
             guard sensitivity > 0 else {
                 throw CobError.isfLookupError
             }
 
-            let avgDelta = (bg - bucketedData[bucketCount + 3].glucose) / 3
-            let delta = bg - bucketedData[bucketCount + 1].glucose
+            let avgDelta = (glucose - bucketedData[bucketCount + 3].glucose) / 3
+            let delta = glucose - bucketedData[bucketCount + 1].glucose
 
             var simulationProfile = profile
-            simulationProfile.currentBasal = try Basal.basalLookup(basalProfile, now: bgTime)
+            simulationProfile.currentBasal = try Basal.basalLookup(basalProfile, now: glucoseTime)
 
-            let iob = try IobCalculation.iobTotal(treatments: treatments, profile: simulationProfile, time: bgTime)
+            let iob = try IobCalculation.iobTotal(treatments: treatments, profile: simulationProfile, time: glucoseTime)
 
             // Copying Javascript rounding
-            let bgi: Decimal = (-iob.activity * sensitivity * 5 * 100 + 0.5).rounded(scale: 0, roundingMode: .down) / 100
-            let deviation = delta - bgi
+            // JS oref calls this "big" = "blood glucose impact"
+            let glucoseImpact: Decimal = (-iob.activity * sensitivity * 5 * 100 + 0.5)
+                .rounded(scale: 0, roundingMode: .down) / 100
+            let deviation = delta - glucoseImpact
 
             // Calculate the deviation right now, for use in min_5m
             if bucketCount == 0 {
-                currentDeviation = ((avgDelta - bgi) * 1000).rounded() / 1000
-                if let ciDate = ciDate, ciDate > bgTime {
+                currentDeviation = ((avgDelta - glucoseImpact) * 1000).rounded() / 1000
+                if let carbImpactDate = carbImpactDate, carbImpactDate > glucoseTime {
                     allDeviations.append(currentDeviation.rounded())
                 }
-            } else if let ciDate = ciDate, ciDate > bgTime {
-                let avgDeviation = ((avgDelta - bgi) * 1000).rounded() / 1000
-                let deviationSlope = (avgDeviation - currentDeviation) / Decimal(bgTime.timeIntervalSince(ciDate)) * 1000 * 60 * 5
+            } else if let carbImpactDate = carbImpactDate, carbImpactDate > glucoseTime {
+                let avgDeviation = ((avgDelta - glucoseImpact) * 1000).rounded() / 1000
+                let deviationSlope = (avgDeviation - currentDeviation) / Decimal(glucoseTime.timeIntervalSince(carbImpactDate)) *
+                    1000 * 60 * 5
 
                 if avgDeviation > maxDeviation {
                     slopeFromMaxDeviation = min(0, deviationSlope)
@@ -203,8 +207,8 @@ struct MealCob {
                 allDeviations.append(avgDeviation.rounded())
             }
 
-            // If bgTime is more recent than mealTime
-            if bgTime > mealDate {
+            // If glucoseTime is more recent than mealTime
+            if glucoseTime > mealDate {
                 guard let carbRatio = profile.carbRatio else {
                     throw CobError.missingCarbRatioInProfile
                 }
@@ -233,5 +237,5 @@ enum CobError: Error {
     case missingIsfProfile
     case isfLookupError
     case missingCarbRatioInProfile
-    case couldNotDetermineLastBgTime
+    case couldNotDetermineLastglucoseTime
 }

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

@@ -22,7 +22,7 @@ struct COBInputs {
     let iobInputs: IOBInput
     let basalProfile: [BasalProfileEntry]
     var mealDate: Date
-    var ciDate: Date?
+    var carbImpactDate: Date?
 }
 
 enum MealTotal {
@@ -78,7 +78,7 @@ enum MealTotal {
                         basalProfile: cobInputs.basalProfile,
                         profile: cobInputs.iobInputs.profile,
                         mealDate: cobInputs.mealDate,
-                        ciDate: cobInputs.ciDate
+                        carbImpactDate: cobInputs.carbImpactDate
                     ).carbsAbsorbed
 
                     // TODO: add logging?
@@ -98,7 +98,7 @@ enum MealTotal {
         carbs -= carbsToRemove
 
         // calculate the current deviation and steepest deviation downslope over the last hour
-        cobInputs.ciDate = time
+        cobInputs.carbImpactDate = time
         cobInputs.mealDate = time - Double(profile.maxMealAbsorptionTime) * 3600
 
         // set a hard upper limit on COB to mitigate impact of erroneous or malicious carb entry
@@ -111,7 +111,7 @@ enum MealTotal {
             basalProfile: cobInputs.basalProfile,
             profile: cobInputs.iobInputs.profile,
             mealDate: cobInputs.mealDate,
-            ciDate: cobInputs.ciDate
+            carbImpactDate: cobInputs.carbImpactDate
         )
 
         // if currentDeviation is null or maxDeviation is 0, set mealCOB to 0 for zombie-carb safety

+ 6 - 6
TrioTests/OpenAPSSwiftTests/MealCobBucketingTests.swift

@@ -46,7 +46,7 @@ import Testing
             glucose: glucose_data,
             profile: createDefaultProfile(),
             mealDate: mealTime,
-            ciDate: nil
+            carbImpactDate: nil
         )
 
         // Should return same number of entries
@@ -74,7 +74,7 @@ import Testing
             glucose: glucose_data,
             profile: createDefaultProfile(),
             mealDate: mealTime,
-            ciDate: nil
+            carbImpactDate: nil
         )
 
         // Should have interpolated 4 additional points (5, 10, 15, 20 minutes)
@@ -116,7 +116,7 @@ import Testing
             glucose: glucose_data,
             profile: profile,
             mealDate: mealTime,
-            ciDate: nil
+            carbImpactDate: nil
         )
 
         // Should only process up to 2 hours of data (24 entries + 1 initial = 25)
@@ -148,7 +148,7 @@ import Testing
             glucose: glucose_data,
             profile: createDefaultProfile(),
             mealDate: mealTime,
-            ciDate: ciTime
+            carbImpactDate: ciTime
         )
 
         // Should only include data within 45 minutes of ciTime
@@ -180,7 +180,7 @@ import Testing
             glucose: glucose_data,
             profile: createDefaultProfile(),
             mealDate: mealTime,
-            ciDate: nil
+            carbImpactDate: nil
         )
 
         // Should only process from meal time forward (in reverse chronological order)
@@ -213,7 +213,7 @@ import Testing
             glucose: glucose_data,
             profile: createDefaultProfile(),
             mealDate: mealTime,
-            ciDate: nil
+            carbImpactDate: nil
         )
 
         // Close readings should be averaged (in reverse chronological order)

+ 15 - 15
TrioTests/OpenAPSSwiftTests/MealCobTests.swift

@@ -38,7 +38,7 @@ import Testing
 
     @Test("should detect carb absorption with rising glucose") func detectCarbAbsorptionWithRisingGlucose() async throws {
         let mealTime = Date.from(isoString: "2016-06-19T12:00:00-04:00")
-        let ciTime = Date.from(isoString: "2016-06-19T13:00:00-04:00")
+        let carbImpactTime = Date.from(isoString: "2016-06-19T13:00:00-04:00")
 
         // Create glucose data showing significant rise after meal
         let glucoseValues = [100, 105, 110, 115, 120, 130, 140, 150, 155, 160, 160, 160, 160]
@@ -48,26 +48,26 @@ import Testing
         let basalProfile = createBasalProfile()
         let pumpHistory: [PumpHistoryEvent] = []
 
-        // Test with ciTime
+        // Test with carbImpactTime
         var result = try MealCob.detectCarbAbsorption(
             glucose: glucoseData,
             pumpHistory: pumpHistory,
             basalProfile: basalProfile,
             profile: profile,
             mealDate: mealTime,
-            ciDate: ciTime
+            carbImpactDate: carbImpactTime
         )
 
         #expect(result.carbsAbsorbed.isWithin(0.01, of: 9.75))
 
-        // Test without ciTime
+        // Test without carbImpactTime
         result = try MealCob.detectCarbAbsorption(
             glucose: glucoseData,
             pumpHistory: pumpHistory,
             basalProfile: basalProfile,
             profile: profile,
             mealDate: mealTime,
-            ciDate: nil
+            carbImpactDate: nil
         )
 
         #expect(result.carbsAbsorbed.isWithin(0.01, of: 14.75))
@@ -75,7 +75,7 @@ import Testing
 
     @Test("should handle stable glucose (no carb absorption)") func handleStableGlucose() async throws {
         let mealTime = Date.from(isoString: "2016-06-19T12:00:00-04:00")
-        let ciTime = Date.from(isoString: "2016-06-19T13:00:00-04:00")
+        let carbImpactTime = Date.from(isoString: "2016-06-19T13:00:00-04:00")
 
         // Create stable glucose data
         let glucoseValues = [100, 100, 100, 100, 100, 100]
@@ -91,7 +91,7 @@ import Testing
             basalProfile: basalProfile,
             profile: profile,
             mealDate: mealTime,
-            ciDate: ciTime
+            carbImpactDate: carbImpactTime
         )
 
         #expect(result.carbsAbsorbed == 0)
@@ -99,7 +99,7 @@ import Testing
 
     @Test("should handle falling glucose (negative deviation)") func handleFallingGlucose() async throws {
         let mealTime = Date.from(isoString: "2016-06-19T12:00:00-04:00")
-        let ciTime = Date.from(isoString: "2016-06-19T13:00:00-04:00")
+        let carbImpactTime = Date.from(isoString: "2016-06-19T13:00:00-04:00")
 
         // Create falling glucose data: 150 -> 125
         let glucoseValues = [150, 145, 140, 135, 130, 125]
@@ -115,7 +115,7 @@ import Testing
             basalProfile: basalProfile,
             profile: profile,
             mealDate: mealTime,
-            ciDate: ciTime
+            carbImpactDate: carbImpactTime
         )
 
         #expect(result.carbsAbsorbed == 0) // No carbs absorbed when glucose is falling
@@ -123,7 +123,7 @@ import Testing
 
     @Test("should stop processing when pre-meal BG is found") func stopProcessingWhenPreMealBGFound() async throws {
         let mealTime = Date.from(isoString: "2016-06-19T12:00:00-04:00")
-        let ciTime = Date.from(isoString: "2016-06-19T13:00:00-04:00")
+        let carbImpactTime = Date.from(isoString: "2016-06-19T13:00:00-04:00")
 
         // Include glucose data from before meal time
         let glucoseData = [
@@ -155,7 +155,7 @@ import Testing
             basalProfile: basalProfile,
             profile: profile,
             mealDate: mealTime,
-            ciDate: ciTime
+            carbImpactDate: carbImpactTime
         )
 
         #expect(result.carbsAbsorbed.isWithin(0.01, of: 3.75))
@@ -163,7 +163,7 @@ import Testing
 
     @Test("should respect maxMealAbsorptionTime") func respectMaxMealAbsorptionTime() async throws {
         let mealTime = Date.from(isoString: "2016-06-19T12:00:00-04:00")
-        let ciTime = Date.from(isoString: "2016-06-19T13:00:00-04:00")
+        let carbImpactTime = Date.from(isoString: "2016-06-19T13:00:00-04:00")
 
         // Create glucose data spanning longer than maxMealAbsorptionTime
         var glucoseValues: [Int] = []
@@ -188,7 +188,7 @@ import Testing
             basalProfile: basalProfile,
             profile: profile,
             mealDate: mealTime,
-            ciDate: ciTime
+            carbImpactDate: carbImpactTime
         )
 
         // For this check, the Swift implementation is very
@@ -201,7 +201,7 @@ import Testing
 
     @Test("should handle minimum carb impact from profile") func handleMinimumCarbImpactFromProfile() async throws {
         let mealTime = Date.from(isoString: "2016-06-19T12:00:00-04:00")
-        let ciTime: Date? = nil
+        let carbImpactTime: Date? = nil
 
         // Create glucose data with slight rise to trigger carb absorption
         let glucoseValues = [100, 101, 102, 103, 104, 105]
@@ -218,7 +218,7 @@ import Testing
             basalProfile: basalProfile,
             profile: profile,
             mealDate: mealTime,
-            ciDate: ciTime
+            carbImpactDate: carbImpactTime
         )
 
         #expect(result.carbsAbsorbed.isWithin(0.01, of: 3.75))