From 558e674dc559381c08c89aa276253cea95ecc04b Mon Sep 17 00:00:00 2001 From: Mark Wilson Date: Thu, 20 Oct 2016 16:29:28 -0700 Subject: [PATCH] Upload carbs-on-board to Nightscout --- Loop/Managers/LoopDataManager.swift | 25 ++++++++++++++++--- Loop/Managers/NightscoutDataManager.swift | 17 ++++++++++--- Loop/Managers/WatchDataManager.swift | 2 +- .../PredictionTableViewController.swift | 2 +- .../StatusTableViewController.swift | 2 +- 5 files changed, 38 insertions(+), 10 deletions(-) diff --git a/Loop/Managers/LoopDataManager.swift b/Loop/Managers/LoopDataManager.swift index f5edaaa111..5714acc26a 100644 --- a/Loop/Managers/LoopDataManager.swift +++ b/Loop/Managers/LoopDataManager.swift @@ -76,6 +76,7 @@ final class LoopDataManager { center.addObserver(forName: .CarbEntriesDidUpdate, object: nil, queue: nil) { (note) -> Void in self.dataAccessQueue.async { self.carbEffect = nil + self.carbsOnBoardSeries = nil self.notify(forChange: .carbs) } } @@ -165,6 +166,18 @@ final class LoopDataManager { } } + if carbsOnBoardSeries == nil, let carbStore = deviceDataManager.carbStore { + updateGroup.enter() + carbStore.getCarbsOnBoardValues { (values, error) in + if let error = error { + self.deviceDataManager.logger.addError(error, fromSource: "CarbStore") + } + + self.carbsOnBoardSeries = values + updateGroup.leave() + } + } + if insulinEffect == nil { updateGroup.enter() updateInsulinEffect { (effects, error) in @@ -229,9 +242,10 @@ final class LoopDataManager { - lastTempBasal: The last set temp basal - lastLoopCompleted: The last date at which a loop completed, from prediction to dose (if dosing is enabled) - insulinOnBoard Current insulin on board + - carbsOnBoard Current carbs on board - error: An error in the current state of the loop, or one that happened during the last attempt to loop. */ - func getLoopStatus(_ resultsHandler: @escaping (_ predictedGlucose: [GlucoseValue]?, _ retrospectivePredictedGlucose: [GlucoseValue]?, _ recommendedTempBasal: TempBasalRecommendation?, _ lastTempBasal: DoseEntry?, _ lastLoopCompleted: Date?, _ insulinOnBoard: InsulinValue?, _ error: Error?) -> Void) { + func getLoopStatus(_ resultsHandler: @escaping (_ predictedGlucose: [GlucoseValue]?, _ retrospectivePredictedGlucose: [GlucoseValue]?, _ recommendedTempBasal: TempBasalRecommendation?, _ lastTempBasal: DoseEntry?, _ lastLoopCompleted: Date?, _ insulinOnBoard: InsulinValue?, _ carbsOnBoard: CarbValue?, _ error: Error?) -> Void) { dataAccessQueue.async { var error: Error? @@ -241,7 +255,9 @@ final class LoopDataManager { error = updateError } - resultsHandler(self.predictedGlucose, self.retrospectivePredictedGlucose, self.recommendedTempBasal, self.lastTempBasal, self.lastLoopCompleted, self.insulinOnBoard, error ?? self.lastLoopError) + let currentCOB = self.carbsOnBoardSeries?.closestPriorToDate(Date()) + + resultsHandler(self.predictedGlucose, self.retrospectivePredictedGlucose, self.recommendedTempBasal, self.lastTempBasal, self.lastLoopCompleted, self.insulinOnBoard, currentCOB, error ?? self.lastLoopError) } } @@ -294,6 +310,7 @@ final class LoopDataManager { retrospectivePredictedGlucose = nil } } + private var carbsOnBoardSeries: [CarbValue]? private var insulinEffect: [GlucoseEffect]? { didSet { if let bolusDate = lastBolus?.date, bolusDate.timeIntervalSinceNow < TimeInterval(minutes: -5) { @@ -544,6 +561,7 @@ final class LoopDataManager { self.dataAccessQueue.async { if success { self.carbEffect = nil + self.carbsOnBoardSeries = nil do { try self.update() @@ -673,7 +691,7 @@ extension LoopDataManager { /// /// - parameter completionHandler: A closure called once the report has been generated. The closure takes a single argument of the report string. func generateDiagnosticReport(_ completionHandler: @escaping (_ report: String) -> Void) { - getLoopStatus { (predictedGlucose, retrospectivePredictedGlucose, recommendedTempBasal, lastTempBasal, lastLoopCompleted, insulinOnBoard, error) in + getLoopStatus { (predictedGlucose, retrospectivePredictedGlucose, recommendedTempBasal, lastTempBasal, lastLoopCompleted, insulinOnBoard, carbsOnBoard, error) in let report = [ "## LoopDataManager", "predictedGlucose: \(predictedGlucose ?? [])", @@ -682,6 +700,7 @@ extension LoopDataManager { "lastTempBasal: \(lastTempBasal)", "lastLoopCompleted: \(lastLoopCompleted ?? .distantPast)", "insulinOnBoard: \(insulinOnBoard)", + "carbsOnBoard: \(carbsOnBoard)", "error: \(error)" ] completionHandler(report.joined(separator: "\n")) diff --git a/Loop/Managers/NightscoutDataManager.swift b/Loop/Managers/NightscoutDataManager.swift index a6346aab84..cedb694039 100644 --- a/Loop/Managers/NightscoutDataManager.swift +++ b/Loop/Managers/NightscoutDataManager.swift @@ -8,6 +8,7 @@ import Foundation import NightscoutUploadKit +import CarbKit import HealthKit import InsulinKit import LoopKit @@ -34,20 +35,20 @@ class NightscoutDataManager { return } - deviceDataManager.loopManager.getLoopStatus { (predictedGlucose, _, recommendedTempBasal, lastTempBasal, _, insulinOnBoard, loopError) in + deviceDataManager.loopManager.getLoopStatus { (predictedGlucose, _, recommendedTempBasal, lastTempBasal, _, insulinOnBoard, carbsOnBoard, loopError) in self.deviceDataManager.loopManager.getRecommendedBolus { (bolusUnits, getBolusError) in if let getBolusError = getBolusError { self.deviceDataManager.logger.addError(getBolusError, fromSource: "NightscoutDataManager") } - self.uploadLoopStatus(insulinOnBoard, predictedGlucose: predictedGlucose, recommendedTempBasal: recommendedTempBasal, recommendedBolus: bolusUnits, lastTempBasal: lastTempBasal, loopError: loopError ?? getBolusError) + self.uploadLoopStatus(insulinOnBoard, carbsOnBoard: carbsOnBoard, predictedGlucose: predictedGlucose, recommendedTempBasal: recommendedTempBasal, recommendedBolus: bolusUnits, lastTempBasal: lastTempBasal, loopError: loopError ?? getBolusError) } } } private var lastTempBasalUploaded: DoseEntry? - func uploadLoopStatus(_ insulinOnBoard: InsulinValue? = nil, predictedGlucose: [GlucoseValue]? = nil, recommendedTempBasal: LoopDataManager.TempBasalRecommendation? = nil, recommendedBolus: Double? = nil, lastTempBasal: DoseEntry? = nil, loopError: Error? = nil) { + func uploadLoopStatus(_ insulinOnBoard: InsulinValue? = nil, carbsOnBoard: CarbValue? = nil, predictedGlucose: [GlucoseValue]? = nil, recommendedTempBasal: LoopDataManager.TempBasalRecommendation? = nil, recommendedBolus: Double? = nil, lastTempBasal: DoseEntry? = nil, loopError: Error? = nil) { guard deviceDataManager.remoteDataManager.nightscoutUploader != nil else { return @@ -62,6 +63,14 @@ class NightscoutDataManager { } else { iob = nil } + + let cob: COBStatus? + + if let carbsOnBoard = carbsOnBoard { + cob = COBStatus(cob: carbsOnBoard.quantity.doubleValue(for: HKUnit.gram()), timestamp: carbsOnBoard.startDate) + } else { + cob = nil + } let predicted: PredictedBG? if let predictedGlucose = predictedGlucose, let startDate = predictedGlucose.first?.startDate { @@ -93,7 +102,7 @@ class NightscoutDataManager { let loopName = Bundle.main.bundleDisplayName let loopVersion = Bundle.main.shortVersionString - let loopStatus = LoopStatus(name: loopName, version: loopVersion, timestamp: statusTime, iob: iob, predicted: predicted, recommendedTempBasal: recommended, recommendedBolus: recommendedBolus, enacted: loopEnacted, failureReason: loopError) + let loopStatus = LoopStatus(name: loopName, version: loopVersion, timestamp: statusTime, iob: iob, cob: cob, predicted: predicted, recommendedTempBasal: recommended, recommendedBolus: recommendedBolus, enacted: loopEnacted, failureReason: loopError) uploadDeviceStatus(nil, loopStatus: loopStatus, includeUploaderStatus: false) diff --git a/Loop/Managers/WatchDataManager.swift b/Loop/Managers/WatchDataManager.swift index 14670c1364..37c98a3ead 100644 --- a/Loop/Managers/WatchDataManager.swift +++ b/Loop/Managers/WatchDataManager.swift @@ -105,7 +105,7 @@ final class WatchDataManager: NSObject, WCSessionDelegate { let reservoir = deviceDataManager.doseStore.lastReservoirValue let maxBolus = deviceDataManager.maximumBolus - deviceDataManager.loopManager.getLoopStatus { (predictedGlucose, _, recommendedTempBasal, lastTempBasal, lastLoopCompleted, _, error) in + deviceDataManager.loopManager.getLoopStatus { (predictedGlucose, _, recommendedTempBasal, lastTempBasal, lastLoopCompleted, _, _, error) in let eventualGlucose = predictedGlucose?.last self.deviceDataManager.loopManager.getRecommendedBolus { (units, error) in diff --git a/Loop/View Controllers/PredictionTableViewController.swift b/Loop/View Controllers/PredictionTableViewController.swift index ba16d503dd..a446fe1951 100644 --- a/Loop/View Controllers/PredictionTableViewController.swift +++ b/Loop/View Controllers/PredictionTableViewController.swift @@ -151,7 +151,7 @@ class PredictionTableViewController: UITableViewController, IdentifiableClass, U } reloadGroup.enter() - self.dataManager.loopManager.getLoopStatus { (predictedGlucose, retrospectivePredictedGlucose, _, _, _, _, error) in + self.dataManager.loopManager.getLoopStatus { (predictedGlucose, retrospectivePredictedGlucose, _, _, _, _, _, error) in if error != nil { self.needsRefresh = true } diff --git a/Loop/View Controllers/StatusTableViewController.swift b/Loop/View Controllers/StatusTableViewController.swift index 6eae7be5f4..9b9a2ad8f4 100644 --- a/Loop/View Controllers/StatusTableViewController.swift +++ b/Loop/View Controllers/StatusTableViewController.swift @@ -170,7 +170,7 @@ final class StatusTableViewController: UITableViewController, UIGestureRecognize } reloadGroup.enter() - self.dataManager.loopManager.getLoopStatus { (predictedGlucose, _, recommendedTempBasal, lastTempBasal, lastLoopCompleted, _, error) -> Void in + self.dataManager.loopManager.getLoopStatus { (predictedGlucose, _, recommendedTempBasal, lastTempBasal, lastLoopCompleted, _, _, error) -> Void in if error != nil { self.needsRefresh = true }