Skip to content

Commit 140df85

Browse files
erikdips2
authored andcommitted
Refactor Bolus recommendation code (#708)
* Refactor Bolus Recommendation Make Bolus recommendation part of Loop update and don't allow external calls to it. The data doesn't change in any case and update() is called in all places where we want a Bolus recommendation. This is in preparation of automated Bolus code, which needs consistent Bolus and Basal data. * Remove recommendBolus function from LoopState The recommendedBolus variable contains the same information.
1 parent 1cabd3a commit 140df85

File tree

4 files changed

+37
-66
lines changed

4 files changed

+37
-66
lines changed

Loop/Managers/LoopDataManager.swift

Lines changed: 34 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -293,7 +293,7 @@ final class LoopDataManager {
293293
do {
294294
try self.update()
295295

296-
completion(.success(try self.recommendBolus()))
296+
completion(.success(self.recommendedBolus?.recommendation))
297297
} catch let error {
298298
completion(.failure(error))
299299
}
@@ -580,7 +580,7 @@ final class LoopDataManager {
580580

581581
if predictedGlucose == nil {
582582
do {
583-
try updatePredictedGlucoseAndRecommendedBasal()
583+
try updatePredictedGlucoseAndRecommendedBasalAndBolus()
584584
} catch let error {
585585
logger.error(error)
586586

@@ -719,6 +719,7 @@ final class LoopDataManager {
719719
fileprivate var predictedGlucose: [GlucoseValue]? {
720720
didSet {
721721
recommendedTempBasal = nil
722+
recommendedBolus = nil
722723
}
723724
}
724725
fileprivate var retrospectivePredictedGlucose: [GlucoseValue]? {
@@ -728,6 +729,8 @@ final class LoopDataManager {
728729
}
729730
fileprivate var recommendedTempBasal: (recommendation: TempBasalRecommendation, date: Date)?
730731

732+
fileprivate var recommendedBolus: (recommendation: BolusRecommendation, date: Date)?
733+
731734
fileprivate var carbsOnBoard: CarbValue?
732735

733736
fileprivate var lastTempBasal: DoseEntry?
@@ -843,7 +846,7 @@ final class LoopDataManager {
843846
/// - LoopError.glucoseTooOld
844847
/// - LoopError.missingDataError
845848
/// - LoopError.pumpDataTooOld
846-
private func updatePredictedGlucoseAndRecommendedBasal() throws {
849+
private func updatePredictedGlucoseAndRecommendedBasalAndBolus() throws {
847850
dispatchPrecondition(condition: .onQueue(dataAccessQueue))
848851

849852
guard let glucose = glucoseStore.latestGlucose else {
@@ -881,14 +884,25 @@ final class LoopDataManager {
881884
let glucoseTargetRange = settings.glucoseTargetRangeSchedule,
882885
let insulinSensitivity = insulinSensitivitySchedule,
883886
let basalRates = basalRateSchedule,
887+
let maxBolus = settings.maximumBolus,
884888
let model = insulinModelSettings?.model
885889
else {
886890
throw LoopError.configurationError("Check settings")
887891
}
892+
893+
let pendingInsulin = try self.getPendingInsulin()
888894

889-
guard
890-
lastRequestedBolus == nil, // Don't recommend changes if a bolus was just set
891-
let tempBasal = predictedGlucose.recommendedTempBasal(
895+
guard lastRequestedBolus == nil
896+
else {
897+
// Don't recommend changes if a bolus was just requested.
898+
// Sending additional pump commands is not going to be
899+
// successful in any case.
900+
recommendedBolus = nil
901+
recommendedTempBasal = nil
902+
return
903+
}
904+
905+
let tempBasal = predictedGlucose.recommendedTempBasal(
892906
to: glucoseTargetRange,
893907
suspendThreshold: settings.suspendThreshold?.quantity,
894908
sensitivity: insulinSensitivity,
@@ -897,42 +911,13 @@ final class LoopDataManager {
897911
maxBasalRate: maxBasal,
898912
lastTempBasal: lastTempBasal
899913
)
900-
else {
914+
915+
if let temp = tempBasal {
916+
recommendedTempBasal = (recommendation: temp, date: startDate)
917+
} else {
901918
recommendedTempBasal = nil
902-
return
903-
}
904-
905-
recommendedTempBasal = (recommendation: tempBasal, date: Date())
906-
}
907-
908-
/// - Returns: A bolus recommendation from the current data
909-
/// - Throws:
910-
/// - LoopError.configurationError
911-
/// - LoopError.glucoseTooOld
912-
/// - LoopError.missingDataError
913-
fileprivate func recommendBolus() throws -> BolusRecommendation {
914-
dispatchPrecondition(condition: .onQueue(dataAccessQueue))
915-
916-
guard
917-
let predictedGlucose = predictedGlucose,
918-
let maxBolus = settings.maximumBolus,
919-
let glucoseTargetRange = settings.glucoseTargetRangeSchedule,
920-
let insulinSensitivity = insulinSensitivitySchedule,
921-
let model = insulinModelSettings?.model
922-
else {
923-
throw LoopError.configurationError("Check Settings")
924-
}
925-
926-
guard let glucoseDate = predictedGlucose.first?.startDate else {
927-
throw LoopError.missingDataError(details: "No glucose data found", recovery: "Check your CGM source")
928919
}
929-
930-
guard abs(glucoseDate.timeIntervalSinceNow) <= recencyInterval else {
931-
throw LoopError.glucoseTooOld(date: glucoseDate)
932-
}
933-
934-
let pendingInsulin = try self.getPendingInsulin()
935-
920+
936921
let recommendation = predictedGlucose.recommendedBolus(
937922
to: glucoseTargetRange,
938923
suspendThreshold: settings.suspendThreshold?.quantity,
@@ -941,8 +926,7 @@ final class LoopDataManager {
941926
pendingInsulin: pendingInsulin,
942927
maxBolus: maxBolus
943928
)
944-
945-
return recommendation
929+
recommendedBolus = (recommendation: recommendation, date: startDate)
946930
}
947931

948932
/// *This method should only be called from the `dataAccessQueue`*
@@ -999,6 +983,8 @@ protocol LoopState {
999983
/// The recommended temp basal based on predicted glucose
1000984
var recommendedTempBasal: (recommendation: TempBasalRecommendation, date: Date)? { get }
1001985

986+
var recommendedBolus: (recommendation: BolusRecommendation, date: Date)? { get }
987+
1002988
/// The retrospective prediction over a recent period of glucose samples
1003989
var retrospectivePredictedGlucose: [GlucoseValue]? { get }
1004990

@@ -1010,15 +996,6 @@ protocol LoopState {
1010996
/// - Returns: An timeline of predicted glucose values
1011997
/// - Throws: LoopError.missingDataError if prediction cannot be computed
1012998
func predictGlucose(using inputs: PredictionInputEffect) throws -> [GlucoseValue]
1013-
1014-
/// Calculates a recommended bolus based on predicted glucose
1015-
///
1016-
/// - Returns: A bolus recommendation
1017-
/// - Throws: An error describing why a bolus couldnʼt be computed
1018-
/// - LoopError.configurationError
1019-
/// - LoopError.glucoseTooOld
1020-
/// - LoopError.missingDataError
1021-
func recommendBolus() throws -> BolusRecommendation
1022999
}
10231000

10241001

@@ -1066,6 +1043,11 @@ extension LoopDataManager {
10661043
dispatchPrecondition(condition: .onQueue(loopDataManager.dataAccessQueue))
10671044
return loopDataManager.recommendedTempBasal
10681045
}
1046+
1047+
var recommendedBolus: (recommendation: BolusRecommendation, date: Date)? {
1048+
dispatchPrecondition(condition: .onQueue(loopDataManager.dataAccessQueue))
1049+
return loopDataManager.recommendedBolus
1050+
}
10691051

10701052
var retrospectivePredictedGlucose: [GlucoseValue]? {
10711053
dispatchPrecondition(condition: .onQueue(loopDataManager.dataAccessQueue))
@@ -1075,10 +1057,6 @@ extension LoopDataManager {
10751057
func predictGlucose(using inputs: PredictionInputEffect) throws -> [GlucoseValue] {
10761058
return try loopDataManager.predictGlucose(using: inputs)
10771059
}
1078-
1079-
func recommendBolus() throws -> BolusRecommendation {
1080-
return try loopDataManager.recommendBolus()
1081-
}
10821060
}
10831061

10841062
/// Executes a closure with access to the current state of the loop.
@@ -1120,6 +1098,7 @@ extension LoopDataManager {
11201098
"predictedGlucose: \(state.predictedGlucose ?? [])",
11211099
"retrospectivePredictedGlucose: \(state.retrospectivePredictedGlucose ?? [])",
11221100
"recommendedTempBasal: \(String(describing: state.recommendedTempBasal))",
1101+
"recommendedBolus: \(String(describing: state.recommendedBolus))",
11231102
"lastBolus: \(String(describing: manager.lastRequestedBolus))",
11241103
"lastGlucoseChange: \(String(describing: manager.lastGlucoseChange))",
11251104
"retrospectiveGlucoseChange: \(String(describing: manager.retrospectiveGlucoseChange))",

Loop/Managers/NightscoutDataManager.swift

Lines changed: 1 addition & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -43,15 +43,7 @@ final class NightscoutDataManager {
4343
var loopError = state.error
4444
let recommendedBolus: Double?
4545

46-
do {
47-
recommendedBolus = try state.recommendBolus().amount
48-
} catch let error {
49-
recommendedBolus = nil
50-
51-
if loopError == nil {
52-
loopError = error
53-
}
54-
}
46+
recommendedBolus = state.recommendedBolus?.recommendation.amount
5547

5648
let carbsOnBoard = state.carbsOnBoard
5749
let predictedGlucose = state.predictedGlucose

Loop/Managers/WatchDataManager.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -125,7 +125,7 @@ final class WatchDataManager: NSObject, WCSessionDelegate {
125125
context.reservoir = reservoir?.unitVolume
126126

127127
context.loopLastRunDate = state.lastLoopCompleted
128-
context.recommendedBolusDose = try? state.recommendBolus().amount
128+
context.recommendedBolusDose = state.recommendedBolus?.recommendation.amount
129129
context.maxBolus = manager.settings.maximumBolus
130130

131131
if let glucoseTargetRangeSchedule = manager.settings.glucoseTargetRangeSchedule {

Loop/View Controllers/BolusViewController+LoopDataManager.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ extension BolusViewController {
2020
if let recommendation = recommendation {
2121
bolusRecommendation = recommendation
2222
} else {
23-
bolusRecommendation = try? state.recommendBolus()
23+
bolusRecommendation = state.recommendedBolus?.recommendation
2424
}
2525

2626
manager.doseStore.insulinOnBoard(at: Date()) { (result) in

0 commit comments

Comments
 (0)