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

This commit is contained in:
Johan Degraeve 2020-12-27 23:56:13 +01:00
parent efda0d9742
commit e1a9c04eb5
5 changed files with 62 additions and 26 deletions

View File

@ -30,7 +30,7 @@ public class XDripClient {
private func fetchLastWithRetries(_ n: Int, remaining: Int, callback: @escaping (ClientError?, [Glucose]?) -> Void) { private func fetchLastWithRetries(_ n: Int, remaining: Int, callback: @escaping (ClientError?, [Glucose]?) -> Void) {
do { do {
guard let sharedData = shared?.data(forKey: "latestReadings-widget") else { guard let sharedData = shared?.data(forKey: "latestReadings") else {
throw ClientError.fetchError throw ClientError.fetchError
} }

View File

@ -3,9 +3,6 @@ import Foundation
enum ConstantsShareWithLoop { enum ConstantsShareWithLoop {
/// maximum number of readings to share with Loop /// 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
} }

View File

@ -198,6 +198,13 @@ extension UserDefaults {
/// timestamp of latest reading uploaded to Dexcom Share /// timestamp of latest reading uploaded to Dexcom Share
case timeStampLatestDexcomShareUploadedBgReading = "timeStampLatestDexcomShareUploadedBgReading" 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 // Trace
/// should debug level logs be added in trace file or not, and also in NSLog /// should debug level logs be added in trace file or not, and also in NSLog
case addDebugLevelLogsInTraceFileAndNSLog = "addDebugLevelLogsInTraceFileAndNSLog" 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<String, Any>]? {
get {
return object(forKey: Key.readingsStoredInSharedUserDefaultsAsDictionary.rawValue) as? [Dictionary<String, Any>]
}
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 ====== // MARK: - ===== technical settings for testing ======
/// G6 factor 1 /// G6 factor 1

View File

@ -37,14 +37,18 @@ public class LoopManager:NSObject {
// MARK: - public functions // MARK: - public functions
/// share latest readings with Loop /// share latest readings with Loop
/// - lastConnectionStatusChangeTimeStamp : when was the last transmitter dis/reconnect - if nil then 1 1 1970 is used public func share() {
public func share(lastConnectionStatusChangeTimeStamp: Date?) {
// unwrap sharedUserDefaults // unwrap sharedUserDefaults
guard let sharedUserDefaults = sharedUserDefaults else {return} 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 // get last readings with calculated value
var lastReadings = bgReadingsAccessor.getLatestBgReadings(limit: ConstantsShareWithLoop.maxReadingsToShareWithLoop, fromDate: nil, forSensor: nil, ignoreRawData: true, ignoreCalculatedValue: false) 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 // convert to json Dexcom Share format
var dictionary = [Dictionary<String, Any>]() var dictionary = [Dictionary<String, Any>]()
@ -52,22 +56,24 @@ public class LoopManager:NSObject {
dictionary.append(reading.dictionaryRepresentationForDexcomShareUpload) dictionary.append(reading.dictionaryRepresentationForDexcomShareUpload)
} }
// to json // get Dictionary stored in UserDefaults from previous session
if let data = try? JSONSerialization.data(withJSONObject: dictionary) { // 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)
// share to userDefaults for widget
if lastReadings.count > 0 {
sharedUserDefaults.set(data, forKey: "latestReadings-widget")
} }
} }
// 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 { guard let data = try? JSONSerialization.data(withJSONObject: dictionary) else {
@ -76,6 +82,10 @@ public class LoopManager:NSObject {
sharedUserDefaults.set(data, forKey: "latestReadings") sharedUserDefaults.set(data, forKey: "latestReadings")
UserDefaults.standard.timeStampLatestLoopSharedBgReading = lastReadings.first!.timeStamp
UserDefaults.standard.readingsStoredInSharedUserDefaultsAsDictionary = dictionary
} }
} }

View File

@ -786,7 +786,7 @@ final class RootViewController: UIViewController {
watchManager?.processNewReading(lastConnectionStatusChangeTimeStamp: lastConnectionStatusChangeTimeStamp()) watchManager?.processNewReading(lastConnectionStatusChangeTimeStamp: lastConnectionStatusChangeTimeStamp())
loopManager?.share(lastConnectionStatusChangeTimeStamp: lastConnectionStatusChangeTimeStamp()) loopManager?.share()
} }
} }
@ -1092,7 +1092,7 @@ final class RootViewController: UIViewController {
self.watchManager?.processNewReading(lastConnectionStatusChangeTimeStamp: self.lastConnectionStatusChangeTimeStamp()) self.watchManager?.processNewReading(lastConnectionStatusChangeTimeStamp: self.lastConnectionStatusChangeTimeStamp())
// send also to loopmanager, not interesting for loop probably, but the data is also used for today widget // 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() bluetoothPeripheralManager?.sendLatestReading()
// send also to loopmanager, not interesting for loop probably, but the data is also used for today widget // 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()
} }
} }