@@ -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) ) " ,
0 commit comments