From e1a9c04eb55f288bccca09cff5a4686ce0e77c11 Mon Sep 17 00:00:00 2001 From: Johan Degraeve Date: Sun, 27 Dec 2020 23:56:13 +0100 Subject: [PATCH] Loop share changes : get latest readings with timestamp > latest share. To make sure that's there's always 60 readings shared, the shared readings are stored in userdefaults (as array of dictionary), and will be reused next time that one or more new readings are shared. This will allow to update readings in coredata (due to smoothing algorithms), but still sharing the older values with Loop, and still making sure there's always 60 readings shared --- .../xDripClient/XDripClient.swift | 2 +- xdrip/Constants/ConstantsShareWithLoop.swift | 5 +- xdrip/Extensions/UserDefaults.swift | 29 ++++++++++++ xdrip/Managers/Loop/LoopManager.swift | 46 +++++++++++-------- .../RootViewController.swift | 6 +-- 5 files changed, 62 insertions(+), 26 deletions(-) diff --git a/xDrip4iOS Widget/xDripClient/XDripClient.swift b/xDrip4iOS Widget/xDripClient/XDripClient.swift index e35dd12e..d50a158a 100644 --- a/xDrip4iOS Widget/xDripClient/XDripClient.swift +++ b/xDrip4iOS Widget/xDripClient/XDripClient.swift @@ -30,7 +30,7 @@ public class XDripClient { private func fetchLastWithRetries(_ n: Int, remaining: Int, callback: @escaping (ClientError?, [Glucose]?) -> Void) { do { - guard let sharedData = shared?.data(forKey: "latestReadings-widget") else { + guard let sharedData = shared?.data(forKey: "latestReadings") else { throw ClientError.fetchError } diff --git a/xdrip/Constants/ConstantsShareWithLoop.swift b/xdrip/Constants/ConstantsShareWithLoop.swift index 1bd0bc52..7c93cd5b 100644 --- a/xdrip/Constants/ConstantsShareWithLoop.swift +++ b/xdrip/Constants/ConstantsShareWithLoop.swift @@ -3,9 +3,6 @@ import Foundation enum ConstantsShareWithLoop { /// maximum number of readings to share with Loop - static let maxReadingsToShareWithLoop = 12 + static let maxReadingsToShareWithLoop = 60 - /// if the time between the last and last but one reading is less than minimiumTimeBetweenTwoReadingsInMinutes, then the reading will not be shared with loop - except if there's been a disconnect in between these two readings - static let minimiumTimeBetweenTwoReadingsInMinutes = 4.75 - } diff --git a/xdrip/Extensions/UserDefaults.swift b/xdrip/Extensions/UserDefaults.swift index df3b4071..778e6b71 100644 --- a/xdrip/Extensions/UserDefaults.swift +++ b/xdrip/Extensions/UserDefaults.swift @@ -198,6 +198,13 @@ extension UserDefaults { /// timestamp of latest reading uploaded to Dexcom Share case timeStampLatestDexcomShareUploadedBgReading = "timeStampLatestDexcomShareUploadedBgReading" + // Loop + /// dictionary representation of readings that were shared with Loop. This is not the json representation, it's an array of dictionary + case readingsStoredInSharedUserDefaultsAsDictionary = "readingsStoredInSharedUserDefaultsAsDictionary" + + /// timestamp lastest reading shared with Loop + case timeStampLatestLoopSharedBgReading = "timeStampLatestLoopSharedBgReading" + // Trace /// should debug level logs be added in trace file or not, and also in NSLog case addDebugLevelLogsInTraceFileAndNSLog = "addDebugLevelLogsInTraceFileAndNSLog" @@ -1081,6 +1088,28 @@ extension UserDefaults { } } + // MARK: - ===== Loop Share Settings ====== + + /// dictionary representation of readings that were shared with Loop. This is not the json representation, it's an array of dictionary + var readingsStoredInSharedUserDefaultsAsDictionary: [Dictionary]? { + get { + return object(forKey: Key.readingsStoredInSharedUserDefaultsAsDictionary.rawValue) as? [Dictionary] + } + set { + set(newValue, forKey: Key.readingsStoredInSharedUserDefaultsAsDictionary.rawValue) + } + } + + /// timestamp lastest reading uploaded to NightScout + var timeStampLatestLoopSharedBgReading:Date? { + get { + return object(forKey: Key.timeStampLatestLoopSharedBgReading.rawValue) as? Date + } + set { + set(newValue, forKey: Key.timeStampLatestLoopSharedBgReading.rawValue) + } + } + // MARK: - ===== technical settings for testing ====== /// G6 factor 1 diff --git a/xdrip/Managers/Loop/LoopManager.swift b/xdrip/Managers/Loop/LoopManager.swift index 5ccc8de0..c8c7547a 100644 --- a/xdrip/Managers/Loop/LoopManager.swift +++ b/xdrip/Managers/Loop/LoopManager.swift @@ -37,14 +37,18 @@ public class LoopManager:NSObject { // MARK: - public functions /// share latest readings with Loop - /// - lastConnectionStatusChangeTimeStamp : when was the last transmitter dis/reconnect - if nil then 1 1 1970 is used - public func share(lastConnectionStatusChangeTimeStamp: Date?) { + public func share() { // unwrap sharedUserDefaults guard let sharedUserDefaults = sharedUserDefaults else {return} - // get last readings with calculated value, don't apply yet the filtering, we will first store for the widget unfiltered - var lastReadings = bgReadingsAccessor.getLatestBgReadings(limit: ConstantsShareWithLoop.maxReadingsToShareWithLoop, fromDate: nil, forSensor: nil, ignoreRawData: true, ignoreCalculatedValue: false) + // get last readings with calculated value + let lastReadings = bgReadingsAccessor.getLatestBgReadings(limit: ConstantsShareWithLoop.maxReadingsToShareWithLoop, fromDate: UserDefaults.standard.timeStampLatestLoopSharedBgReading, forSensor: nil, ignoreRawData: true, ignoreCalculatedValue: false) + + // if there's no readings, then no further processing + if lastReadings.count == 0 { + return + } // convert to json Dexcom Share format var dictionary = [Dictionary]() @@ -52,30 +56,36 @@ public class LoopManager:NSObject { dictionary.append(reading.dictionaryRepresentationForDexcomShareUpload) } - // to json - if let data = try? JSONSerialization.data(withJSONObject: dictionary) { - - // share to userDefaults for widget - if lastReadings.count > 0 { - sharedUserDefaults.set(data, forKey: "latestReadings-widget") + // get Dictionary stored in UserDefaults from previous session + // append readings already stored in this storedDictionary so that we get dictionary filled with maxReadingsToShareWithLoop readings, if possible + if let storedDictionary = UserDefaults.standard.readingsStoredInSharedUserDefaultsAsDictionary, storedDictionary.count > 0 { + + let maxAmountsOfReadingsToAppend = ConstantsShareWithLoop.maxReadingsToShareWithLoop - dictionary.count + + if maxAmountsOfReadingsToAppend > 0 { + + let rangeToAppend = 0..<(min(storedDictionary.count, maxAmountsOfReadingsToAppend)) + + for value in storedDictionary[rangeToAppend] { + + dictionary.append(value) + + } + } } - // applying minimumTimeBetweenTwoReadingsInMinutes filter, for loop - lastReadings = lastReadings.filter(minimumTimeBetweenTwoReadingsInMinutes: ConstantsShareWithLoop.minimiumTimeBetweenTwoReadingsInMinutes, lastConnectionStatusChangeTimeStamp: lastConnectionStatusChangeTimeStamp, timeStampLastProcessedBgReading: nil) - - // if there's no readings, then no further processing - if lastReadings.count == 0 { - return - } - guard let data = try? JSONSerialization.data(withJSONObject: dictionary) else { return } sharedUserDefaults.set(data, forKey: "latestReadings") + UserDefaults.standard.timeStampLatestLoopSharedBgReading = lastReadings.first!.timeStamp + + UserDefaults.standard.readingsStoredInSharedUserDefaultsAsDictionary = dictionary + } } diff --git a/xdrip/View Controllers/Root View Controller/RootViewController.swift b/xdrip/View Controllers/Root View Controller/RootViewController.swift index 604f2195..2fb61ec6 100644 --- a/xdrip/View Controllers/Root View Controller/RootViewController.swift +++ b/xdrip/View Controllers/Root View Controller/RootViewController.swift @@ -786,7 +786,7 @@ final class RootViewController: UIViewController { watchManager?.processNewReading(lastConnectionStatusChangeTimeStamp: lastConnectionStatusChangeTimeStamp()) - loopManager?.share(lastConnectionStatusChangeTimeStamp: lastConnectionStatusChangeTimeStamp()) + loopManager?.share() } } @@ -1092,7 +1092,7 @@ final class RootViewController: UIViewController { self.watchManager?.processNewReading(lastConnectionStatusChangeTimeStamp: self.lastConnectionStatusChangeTimeStamp()) // send also to loopmanager, not interesting for loop probably, but the data is also used for today widget - self.loopManager?.share(lastConnectionStatusChangeTimeStamp: self.lastConnectionStatusChangeTimeStamp()) + self.loopManager?.share() } @@ -1817,7 +1817,7 @@ extension RootViewController:NightScoutFollowerDelegate { bluetoothPeripheralManager?.sendLatestReading() // send also to loopmanager, not interesting for loop probably, but the data is also used for today widget - self.loopManager?.share(lastConnectionStatusChangeTimeStamp: lastConnectionStatusChangeTimeStamp()) + self.loopManager?.share() } }