From eedb1fd7277bf8cf6f8da3d7f4011e36322fd863 Mon Sep 17 00:00:00 2001 From: Pete Schwamb Date: Sat, 5 May 2018 18:41:07 +0200 Subject: [PATCH 1/2] Sync pump time automatically * Automatically set pump time if off by 20 seconds or more * Ensure LoopStateView is up-to-date even when the app is running in the background --- Common/Extensions/NSTimeInterval.swift | 4 ++ Loop/Managers/AnalyticsManager.swift | 34 ++++++++--- Loop/Managers/DeviceDataManager.swift | 58 ++++++++++++++----- Loop/Managers/LoopDataManager.swift | 18 +++--- .../StatusTableViewController.swift | 5 ++ LoopUI/Views/LoopCompletionHUDView.swift | 2 +- 6 files changed, 89 insertions(+), 32 deletions(-) diff --git a/Common/Extensions/NSTimeInterval.swift b/Common/Extensions/NSTimeInterval.swift index d40ded0035..be19ded20d 100644 --- a/Common/Extensions/NSTimeInterval.swift +++ b/Common/Extensions/NSTimeInterval.swift @@ -10,6 +10,10 @@ import Foundation extension TimeInterval { + static func seconds(_ seconds: Double) -> TimeInterval { + return seconds + } + static func minutes(_ minutes: Double) -> TimeInterval { return TimeInterval(minutes: minutes) } diff --git a/Loop/Managers/AnalyticsManager.swift b/Loop/Managers/AnalyticsManager.swift index 999bf15b5c..4291a34804 100644 --- a/Loop/Managers/AnalyticsManager.swift +++ b/Loop/Managers/AnalyticsManager.swift @@ -62,19 +62,27 @@ final class AnalyticsManager: IdentifiableClass { // MARK: - Config Events func didChangeRileyLinkConnectionState() { - logEvent("RileyLink Connection") + logEvent("RileyLink Connection", outOfSession: true) } func transmitterTimeDidDrift(_ drift: TimeInterval) { - logEvent("Transmitter time change", withProperties: ["value" : drift]) + logEvent("Transmitter time change", withProperties: ["value" : drift], outOfSession: true) + } + + func pumpTimeDidDrift(_ drift: TimeInterval) { + logEvent("Pump time change", withProperties: ["value": drift], outOfSession: true) + } + + func punpTimeZoneDidChange() { + logEvent("Pump time zone change", outOfSession: true) } func pumpBatteryWasReplaced() { - logEvent("Pump battery replacement") + logEvent("Pump battery replacement", outOfSession: true) } func reservoirWasRewound() { - logEvent("Pump reservoir rewind") + logEvent("Pump reservoir rewind", outOfSession: true) } func didChangeBasalRateSchedule() { @@ -93,12 +101,8 @@ final class AnalyticsManager: IdentifiableClass { logEvent("Insulin sensitivity change") } - func didChangeGlucoseTargetRangeSchedule() { - logEvent("Glucose target range change") - } - func didChangeLoopSettings(from oldValue: LoopSettings, to newValue: LoopSettings) { - logEvent("Loop settings change") + logEvent("Loop settings change", outOfSession: true) if newValue.maximumBasalRatePerHour != oldValue.maximumBasalRatePerHour { logEvent("Maximum basal rate change") @@ -111,6 +115,14 @@ final class AnalyticsManager: IdentifiableClass { if newValue.suspendThreshold != oldValue.suspendThreshold { logEvent("Minimum BG Guard change") } + + if newValue.dosingEnabled != oldValue.dosingEnabled { + logEvent("Closed loop enabled change") + } + + if newValue.retrospectiveCorrectionEnabled != oldValue.retrospectiveCorrectionEnabled { + logEvent("Retrospective correction enabled change") + } } // MARK: - Loop Events @@ -127,6 +139,10 @@ final class AnalyticsManager: IdentifiableClass { logEvent("Bolus set", withProperties: ["source" : "Watch"], outOfSession: true) } + func didFetchNewCGMData() { + logEvent("CGM Fetch", outOfSession: true) + } + func loopDidSucceed() { logEvent("Loop success", outOfSession: true) } diff --git a/Loop/Managers/DeviceDataManager.swift b/Loop/Managers/DeviceDataManager.swift index 146ecc5d08..e9ee6cae47 100644 --- a/Loop/Managers/DeviceDataManager.swift +++ b/Loop/Managers/DeviceDataManager.swift @@ -154,6 +154,10 @@ final class DeviceDataManager { self.lastTimerTick = Date() self.cgmManager?.fetchNewDataIfNeeded(with: self) { (result) in + if case .newData = result { + AnalyticsManager.shared.didFetchNewCGMData() + } + // TODO: Isolate to queue? self.cgmManager(self.cgmManager!, didUpdateWith: result) } @@ -487,6 +491,16 @@ final class DeviceDataManager { return } + // Check if the clock should be reset + if abs(date.timeIntervalSinceNow) > .seconds(20) { + self.logger.addError("Pump clock is more than 20 seconds off. Resetting.", fromSource: "RileyLink") + AnalyticsManager.shared.pumpTimeDidDrift(date.timeIntervalSinceNow) + try session.setTime { () -> DateComponents in + let calendar = Calendar(identifier: .gregorian) + return calendar.dateComponents([.year, .month, .day, .hour, .minute, .second], from: Date()) + } + } + self.observeBatteryDuring { self.latestPumpStatus = status } @@ -840,18 +854,6 @@ extension DeviceDataManager: LoopDataManagerDelegate { return } - let notify = { (result: Result) -> Void in - // If we haven't fetched history in a while (preferredInsulinDataSource == .reservoir), - // let's try to do so while the pump radio is on. - if self.loopManager.doseStore.lastAddedPumpEvents.timeIntervalSinceNow < .minutes(-4) { - self.fetchPumpHistory { (_) in - completion(result) - } - } else { - completion(result) - } - } - pumpOps.runSession(withName: "Set Temp Basal", using: rileyLinkManager.firstConnectedDevice) { (session) in guard let session = session else { completion(.failure(LoopError.connectionError)) @@ -864,15 +866,43 @@ extension DeviceDataManager: LoopDataManagerDelegate { let now = Date() let endDate = now.addingTimeInterval(response.timeRemaining) let startDate = endDate.addingTimeInterval(-basal.recommendation.duration) - notify(.success(DoseEntry( + completion(.success(DoseEntry( type: .tempBasal, startDate: startDate, endDate: endDate, value: response.rate, unit: .unitsPerHour ))) + + // Continue below + } catch let error { + completion(.failure(error)) + return + } + + do { + // If we haven't fetched history in a while, our preferredInsulinDataSource is probably .reservoir, so + // let's take advantage of the pump radio being on. + if self.loopManager.doseStore.lastAddedPumpEvents.timeIntervalSinceNow < .minutes(-4) { + let clock = try session.getTime() + // Check if the clock should be reset + if let date = clock.date, abs(date.timeIntervalSinceNow) > .seconds(20) { + self.logger.addError("Pump clock is more than 20 seconds off. Resetting.", fromSource: "RileyLink") + AnalyticsManager.shared.pumpTimeDidDrift(date.timeIntervalSinceNow) + try session.setTime { () -> DateComponents in + let calendar = Calendar(identifier: .gregorian) + return calendar.dateComponents([.year, .month, .day, .hour, .minute, .second], from: Date()) + } + } + + self.fetchPumpHistory { (error) in + if let error = error { + self.logger.addError("Post-basal history fetch failed: \(error)", fromSource: "RileyLink") + } + } + } } catch let error { - notify(.failure(error)) + self.logger.addError("Post-basal time sync failed: \(error)", fromSource: "RileyLink") } } } diff --git a/Loop/Managers/LoopDataManager.swift b/Loop/Managers/LoopDataManager.swift index 75abd492c3..e65307ae96 100644 --- a/Loop/Managers/LoopDataManager.swift +++ b/Loop/Managers/LoopDataManager.swift @@ -24,6 +24,7 @@ final class LoopDataManager { } static let LoopUpdateContextKey = "com.loudnate.Loop.LoopDataManager.LoopUpdateContext" + static let LastLoopCompletedKey = "com.loopkit.Loop.LoopDataManager.LastLoopCompleted" fileprivate typealias GlucoseChange = (start: GlucoseValue, end: GlucoseValue) @@ -134,13 +135,6 @@ final class LoopDataManager { } } - /// Disable any active workout glucose targets - func disableWorkoutMode() { - settings.glucoseTargetRangeSchedule?.clearOverride() - - notify(forChange: .preferences) - } - /// The length of time insulin has an effect on blood glucose var insulinModelSettings: InsulinModelSettings? { get { @@ -596,9 +590,17 @@ final class LoopDataManager { } private func notify(forChange context: LoopUpdateContext) { + var userInfo: [String: Any] = [ + type(of: self).LoopUpdateContextKey: context.rawValue + ] + + if let lastLoopCompleted = lastLoopCompleted { + userInfo[type(of: self).LastLoopCompletedKey] = lastLoopCompleted + } + NotificationCenter.default.post(name: .LoopDataUpdated, object: self, - userInfo: [type(of: self).LoopUpdateContextKey: context.rawValue] + userInfo: userInfo ) } diff --git a/Loop/View Controllers/StatusTableViewController.swift b/Loop/View Controllers/StatusTableViewController.swift index 09713870d2..0d690ffcf0 100644 --- a/Loop/View Controllers/StatusTableViewController.swift +++ b/Loop/View Controllers/StatusTableViewController.swift @@ -46,6 +46,7 @@ final class StatusTableViewController: ChartsTableViewController { notificationObservers += [ notificationCenter.addObserver(forName: .LoopDataUpdated, object: deviceManager.loopManager, queue: nil) { [unowned self] note in let context = note.userInfo?[LoopDataManager.LoopUpdateContextKey] as! LoopDataManager.LoopUpdateContext.RawValue + let lastLoopCompleted = note.userInfo?[LoopDataManager.LastLoopCompletedKey] as? Date DispatchQueue.main.async { switch LoopDataManager.LoopUpdateContext(rawValue: context) { case .none, .bolus?: @@ -58,6 +59,10 @@ final class StatusTableViewController: ChartsTableViewController { self.refreshContext.formUnion([.glucose, .carbs]) case .tempBasal?: self.refreshContext.update(with: .insulin) + + if let lastLoopCompleted = lastLoopCompleted { + self.hudView?.loopCompletionHUD.lastLoopCompleted = lastLoopCompleted + } } self.hudView?.loopCompletionHUD.loopInProgress = false diff --git a/LoopUI/Views/LoopCompletionHUDView.swift b/LoopUI/Views/LoopCompletionHUDView.swift index 11f1fb3364..313b94e67b 100644 --- a/LoopUI/Views/LoopCompletionHUDView.swift +++ b/LoopUI/Views/LoopCompletionHUDView.swift @@ -95,7 +95,7 @@ public final class LoopCompletionHUDView: BaseHUDView { ) updateTimer = timer - RunLoop.main.add(timer, forMode: RunLoopMode.defaultRunLoopMode) + RunLoop.main.add(timer, forMode: .defaultRunLoopMode) } private var updateTimer: Timer? { From 5a626d55b3d24d0c56aadd20576640e49048fdd3 Mon Sep 17 00:00:00 2001 From: Pete Schwamb Date: Sat, 5 May 2018 18:58:46 +0200 Subject: [PATCH 2/2] Add didChangeGlucoseTargetRangeSchedule to analytics manager --- Loop/Managers/AnalyticsManager.swift | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Loop/Managers/AnalyticsManager.swift b/Loop/Managers/AnalyticsManager.swift index 4291a34804..fe08839154 100644 --- a/Loop/Managers/AnalyticsManager.swift +++ b/Loop/Managers/AnalyticsManager.swift @@ -101,6 +101,10 @@ final class AnalyticsManager: IdentifiableClass { logEvent("Insulin sensitivity change") } + func didChangeGlucoseTargetRangeSchedule() { + logEvent("Glucose target range change") + } + func didChangeLoopSettings(from oldValue: LoopSettings, to newValue: LoopSettings) { logEvent("Loop settings change", outOfSession: true)