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

Merge pull request #551 from nightscout/oref-swift-determine-basal-fixes

Fixes two crashing bugs from oref swift determineBasal
Sam King 10 месяцев назад
Родитель
Сommit
37d866260c

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

@@ -1,6 +1,13 @@
 import Foundation
 
 extension DeterminationGenerator {
+    /// helper struct for managing glucose
+    private struct GlucoseReading {
+        let glucose: Int
+        let date: Date
+        let noise: Int?
+    }
+
     /// Smooths given CGM readings, and computes rolling delta statistics
     /// (i.e., last, short-term, and long-term).
     ///
@@ -20,6 +27,12 @@ extension DeterminationGenerator {
     /// - Returns: `nil` if no valid glucose readings are found in the past day.
     static func getGlucoseStatus(glucoseReadings: [BloodGlucose]) throws -> GlucoseStatus? {
         // FIXME: put this here for now; use implementation in GlucoseStorage later (already implemented and commented out for now)
+
+        let glucoseReadings = glucoseReadings.compactMap { reading -> GlucoseReading? in
+            guard let glucose = reading.glucose ?? reading.sgv else { return nil }
+            return GlucoseReading(glucose: glucose, date: reading.dateString, noise: reading.noise)
+        }
+
         guard glucoseReadings.isNotEmpty else {
             return nil
         }
@@ -27,9 +40,9 @@ extension DeterminationGenerator {
         // Sort descending (newest first)
         let sorted = glucoseReadings.sorted { $0.date > $1.date }
 
-        let mostRecentGlucose = sorted[0]
-        var mostRecentGlucoseReading: Int = mostRecentGlucose.glucose!
-        var mostRecentGlucoseDate: Date = mostRecentGlucose.dateString
+        guard let mostRecentGlucose = sorted.first else { return nil }
+        var mostRecentGlucoseReading: Int = mostRecentGlucose.glucose
+        var mostRecentGlucoseDate: Date = mostRecentGlucose.date
 
         var lastDeltas: [Decimal] = []
         var shortDeltas: [Decimal] = []
@@ -42,20 +55,20 @@ extension DeterminationGenerator {
             // so we omit this check
 
             // only use readings >38 mg/dL (to skip code values, <39)
-            guard let glucose = entry.glucose, glucose > 38 else { continue }
+            guard entry.glucose > 38 else { continue }
 
-            let minutesAgo = mostRecentGlucoseDate.timeIntervalSince(entry.dateString) / 60
+            let minutesAgo = mostRecentGlucoseDate.timeIntervalSince(entry.date) / 60
             guard minutesAgo != 0 else { continue }
             // compute mg/dL per 5 m as a Decimal:
-            let change = Decimal(mostRecentGlucoseReading - glucose)
+            let change = Decimal(mostRecentGlucoseReading - entry.glucose)
             let avgDelta = (change / Decimal(minutesAgo)) * Decimal(5)
 
             // very-recent (<2.5 m) smooths "now"
             if minutesAgo > -2, minutesAgo <= 2.5 {
-                mostRecentGlucoseReading = (mostRecentGlucoseReading + glucose) / 2
+                mostRecentGlucoseReading = (mostRecentGlucoseReading + entry.glucose) / 2
                 mostRecentGlucoseDate = Date(
                     timeIntervalSince1970: (
-                        mostRecentGlucoseDate.timeIntervalSince1970 + entry.dateString
+                        mostRecentGlucoseDate.timeIntervalSince1970 + entry.date
                             .timeIntervalSince1970
                     ) / 2
                 )

+ 11 - 4
Trio/Sources/APS/OpenAPSSwift/DetermineBasal/DetermineBasalGenerator.swift

@@ -61,9 +61,12 @@ enum DeterminationGenerator {
         )
 
         // Safety check: current temp vs. last temp in iob
+        guard let lastTempTarget = iobData.first?.lastTemp else {
+            throw DeterminationError.missingIob
+        }
         if !checkCurrentTempBasalRateSafety(
             currentTemp: currentTemp,
-            lastTempTarget: iobData[0].lastTemp,
+            lastTempTarget: lastTempTarget,
             currentTime: currentTime
         ) {
             let reason =
@@ -77,7 +80,7 @@ enum DeterminationGenerator {
                 sensitivityRatio: nil,
                 rate: 0,
                 duration: 0,
-                iob: iobData[0].iob,
+                iob: iobData.first?.iob,
                 cob: nil,
                 predictions: nil,
                 deliverAt: currentTime,
@@ -118,7 +121,9 @@ enum DeterminationGenerator {
             withZeroTemp: true
         )
 
-        let currentGlucoseImpact = glucoseImpactSeries[0]
+        guard let currentGlucoseImpact = glucoseImpactSeries.first else {
+            throw DeterminationError.determinationError
+        }
 
         let minDelta = min(glucoseStatus.delta, glucoseStatus.shortAvgDelta)
         let minAvgDelta = min(glucoseStatus.shortAvgDelta, glucoseStatus.longAvgDelta)
@@ -135,7 +140,9 @@ enum DeterminationGenerator {
         }
 
         // Calculate what oref calls "naive eventual glucose"
-        let currentIob = iobData[0].iob
+        guard let currentIob = iobData.first?.iob else {
+            throw DeterminationError.missingIob
+        }
 
         let naiveEventualGlucose: Decimal
         if currentIob > 0 {

+ 15 - 4
Trio/Sources/APS/OpenAPSSwift/Forecasts/ForecastGenerator.swift

@@ -280,10 +280,21 @@ enum ForecastGenerator {
 
     /// Trims trailing flat-line points beyond a “lookback” count
     public static func trimFlatTails(_ series: [Decimal], lookback: Int) -> [Decimal] {
-        var s = series
-        while s.count > lookback, s.suffix(2)[0] == s.suffix(2)[1] {
-            s.removeLast()
+        guard series.count > lookback, lookback >= 0 else {
+            return series
         }
-        return s
+        let maxToRemove = series.count - lookback
+        let reversedSeries = series.reversed()
+        var removeCount = 0
+        for (curr, next) in zip(reversedSeries, reversedSeries.dropFirst()) {
+            guard curr == next else {
+                break
+            }
+            removeCount += 1
+        }
+
+        removeCount = min(maxToRemove, removeCount)
+
+        return Array(series.dropLast(removeCount))
     }
 }

+ 12 - 1
Trio/Sources/APS/OpenAPSSwift/Logging/OrefFunction.swift

@@ -46,6 +46,7 @@ enum OrefFunction: String, Codable {
         case .determineBasal:
             // FIXME: Adjust as we go
             return Set([
+                // Not calculating yet
                 "id",
                 "units",
                 "insulinReq",
@@ -62,7 +63,17 @@ enum OrefFunction: String, Codable {
                 "manualBolusErrorString",
                 "minDelta",
                 "CR",
-                "received"
+                "received",
+                "reason",
+                // in JS but not in Swift
+                "tick",
+                "BGI",
+                "target_bg",
+                "deviation",
+                // in Swift but not in JS
+                "timestamp",
+                "minGuardBG",
+                "minPredBG"
             ])
         }
     }