|
@@ -10,7 +10,6 @@ enum DosingEngine {
|
|
|
struct SMBDecision {
|
|
struct SMBDecision {
|
|
|
let isEnabled: Bool
|
|
let isEnabled: Bool
|
|
|
let manualBolusError: Int?
|
|
let manualBolusError: Int?
|
|
|
- let insulinForManualBolus: Decimal?
|
|
|
|
|
let minGuardGlucose: Decimal?
|
|
let minGuardGlucose: Decimal?
|
|
|
let reason: String?
|
|
let reason: String?
|
|
|
}
|
|
}
|
|
@@ -111,9 +110,9 @@ enum DosingEngine {
|
|
|
meal: ComputedCarbs,
|
|
meal: ComputedCarbs,
|
|
|
currentGlucose: Decimal,
|
|
currentGlucose: Decimal,
|
|
|
adjustedTargetGlucose: Decimal,
|
|
adjustedTargetGlucose: Decimal,
|
|
|
- adjustedSensitivity: Decimal,
|
|
|
|
|
|
|
+ adjustedSensitivity _: Decimal,
|
|
|
minGuardGlucose: Decimal,
|
|
minGuardGlucose: Decimal,
|
|
|
- eventualGlucose: Decimal,
|
|
|
|
|
|
|
+ eventualGlucose _: Decimal,
|
|
|
threshold: Decimal,
|
|
threshold: Decimal,
|
|
|
glucoseStatus: GlucoseStatus,
|
|
glucoseStatus: GlucoseStatus,
|
|
|
trioCustomOrefVariables: TrioCustomOrefVariables,
|
|
trioCustomOrefVariables: TrioCustomOrefVariables,
|
|
@@ -133,12 +132,10 @@ enum DosingEngine {
|
|
|
// in one place. Note: We can't shortcut the return value because
|
|
// in one place. Note: We can't shortcut the return value because
|
|
|
// the determineBasal logic always evaluates this logic
|
|
// the determineBasal logic always evaluates this logic
|
|
|
var manualBolusError: Int?
|
|
var manualBolusError: Int?
|
|
|
- var insulinForManualBolus: Decimal?
|
|
|
|
|
var minGuardGlucoseDecision: Decimal?
|
|
var minGuardGlucoseDecision: Decimal?
|
|
|
var reason: String?
|
|
var reason: String?
|
|
|
if smbIsEnabled, minGuardGlucose < threshold {
|
|
if smbIsEnabled, minGuardGlucose < threshold {
|
|
|
manualBolusError = 1
|
|
manualBolusError = 1
|
|
|
- insulinForManualBolus = ((eventualGlucose - adjustedTargetGlucose) / adjustedSensitivity).jsRounded(scale: 2)
|
|
|
|
|
minGuardGlucoseDecision = minGuardGlucose
|
|
minGuardGlucoseDecision = minGuardGlucose
|
|
|
smbIsEnabled = false
|
|
smbIsEnabled = false
|
|
|
}
|
|
}
|
|
@@ -153,7 +150,6 @@ enum DosingEngine {
|
|
|
return SMBDecision(
|
|
return SMBDecision(
|
|
|
isEnabled: smbIsEnabled,
|
|
isEnabled: smbIsEnabled,
|
|
|
manualBolusError: manualBolusError,
|
|
manualBolusError: manualBolusError,
|
|
|
- insulinForManualBolus: insulinForManualBolus,
|
|
|
|
|
minGuardGlucose: minGuardGlucoseDecision,
|
|
minGuardGlucose: minGuardGlucoseDecision,
|
|
|
reason: reason
|
|
reason: reason
|
|
|
)
|
|
)
|
|
@@ -289,12 +285,12 @@ enum DosingEngine {
|
|
|
threshold: Decimal,
|
|
threshold: Decimal,
|
|
|
overrideFactor: Decimal,
|
|
overrideFactor: Decimal,
|
|
|
profile: Profile,
|
|
profile: Profile,
|
|
|
- eventualGlucose: Decimal,
|
|
|
|
|
|
|
+ eventualGlucose _: Decimal,
|
|
|
adjustedSensitivity: Decimal,
|
|
adjustedSensitivity: Decimal,
|
|
|
targetGlucose: Decimal,
|
|
targetGlucose: Decimal,
|
|
|
currentTemp: TempBasal,
|
|
currentTemp: TempBasal,
|
|
|
determination: Determination
|
|
determination: Determination
|
|
|
- ) throws -> (setTempBasal: Bool, determination: Determination) {
|
|
|
|
|
|
|
+ ) throws -> (shouldSetTempBasal: Bool, determination: Determination) {
|
|
|
var newDetermination = determination
|
|
var newDetermination = determination
|
|
|
|
|
|
|
|
guard let currentBasal = profile.currentBasal else {
|
|
guard let currentBasal = profile.currentBasal else {
|
|
@@ -312,43 +308,40 @@ enum DosingEngine {
|
|
|
newDetermination
|
|
newDetermination
|
|
|
.reason +=
|
|
.reason +=
|
|
|
"IOB \(iobString) < \(suspendString) and minDelta \(minDeltaString) > expectedDelta \(expectedDeltaString); "
|
|
"IOB \(iobString) < \(suspendString) and minDelta \(minDeltaString) > expectedDelta \(expectedDeltaString); "
|
|
|
- return (setTempBasal: false, determination: newDetermination)
|
|
|
|
|
|
|
+ return (shouldSetTempBasal: false, determination: newDetermination)
|
|
|
} else if currentGlucose < threshold || minGuardGlucose < threshold {
|
|
} else if currentGlucose < threshold || minGuardGlucose < threshold {
|
|
|
let minGuardGlucoseString = String(describing: convertGlucose(profile: profile, glucose: minGuardGlucose))
|
|
let minGuardGlucoseString = String(describing: convertGlucose(profile: profile, glucose: minGuardGlucose))
|
|
|
let thresholdString = String(describing: convertGlucose(profile: profile, glucose: threshold))
|
|
let thresholdString = String(describing: convertGlucose(profile: profile, glucose: threshold))
|
|
|
newDetermination.reason += "minGuardBG \(minGuardGlucoseString) < \(thresholdString)"
|
|
newDetermination.reason += "minGuardBG \(minGuardGlucoseString) < \(thresholdString)"
|
|
|
|
|
|
|
|
- let bgUndershoot = targetGlucose - minGuardGlucose
|
|
|
|
|
|
|
+ let glucoseUndershoot = targetGlucose - minGuardGlucose
|
|
|
if minGuardGlucose < threshold {
|
|
if minGuardGlucose < threshold {
|
|
|
newDetermination.manualBolusErrorString = 2
|
|
newDetermination.manualBolusErrorString = 2
|
|
|
newDetermination.minGuardBG = minGuardGlucose
|
|
newDetermination.minGuardBG = minGuardGlucose
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- newDetermination.insulinForManualBolus = ((eventualGlucose - targetGlucose) / adjustedSensitivity)
|
|
|
|
|
- .rounded(toPlaces: 2)
|
|
|
|
|
-
|
|
|
|
|
- let worstCaseInsulinReq = bgUndershoot / adjustedSensitivity
|
|
|
|
|
- var durationReq = (60 * worstCaseInsulinReq / (currentBasal * overrideFactor)).rounded()
|
|
|
|
|
- durationReq = (durationReq / 30).rounded() * 30
|
|
|
|
|
- durationReq = max(30, min(120, durationReq))
|
|
|
|
|
|
|
+ let worstCaseInsulinRequired = glucoseUndershoot / adjustedSensitivity
|
|
|
|
|
+ var durationRequired = (60 * worstCaseInsulinRequired / (currentBasal * overrideFactor)).jsRounded()
|
|
|
|
|
+ durationRequired = (durationRequired / 30).jsRounded() * 30
|
|
|
|
|
+ durationRequired = max(30, min(120, durationRequired))
|
|
|
|
|
|
|
|
let finalDetermination = try TempBasalFunctions.setTempBasal(
|
|
let finalDetermination = try TempBasalFunctions.setTempBasal(
|
|
|
rate: 0,
|
|
rate: 0,
|
|
|
- duration: durationReq,
|
|
|
|
|
|
|
+ duration: durationRequired,
|
|
|
profile: profile,
|
|
profile: profile,
|
|
|
determination: newDetermination,
|
|
determination: newDetermination,
|
|
|
currentTemp: currentTemp
|
|
currentTemp: currentTemp
|
|
|
)
|
|
)
|
|
|
- return (setTempBasal: true, determination: finalDetermination)
|
|
|
|
|
|
|
+ return (shouldSetTempBasal: true, determination: finalDetermination)
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- return (setTempBasal: false, determination: determination)
|
|
|
|
|
|
|
+ return (shouldSetTempBasal: false, determination: determination)
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/// Determines if a neutral temp basal should be skipped to avoid pump alerts.
|
|
/// Determines if a neutral temp basal should be skipped to avoid pump alerts.
|
|
|
///
|
|
///
|
|
|
/// - Returns: A tuple containing:
|
|
/// - Returns: A tuple containing:
|
|
|
- /// - `setTempBasal`: A `Bool` that is `true` if `determineBasal` should exit and apply the recommendation immediately.
|
|
|
|
|
|
|
+ /// - `shouldSetTempBasal`: A `Bool` that is `true` if `determineBasal` should exit and apply the recommendation immediately.
|
|
|
/// - `determination`: The (potentially modified) determination object.
|
|
/// - `determination`: The (potentially modified) determination object.
|
|
|
static func skipNeutralTempBasal(
|
|
static func skipNeutralTempBasal(
|
|
|
smbIsEnabled: Bool,
|
|
smbIsEnabled: Bool,
|
|
@@ -356,9 +349,9 @@ enum DosingEngine {
|
|
|
clock: Date,
|
|
clock: Date,
|
|
|
currentTemp: TempBasal,
|
|
currentTemp: TempBasal,
|
|
|
determination: Determination
|
|
determination: Determination
|
|
|
- ) throws -> (setTempBasal: Bool, determination: Determination) {
|
|
|
|
|
|
|
+ ) throws -> (shouldSetTempBasal: Bool, determination: Determination) {
|
|
|
guard profile.skipNeutralTemps else {
|
|
guard profile.skipNeutralTemps else {
|
|
|
- return (setTempBasal: false, determination: determination)
|
|
|
|
|
|
|
+ return (shouldSetTempBasal: false, determination: determination)
|
|
|
}
|
|
}
|
|
|
guard let totalMinutes = clock.minutesSinceMidnight else {
|
|
guard let totalMinutes = clock.minutesSinceMidnight else {
|
|
|
throw CalendarError.invalidCalendar
|
|
throw CalendarError.invalidCalendar
|
|
@@ -366,7 +359,7 @@ enum DosingEngine {
|
|
|
|
|
|
|
|
let minute = totalMinutes % 60
|
|
let minute = totalMinutes % 60
|
|
|
guard minute >= 55 else {
|
|
guard minute >= 55 else {
|
|
|
- return (setTempBasal: false, determination: determination)
|
|
|
|
|
|
|
+ return (shouldSetTempBasal: false, determination: determination)
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
if !smbIsEnabled {
|
|
if !smbIsEnabled {
|
|
@@ -383,11 +376,11 @@ enum DosingEngine {
|
|
|
determination: newDetermination,
|
|
determination: newDetermination,
|
|
|
currentTemp: currentTemp
|
|
currentTemp: currentTemp
|
|
|
)
|
|
)
|
|
|
- return (setTempBasal: true, determination: finalDetermination)
|
|
|
|
|
|
|
+ return (shouldSetTempBasal: true, determination: finalDetermination)
|
|
|
} else {
|
|
} else {
|
|
|
// In the JS, this path logs to the console but does not modify determination.
|
|
// In the JS, this path logs to the console but does not modify determination.
|
|
|
// We will do nothing here to match that behavior.
|
|
// We will do nothing here to match that behavior.
|
|
|
- return (setTempBasal: false, determination: determination)
|
|
|
|
|
|
|
+ return (shouldSetTempBasal: false, determination: determination)
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|