diff --git a/Loop Status Extension/StatusViewController.swift b/Loop Status Extension/StatusViewController.swift index e3c57a98d9..16b9b64f10 100644 --- a/Loop Status Extension/StatusViewController.swift +++ b/Loop Status Extension/StatusViewController.swift @@ -291,7 +291,7 @@ class StatusViewController: UIViewController, NCWidgetProviding { lastGlucose.quantity.doubleValue(for: unit), at: lastGlucose.startDate, unit: unit, - staleGlucoseAge: LoopCoreConstants.inputDataRecencyInterval, + staleGlucoseAge: LoopAlgorithm.inputDataRecencyInterval, glucoseDisplay: context.glucoseDisplay, wasUserEntered: lastGlucose.wasUserEntered, isDisplayOnly: lastGlucose.isDisplayOnly diff --git a/Loop Widget Extension/Timeline/StatusWidgetTimelimeEntry.swift b/Loop Widget Extension/Timeline/StatusWidgetTimelimeEntry.swift index 45271bbe14..d236427e7b 100644 --- a/Loop Widget Extension/Timeline/StatusWidgetTimelimeEntry.swift +++ b/Loop Widget Extension/Timeline/StatusWidgetTimelimeEntry.swift @@ -53,6 +53,6 @@ struct StatusWidgetTimelimeEntry: TimelineEntry { } let glucoseAge = date - glucoseDate - return glucoseAge >= LoopCoreConstants.inputDataRecencyInterval + return glucoseAge >= LoopAlgorithm.inputDataRecencyInterval } } diff --git a/Loop Widget Extension/Timeline/StatusWidgetTimelineProvider.swift b/Loop Widget Extension/Timeline/StatusWidgetTimelineProvider.swift index beb8bd2f70..5dd3af7d29 100644 --- a/Loop Widget Extension/Timeline/StatusWidgetTimelineProvider.swift +++ b/Loop Widget Extension/Timeline/StatusWidgetTimelineProvider.swift @@ -67,7 +67,7 @@ class StatusWidgetTimelineProvider: TimelineProvider { // Date glucose staleness changes if let lastBGTime = newEntry.currentGlucose?.startDate { - let staleBgRefreshTime = lastBGTime.addingTimeInterval(LoopCoreConstants.inputDataRecencyInterval+1) + let staleBgRefreshTime = lastBGTime.addingTimeInterval(LoopAlgorithm.inputDataRecencyInterval+1) datesToRefreshWidget.append(staleBgRefreshTime) } @@ -93,7 +93,7 @@ class StatusWidgetTimelineProvider: TimelineProvider { var glucose: [StoredGlucoseSample] = [] - let startDate = Date(timeIntervalSinceNow: -LoopCoreConstants.inputDataRecencyInterval) + let startDate = Date(timeIntervalSinceNow: -LoopAlgorithm.inputDataRecencyInterval) group.enter() glucoseStore.getGlucoseSamples(start: startDate) { (result) in diff --git a/Loop/Extensions/DeviceDataManager+DeviceStatus.swift b/Loop/Extensions/DeviceDataManager+DeviceStatus.swift index fbcf52b983..bc9b40f4d4 100644 --- a/Loop/Extensions/DeviceDataManager+DeviceStatus.swift +++ b/Loop/Extensions/DeviceDataManager+DeviceStatus.swift @@ -114,7 +114,7 @@ extension DeviceDataManager { var isGlucoseValueStale: Bool { guard let latestGlucoseDataDate = glucoseStore.latestGlucose?.startDate else { return true } - return Date().timeIntervalSince(latestGlucoseDataDate) > LoopCoreConstants.inputDataRecencyInterval + return Date().timeIntervalSince(latestGlucoseDataDate) > LoopAlgorithm.inputDataRecencyInterval } } diff --git a/Loop/Extensions/DosingDecisionStore+SimulatedCoreData.swift b/Loop/Extensions/DosingDecisionStore+SimulatedCoreData.swift index 53f81c5209..94627cfdd1 100644 --- a/Loop/Extensions/DosingDecisionStore+SimulatedCoreData.swift +++ b/Loop/Extensions/DosingDecisionStore+SimulatedCoreData.swift @@ -168,7 +168,6 @@ fileprivate extension StoredDosingDecision { duration: .minutes(30)), bolusUnits: 1.25) let manualBolusRecommendation = ManualBolusRecommendationWithDate(recommendation: ManualBolusRecommendation(amount: 0.2, - pendingInsulin: 0.75, notice: .predictedGlucoseBelowTarget(minGlucose: PredictedGlucoseValue(startDate: date.addingTimeInterval(.minutes(30)), quantity: HKQuantity(unit: .milligramsPerDeciliter, doubleValue: 95.0)))), date: date.addingTimeInterval(-.minutes(1))) diff --git a/Loop/Managers/CGMStalenessMonitor.swift b/Loop/Managers/CGMStalenessMonitor.swift index 82cdc9267d..ad54f9d1eb 100644 --- a/Loop/Managers/CGMStalenessMonitor.swift +++ b/Loop/Managers/CGMStalenessMonitor.swift @@ -43,9 +43,9 @@ class CGMStalenessMonitor { let mostRecentGlucose = samples.map { $0.date }.max()! let cgmDataAge = -mostRecentGlucose.timeIntervalSinceNow - if cgmDataAge < LoopCoreConstants.inputDataRecencyInterval { + if cgmDataAge < LoopAlgorithm.inputDataRecencyInterval { self.cgmDataIsStale = false - self.updateCGMStalenessTimer(expiration: mostRecentGlucose.addingTimeInterval(LoopCoreConstants.inputDataRecencyInterval)) + self.updateCGMStalenessTimer(expiration: mostRecentGlucose.addingTimeInterval(LoopAlgorithm.inputDataRecencyInterval)) } else { self.cgmDataIsStale = true } @@ -62,14 +62,14 @@ class CGMStalenessMonitor { } private func checkCGMStaleness() { - delegate?.getLatestCGMGlucose(since: Date(timeIntervalSinceNow: -LoopCoreConstants.inputDataRecencyInterval)) { (result) in + delegate?.getLatestCGMGlucose(since: Date(timeIntervalSinceNow: -LoopAlgorithm.inputDataRecencyInterval)) { (result) in DispatchQueue.main.async { self.log.debug("Fetched latest CGM Glucose for checkCGMStaleness: %{public}@", String(describing: result)) switch result { case .success(let sample): if let sample = sample { self.cgmDataIsStale = false - self.updateCGMStalenessTimer(expiration: sample.startDate.addingTimeInterval(LoopCoreConstants.inputDataRecencyInterval + CGMStalenessMonitor.cgmStalenessTimerTolerance)) + self.updateCGMStalenessTimer(expiration: sample.startDate.addingTimeInterval(LoopAlgorithm.inputDataRecencyInterval + CGMStalenessMonitor.cgmStalenessTimerTolerance)) } else { self.cgmDataIsStale = true } diff --git a/Loop/Managers/LoopDataManager.swift b/Loop/Managers/LoopDataManager.swift index b56cddd35b..142641066b 100644 --- a/Loop/Managers/LoopDataManager.swift +++ b/Loop/Managers/LoopDataManager.swift @@ -964,7 +964,7 @@ extension LoopDataManager { let updateGroup = DispatchGroup() let historicalGlucoseStartDate = Date(timeInterval: -LoopCoreConstants.dosingDecisionHistoricalGlucoseInterval, since: now()) - let inputDataRecencyStartDate = Date(timeInterval: -LoopCoreConstants.inputDataRecencyInterval, since: now()) + let inputDataRecencyStartDate = Date(timeInterval: -LoopAlgorithm.inputDataRecencyInterval, since: now()) // Fetch glucose effects as far back as we want to make retroactive analysis and historical glucose for dosing decision var historicalGlucose: [HistoricalGlucoseValue]? @@ -1227,7 +1227,7 @@ extension LoopDataManager { let pumpStatusDate = doseStore.lastAddedPumpData let lastGlucoseDate = glucose.startDate - guard now().timeIntervalSince(lastGlucoseDate) <= LoopCoreConstants.inputDataRecencyInterval else { + guard now().timeIntervalSince(lastGlucoseDate) <= LoopAlgorithm.inputDataRecencyInterval else { throw LoopError.glucoseTooOld(date: glucose.startDate) } @@ -1235,7 +1235,7 @@ extension LoopDataManager { throw LoopError.invalidFutureGlucose(date: lastGlucoseDate) } - guard now().timeIntervalSince(pumpStatusDate) <= LoopCoreConstants.inputDataRecencyInterval else { + guard now().timeIntervalSince(pumpStatusDate) <= LoopAlgorithm.inputDataRecencyInterval else { throw LoopError.pumpDataTooOld(date: pumpStatusDate) } @@ -1487,15 +1487,15 @@ extension LoopDataManager { let pumpStatusDate = doseStore.lastAddedPumpData let lastGlucoseDate = glucose.startDate - guard now().timeIntervalSince(lastGlucoseDate) <= LoopCoreConstants.inputDataRecencyInterval else { + guard now().timeIntervalSince(lastGlucoseDate) <= LoopAlgorithm.inputDataRecencyInterval else { throw LoopError.glucoseTooOld(date: glucose.startDate) } - guard lastGlucoseDate.timeIntervalSince(now()) <= LoopCoreConstants.inputDataRecencyInterval else { + guard lastGlucoseDate.timeIntervalSince(now()) <= LoopAlgorithm.inputDataRecencyInterval else { throw LoopError.invalidFutureGlucose(date: lastGlucoseDate) } - guard now().timeIntervalSince(pumpStatusDate) <= LoopCoreConstants.inputDataRecencyInterval else { + guard now().timeIntervalSince(pumpStatusDate) <= LoopAlgorithm.inputDataRecencyInterval else { throw LoopError.pumpDataTooOld(date: pumpStatusDate) } @@ -1541,16 +1541,18 @@ extension LoopDataManager { let model = doseStore.insulinModelProvider.model(for: pumpInsulinType) - return predictedGlucose.recommendedManualBolus( + var recommendation = predictedGlucose.recommendedManualBolus( to: glucoseTargetRange, at: now(), suspendThreshold: settings.suspendThreshold?.quantity, sensitivity: insulinSensitivity, model: model, - pendingInsulin: 0, // Pending insulin is already reflected in the prediction - maxBolus: maxBolus, - volumeRounder: volumeRounder + maxBolus: maxBolus ) + + // Round to pump precision + recommendation.amount = volumeRounder(recommendation.amount) + return recommendation } /// Generates a correction effect based on how large the discrepancy is between the current glucose and its model predicted value. @@ -1575,37 +1577,22 @@ extension LoopDataManager { // Get timeline of glucose discrepancies retrospectiveGlucoseDiscrepancies = insulinCounteractionEffects.subtracting(carbEffects, withUniformInterval: carbStore.delta) - // Calculate retrospective correction - let insulinSensitivity = settings.insulinSensitivitySchedule!.quantity(at: glucose.startDate) - let basalRate = settings.basalRateSchedule!.value(at: glucose.startDate) - let correctionRange = settings.glucoseTargetRangeSchedule!.quantityRange(at: glucose.startDate) - retrospectiveGlucoseEffect = retrospectiveCorrection.computeEffect( startingAt: glucose, retrospectiveGlucoseDiscrepanciesSummed: retrospectiveGlucoseDiscrepanciesSummed, - recencyInterval: LoopCoreConstants.inputDataRecencyInterval, - insulinSensitivity: insulinSensitivity, - basalRate: basalRate, - correctionRange: correctionRange, + recencyInterval: LoopAlgorithm.inputDataRecencyInterval, retrospectiveCorrectionGroupingInterval: LoopMath.retrospectiveCorrectionGroupingInterval ) } private func computeRetrospectiveGlucoseEffect(startingAt glucose: GlucoseValue, carbEffects: [GlucoseEffect]) -> [GlucoseEffect] { - let insulinSensitivity = settings.insulinSensitivitySchedule!.quantity(at: glucose.startDate) - let basalRate = settings.basalRateSchedule!.value(at: glucose.startDate) - let correctionRange = settings.glucoseTargetRangeSchedule!.quantityRange(at: glucose.startDate) - let retrospectiveGlucoseDiscrepancies = insulinCounteractionEffects.subtracting(carbEffects, withUniformInterval: carbStore.delta) let retrospectiveGlucoseDiscrepanciesSummed = retrospectiveGlucoseDiscrepancies.combinedSums(of: LoopMath.retrospectiveCorrectionGroupingInterval * retrospectiveCorrectionGroupingIntervalMultiplier) return retrospectiveCorrection.computeEffect( startingAt: glucose, retrospectiveGlucoseDiscrepanciesSummed: retrospectiveGlucoseDiscrepanciesSummed, - recencyInterval: LoopCoreConstants.inputDataRecencyInterval, - insulinSensitivity: insulinSensitivity, - basalRate: basalRate, - correctionRange: correctionRange, + recencyInterval: LoopAlgorithm.inputDataRecencyInterval, retrospectiveCorrectionGroupingInterval: LoopMath.retrospectiveCorrectionGroupingInterval ) } @@ -1690,17 +1677,17 @@ extension LoopDataManager { var errors = [LoopError]() - if startDate.timeIntervalSince(glucose.startDate) > LoopCoreConstants.inputDataRecencyInterval { + if startDate.timeIntervalSince(glucose.startDate) > LoopAlgorithm.inputDataRecencyInterval { errors.append(.glucoseTooOld(date: glucose.startDate)) } - if glucose.startDate.timeIntervalSince(startDate) > LoopCoreConstants.inputDataRecencyInterval { + if glucose.startDate.timeIntervalSince(startDate) > LoopAlgorithm.inputDataRecencyInterval { errors.append(.invalidFutureGlucose(date: glucose.startDate)) } let pumpStatusDate = doseStore.lastAddedPumpData - if startDate.timeIntervalSince(pumpStatusDate) > LoopCoreConstants.inputDataRecencyInterval { + if startDate.timeIntervalSince(pumpStatusDate) > LoopAlgorithm.inputDataRecencyInterval { errors.append(.pumpDataTooOld(date: pumpStatusDate)) } @@ -2176,7 +2163,7 @@ extension LoopDataManager { sensitivitySchedule: sensitivitySchedule, at: date) - dosingDecision.manualBolusRecommendation = ManualBolusRecommendationWithDate(recommendation: ManualBolusRecommendation(amount: bolusAmount.doubleValue(for: .internationalUnit()), pendingInsulin: 0, notice: notice), + dosingDecision.manualBolusRecommendation = ManualBolusRecommendationWithDate(recommendation: ManualBolusRecommendation(amount: bolusAmount.doubleValue(for: .internationalUnit()), notice: notice), date: Date()) return dosingDecision diff --git a/Loop/Models/ConstantApplicationFactorStrategy.swift b/Loop/Models/ConstantApplicationFactorStrategy.swift index e13c40c42e..7489367cae 100644 --- a/Loop/Models/ConstantApplicationFactorStrategy.swift +++ b/Loop/Models/ConstantApplicationFactorStrategy.swift @@ -18,6 +18,6 @@ struct ConstantApplicationFactorStrategy: ApplicationFactorStrategy { settings: LoopSettings ) -> Double { // The original strategy uses a constant dosing factor. - return LoopConstants.bolusPartialApplicationFactor + return LoopAlgorithm.bolusPartialApplicationFactor } } diff --git a/Loop/Models/LoopConstants.swift b/Loop/Models/LoopConstants.swift index fb69c8275f..bd1296c12f 100644 --- a/Loop/Models/LoopConstants.swift +++ b/Loop/Models/LoopConstants.swift @@ -49,9 +49,6 @@ enum LoopConstants { static let retrospectiveCorrectionEnabled = true - // Percentage of recommended dose to apply as bolus when using automatic bolus dosing strategy - static let bolusPartialApplicationFactor = 0.4 - /// Loop completion aging category limits static let completionFreshLimit = TimeInterval(minutes: 6) static let completionAgingLimit = TimeInterval(minutes: 16) diff --git a/Loop/Models/ManualBolusRecommendation.swift b/Loop/Models/ManualBolusRecommendation.swift index c1ad01125a..d176b77cf8 100644 --- a/Loop/Models/ManualBolusRecommendation.swift +++ b/Loop/Models/ManualBolusRecommendation.swift @@ -62,14 +62,3 @@ extension BolusRecommendationNotice: Equatable { } } - -extension ManualBolusRecommendation: Comparable { - public static func ==(lhs: ManualBolusRecommendation, rhs: ManualBolusRecommendation) -> Bool { - return lhs.amount == rhs.amount - } - - public static func <(lhs: ManualBolusRecommendation, rhs: ManualBolusRecommendation) -> Bool { - return lhs.amount < rhs.amount - } -} - diff --git a/Loop/View Controllers/StatusTableViewController.swift b/Loop/View Controllers/StatusTableViewController.swift index 8906a75986..84bc9428c6 100644 --- a/Loop/View Controllers/StatusTableViewController.swift +++ b/Loop/View Controllers/StatusTableViewController.swift @@ -610,7 +610,7 @@ final class StatusTableViewController: LoopChartsTableViewController { hudView.cgmStatusHUD.setGlucoseQuantity(glucose.quantity.doubleValue(for: unit), at: glucose.startDate, unit: unit, - staleGlucoseAge: LoopCoreConstants.inputDataRecencyInterval, + staleGlucoseAge: LoopAlgorithm.inputDataRecencyInterval, glucoseDisplay: self.deviceManager.glucoseDisplay(for: glucose), wasUserEntered: glucose.wasUserEntered, isDisplayOnly: glucose.isDisplayOnly) diff --git a/Loop/View Models/BolusEntryViewModel.swift b/Loop/View Models/BolusEntryViewModel.swift index a86f20e0cc..08047d67c5 100644 --- a/Loop/View Models/BolusEntryViewModel.swift +++ b/Loop/View Models/BolusEntryViewModel.swift @@ -818,12 +818,12 @@ extension BolusEntryViewModel { var isGlucoseDataStale: Bool { guard let latestGlucoseDataDate = delegate?.mostRecentGlucoseDataDate else { return true } - return now().timeIntervalSince(latestGlucoseDataDate) > LoopCoreConstants.inputDataRecencyInterval + return now().timeIntervalSince(latestGlucoseDataDate) > LoopAlgorithm.inputDataRecencyInterval } var isPumpDataStale: Bool { guard let latestPumpDataDate = delegate?.mostRecentPumpDataDate else { return true } - return now().timeIntervalSince(latestPumpDataDate) > LoopCoreConstants.inputDataRecencyInterval + return now().timeIntervalSince(latestPumpDataDate) > LoopAlgorithm.inputDataRecencyInterval } var isManualGlucosePromptVisible: Bool { diff --git a/Loop/Views/SimpleBolusView.swift b/Loop/Views/SimpleBolusView.swift index 2a7fc3fe59..aa7546c6f9 100644 --- a/Loop/Views/SimpleBolusView.swift +++ b/Loop/Views/SimpleBolusView.swift @@ -392,7 +392,7 @@ struct SimpleBolusCalculatorView_Previews: PreviewProvider { func computeSimpleBolusRecommendation(at date: Date, mealCarbs: HKQuantity?, manualGlucose: HKQuantity?) -> BolusDosingDecision? { var decision = BolusDosingDecision(for: .simpleBolus) - decision.manualBolusRecommendation = ManualBolusRecommendationWithDate(recommendation: ManualBolusRecommendation(amount: 3, pendingInsulin: 0), + decision.manualBolusRecommendation = ManualBolusRecommendationWithDate(recommendation: ManualBolusRecommendation(amount: 3), date: Date()) return decision } diff --git a/LoopCore/LoopCoreConstants.swift b/LoopCore/LoopCoreConstants.swift index d56f2ab9b6..d33ca167bc 100644 --- a/LoopCore/LoopCoreConstants.swift +++ b/LoopCore/LoopCoreConstants.swift @@ -10,9 +10,6 @@ import Foundation import LoopKit public enum LoopCoreConstants { - /// The amount of time since a given date that input data should be considered valid - public static let inputDataRecencyInterval = TimeInterval(minutes: 15) - /// The amount of time in the future a glucose value should be considered valid public static let futureGlucoseDataInterval = TimeInterval(minutes: 5) diff --git a/LoopTests/Fixtures/live_capture/live_capture_input.json b/LoopTests/Fixtures/live_capture/live_capture_input.json index f010194a63..4bd97abaa9 100644 --- a/LoopTests/Fixtures/live_capture/live_capture_input.json +++ b/LoopTests/Fixtures/live_capture/live_capture_input.json @@ -962,48 +962,25 @@ "startDate" : "2023-06-23T02:37:35Z" } ], - "settings" : { - "basal" : [ - { - "endDate" : "2023-06-23T05:00:00Z", - "startDate" : "2023-06-22T10:00:00Z", - "value" : 0.45000000000000001 - } - ], - "carbRatio" : [ - { - "endDate" : "2023-06-23T07:00:00Z", - "startDate" : "2023-06-22T07:00:00Z", - "value" : 11 - } - ], - "maximumBasalRatePerHour" : null, - "maximumBolus" : null, - "sensitivity" : [ - { - "endDate" : "2023-06-23T05:00:00Z", - "startDate" : "2023-06-22T10:00:00Z", - "value" : 60 - } - ], - "suspendThreshold" : null, - "target" : [ - { - "endDate" : "2023-06-23T07:00:00Z", - "startDate" : "2023-06-22T20:25:00Z", - "value" : { - "maxValue" : 115, - "minValue" : 100 - } - }, - { - "endDate" : "2023-06-23T08:50:00Z", - "startDate" : "2023-06-23T07:00:00Z", - "value" : { - "maxValue" : 115, - "minValue" : 100 - } - } - ] - } + "basal" : [ + { + "endDate" : "2023-06-23T05:00:00Z", + "startDate" : "2023-06-22T10:00:00Z", + "value" : 0.45000000000000001 + } + ], + "carbRatio" : [ + { + "endDate" : "2023-06-23T07:00:00Z", + "startDate" : "2023-06-22T07:00:00Z", + "value" : 11 + } + ], + "sensitivity" : [ + { + "endDate" : "2023-06-23T05:00:00Z", + "startDate" : "2023-06-22T10:00:00Z", + "value" : 60 + } + ], } diff --git a/LoopTests/Managers/LoopAlgorithmTests.swift b/LoopTests/Managers/LoopAlgorithmTests.swift index 6c51283872..084a72a3cf 100644 --- a/LoopTests/Managers/LoopAlgorithmTests.swift +++ b/LoopTests/Managers/LoopAlgorithmTests.swift @@ -49,7 +49,7 @@ final class LoopAlgorithmTests: XCTestCase { } - func testLiveCaptureWithFunctionalAlgorithm() throws { + func testLiveCaptureWithFunctionalAlgorithm() { // This matches the "testForecastFromLiveCaptureInputData" test of LoopDataManagerDosingTests, // Using the same input data, but generating the forecast using the LoopAlgorithm.generatePrediction() // function. @@ -57,9 +57,17 @@ final class LoopAlgorithmTests: XCTestCase { let decoder = JSONDecoder() decoder.dateDecodingStrategy = .iso8601 let url = bundle.url(forResource: "live_capture_input", withExtension: "json")! - let predictionInput = try! decoder.decode(LoopPredictionInput.self, from: try! Data(contentsOf: url)) - - let prediction = try LoopAlgorithm.generatePrediction(input: predictionInput) + let input = try! decoder.decode(LoopPredictionInput.self, from: try! Data(contentsOf: url)) + + let prediction = LoopAlgorithm.generatePrediction( + glucoseHistory: input.glucoseHistory, + doses: input.doses, + carbEntries: input.carbEntries, + basal: input.basal, + sensitivity: input.sensitivity, + carbRatio: input.carbRatio, + useIntegralRetrospectiveCorrection: input.useIntegralRetrospectiveCorrection + ) let expectedPredictedGlucose = loadPredictedGlucoseFixture("live_capture_predicted_glucose") diff --git a/LoopTests/Managers/LoopDataManagerDosingTests.swift b/LoopTests/Managers/LoopDataManagerDosingTests.swift index a1f26a0e92..9cdb1f43cd 100644 --- a/LoopTests/Managers/LoopDataManagerDosingTests.swift +++ b/LoopTests/Managers/LoopDataManagerDosingTests.swift @@ -64,19 +64,19 @@ class LoopDataManagerDosingTests: LoopDataManagerTests { // Therapy settings in the "live capture" input only have one value, so we can fake some schedules // from the first entry of each therapy setting's history. let basalRateSchedule = BasalRateSchedule(dailyItems: [ - RepeatingScheduleValue(startTime: 0, value: predictionInput.settings.basal.first!.value) + RepeatingScheduleValue(startTime: 0, value: predictionInput.basal.first!.value) ]) let insulinSensitivitySchedule = InsulinSensitivitySchedule( unit: .milligramsPerDeciliter, dailyItems: [ - RepeatingScheduleValue(startTime: 0, value: predictionInput.settings.sensitivity.first!.value.doubleValue(for: .milligramsPerDeciliter)) + RepeatingScheduleValue(startTime: 0, value: predictionInput.sensitivity.first!.value.doubleValue(for: .milligramsPerDeciliter)) ], timeZone: .utcTimeZone )! let carbRatioSchedule = CarbRatioSchedule( unit: .gram(), dailyItems: [ - RepeatingScheduleValue(startTime: 0.0, value: predictionInput.settings.carbRatio.first!.value) + RepeatingScheduleValue(startTime: 0.0, value: predictionInput.carbRatio.first!.value) ], timeZone: .utcTimeZone )! @@ -89,7 +89,7 @@ class LoopDataManagerDosingTests: LoopDataManagerTests { carbRatioSchedule: carbRatioSchedule, maximumBasalRatePerHour: 10, maximumBolus: 5, - suspendThreshold: predictionInput.settings.suspendThreshold, + suspendThreshold: GlucoseThreshold(unit: .milligramsPerDeciliter, value: 65), automaticDosingStrategy: .automaticBolus ) diff --git a/LoopTests/ViewModels/BolusEntryViewModelTests.swift b/LoopTests/ViewModels/BolusEntryViewModelTests.swift index c373b639b1..8b65faa377 100644 --- a/LoopTests/ViewModels/BolusEntryViewModelTests.swift +++ b/LoopTests/ViewModels/BolusEntryViewModelTests.swift @@ -298,7 +298,7 @@ class BolusEntryViewModelTests: XCTestCase { func testUpdateRecommendedBolusNoNotice() async throws { await setUpViewModel(originalCarbEntry: mockOriginalCarbEntry, potentialCarbEntry: mockPotentialCarbEntry) XCTAssertFalse(bolusEntryViewModel.isBolusRecommended) - let recommendation = ManualBolusRecommendation(amount: 1.25, pendingInsulin: 4.321) + let recommendation = ManualBolusRecommendation(amount: 1.25) delegate.loopState.bolusRecommendationResult = recommendation await bolusEntryViewModel.update() XCTAssertTrue(bolusEntryViewModel.isBolusRecommended) @@ -315,7 +315,7 @@ class BolusEntryViewModelTests: XCTestCase { func testUpdateRecommendedBolusWithNotice() async throws { delegate.settings.suspendThreshold = GlucoseThreshold(unit: .milligramsPerDeciliter, value: Self.exampleCGMGlucoseQuantity.doubleValue(for: .milligramsPerDeciliter)) XCTAssertFalse(bolusEntryViewModel.isBolusRecommended) - let recommendation = ManualBolusRecommendation(amount: 1.25, pendingInsulin: 4.321, notice: BolusRecommendationNotice.glucoseBelowSuspendThreshold(minGlucose: Self.exampleGlucoseValue)) + let recommendation = ManualBolusRecommendation(amount: 1.25, notice: BolusRecommendationNotice.glucoseBelowSuspendThreshold(minGlucose: Self.exampleGlucoseValue)) delegate.loopState.bolusRecommendationResult = recommendation await bolusEntryViewModel.update() XCTAssertTrue(bolusEntryViewModel.isBolusRecommended) @@ -328,7 +328,7 @@ class BolusEntryViewModelTests: XCTestCase { func testUpdateRecommendedBolusWithNoticeMissingSuspendThreshold() async throws { XCTAssertFalse(bolusEntryViewModel.isBolusRecommended) delegate.settings.suspendThreshold = nil - let recommendation = ManualBolusRecommendation(amount: 1.25, pendingInsulin: 4.321, notice: BolusRecommendationNotice.glucoseBelowSuspendThreshold(minGlucose: Self.exampleGlucoseValue)) + let recommendation = ManualBolusRecommendation(amount: 1.25, notice: BolusRecommendationNotice.glucoseBelowSuspendThreshold(minGlucose: Self.exampleGlucoseValue)) delegate.loopState.bolusRecommendationResult = recommendation await bolusEntryViewModel.update() XCTAssertTrue(bolusEntryViewModel.isBolusRecommended) @@ -340,7 +340,7 @@ class BolusEntryViewModelTests: XCTestCase { func testUpdateRecommendedBolusWithOtherNotice() async throws { XCTAssertFalse(bolusEntryViewModel.isBolusRecommended) - let recommendation = ManualBolusRecommendation(amount: 1.25, pendingInsulin: 4.321, notice: BolusRecommendationNotice.currentGlucoseBelowTarget(glucose: Self.exampleGlucoseValue)) + let recommendation = ManualBolusRecommendation(amount: 1.25, notice: BolusRecommendationNotice.currentGlucoseBelowTarget(glucose: Self.exampleGlucoseValue)) delegate.loopState.bolusRecommendationResult = recommendation await bolusEntryViewModel.update() XCTAssertTrue(bolusEntryViewModel.isBolusRecommended) @@ -404,7 +404,7 @@ class BolusEntryViewModelTests: XCTestCase { await setUpViewModel(originalCarbEntry: mockOriginalCarbEntry, potentialCarbEntry: mockPotentialCarbEntry) bolusEntryViewModel.manualGlucoseQuantity = Self.exampleManualGlucoseQuantity XCTAssertFalse(bolusEntryViewModel.isBolusRecommended) - let recommendation = ManualBolusRecommendation(amount: 1.25, pendingInsulin: 4.321) + let recommendation = ManualBolusRecommendation(amount: 1.25) delegate.loopState.bolusRecommendationResult = recommendation await bolusEntryViewModel.update() XCTAssertTrue(bolusEntryViewModel.isBolusRecommended) diff --git a/LoopTests/ViewModels/SimpleBolusViewModelTests.swift b/LoopTests/ViewModels/SimpleBolusViewModelTests.swift index 94c1fd8661..92d7de8b7e 100644 --- a/LoopTests/ViewModels/SimpleBolusViewModelTests.swift +++ b/LoopTests/ViewModels/SimpleBolusViewModelTests.swift @@ -319,7 +319,7 @@ extension SimpleBolusViewModelTests: SimpleBolusViewModelDelegate { func computeSimpleBolusRecommendation(at date: Date, mealCarbs: HKQuantity?, manualGlucose: HKQuantity?) -> BolusDosingDecision? { var decision = BolusDosingDecision(for: .simpleBolus) - decision.manualBolusRecommendation = ManualBolusRecommendationWithDate(recommendation: ManualBolusRecommendation(amount: currentRecommendation, pendingInsulin: 0, notice: .none), + decision.manualBolusRecommendation = ManualBolusRecommendationWithDate(recommendation: ManualBolusRecommendation(amount: currentRecommendation, notice: .none), date: date) decision.insulinOnBoard = currentIOB return decision diff --git a/WatchApp Extension/ComplicationController.swift b/WatchApp Extension/ComplicationController.swift index 9f79aad280..1eae019f17 100644 --- a/WatchApp Extension/ComplicationController.swift +++ b/WatchApp Extension/ComplicationController.swift @@ -8,6 +8,7 @@ import ClockKit import WatchKit +import LoopKit import LoopCore import os.log @@ -88,7 +89,7 @@ final class ComplicationController: NSObject, CLKComplicationDataSource { let template = CLKComplicationTemplate.templateForFamily(complication.family, from: context, at: timelineDate, - recencyInterval: LoopCoreConstants.inputDataRecencyInterval, + recencyInterval: LoopAlgorithm.inputDataRecencyInterval, chartGenerator: self.makeChart) { switch complication.family { @@ -119,7 +120,7 @@ final class ComplicationController: NSObject, CLKComplicationDataSource { var futureChangeDates: [Date] = [ // Stale glucose date: just a second after glucose expires - glucoseDate + LoopCoreConstants.inputDataRecencyInterval + 1, + glucoseDate + LoopAlgorithm.inputDataRecencyInterval + 1, ] if let loopLastRunDate = context.loopLastRunDate { @@ -135,7 +136,7 @@ final class ComplicationController: NSObject, CLKComplicationDataSource { if let template = CLKComplicationTemplate.templateForFamily(complication.family, from: context, at: futureChangeDate, - recencyInterval: LoopCoreConstants.inputDataRecencyInterval, + recencyInterval: LoopAlgorithm.inputDataRecencyInterval, chartGenerator: self.makeChart) { template.tintColor = UIColor.tintColor diff --git a/WatchApp Extension/Controllers/ChartHUDController.swift b/WatchApp Extension/Controllers/ChartHUDController.swift index f7aa0b0231..341eece3f6 100644 --- a/WatchApp Extension/Controllers/ChartHUDController.swift +++ b/WatchApp Extension/Controllers/ChartHUDController.swift @@ -162,7 +162,7 @@ final class ChartHUDController: HUDInterfaceController, WKCrownDelegate { cell.setIsLastRow(row.isLast) cell.setContentInset(systemMinimumLayoutMargins) - let isActiveContextStale = Date().timeIntervalSince(activeContext.creationDate) > LoopCoreConstants.inputDataRecencyInterval + let isActiveContextStale = Date().timeIntervalSince(activeContext.creationDate) > LoopAlgorithm.inputDataRecencyInterval switch row { case .iob: diff --git a/WatchApp Extension/Controllers/HUDInterfaceController.swift b/WatchApp Extension/Controllers/HUDInterfaceController.swift index b23dc56680..2ff5f54fb0 100644 --- a/WatchApp Extension/Controllers/HUDInterfaceController.swift +++ b/WatchApp Extension/Controllers/HUDInterfaceController.swift @@ -79,7 +79,7 @@ class HUDInterfaceController: WKInterfaceController { eventualGlucoseLabel.setHidden(true) } - if let glucose = activeContext.glucose, let glucoseDate = activeContext.glucoseDate, let unit = activeContext.displayGlucoseUnit, glucoseDate.timeIntervalSinceNow > -LoopCoreConstants.inputDataRecencyInterval { + if let glucose = activeContext.glucose, let glucoseDate = activeContext.glucoseDate, let unit = activeContext.displayGlucoseUnit, glucoseDate.timeIntervalSinceNow > -LoopAlgorithm.inputDataRecencyInterval { let formatter = NumberFormatter.glucoseFormatter(for: unit) if let glucoseValue = formatter.string(from: glucose.doubleValue(for: unit)) {