Merge branch 'restrictreadings' into develop

This commit is contained in:
Johan Degraeve 2020-12-27 21:55:03 +01:00
commit efda0d9742
17 changed files with 187 additions and 59 deletions

View File

@ -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") else {
guard let sharedData = shared?.data(forKey: "latestReadings-widget") else {
throw ClientError.fetchError
}

View File

@ -378,6 +378,8 @@
F8B48AA422B2FA9B009BCC01 /* CalibrationRequest.strings in Resources */ = {isa = PBXBuildFile; fileRef = F8B48AA222B2FA9A009BCC01 /* CalibrationRequest.strings */; };
F8B955EB2591355200C06016 /* CGMLibre2Transmitter+TestData.swift in Sources */ = {isa = PBXBuildFile; fileRef = F8B955EA2591355200C06016 /* CGMLibre2Transmitter+TestData.swift */; };
F8B9560A259294FA00C06016 /* GlucoseDataFilterFlatValues.swift in Sources */ = {isa = PBXBuildFile; fileRef = F8B95609259294FA00C06016 /* GlucoseDataFilterFlatValues.swift */; };
F8B955B1258BEE9D00C06016 /* ConstantsSpeakReading.swift in Sources */ = {isa = PBXBuildFile; fileRef = F8B955B0258BEE9D00C06016 /* ConstantsSpeakReading.swift */; };
F8B955B7258D5E2000C06016 /* ConstantsHealthKit.swift in Sources */ = {isa = PBXBuildFile; fileRef = F8B955B6258D5E2000C06016 /* ConstantsHealthKit.swift */; };
F8BDD4242218790E006EAB84 /* UserDefaults.swift in Sources */ = {isa = PBXBuildFile; fileRef = F8BDD4232218790E006EAB84 /* UserDefaults.swift */; };
F8BDD438221A0349006EAB84 /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = F8BDD436221A0349006EAB84 /* Localizable.strings */; };
F8BDD43F221B5BAF006EAB84 /* TextsErrorMessages.swift in Sources */ = {isa = PBXBuildFile; fileRef = F8BDD43E221B5BAF006EAB84 /* TextsErrorMessages.swift */; };
@ -987,6 +989,8 @@
F8B48B1222B37C84009BCC01 /* zh */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = zh; path = zh.lproj/SettingsViews.strings; sourceTree = "<group>"; };
F8B955EA2591355200C06016 /* CGMLibre2Transmitter+TestData.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "CGMLibre2Transmitter+TestData.swift"; sourceTree = "<group>"; };
F8B95609259294FA00C06016 /* GlucoseDataFilterFlatValues.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GlucoseDataFilterFlatValues.swift; sourceTree = "<group>"; };
F8B955B0258BEE9D00C06016 /* ConstantsSpeakReading.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConstantsSpeakReading.swift; sourceTree = "<group>"; };
F8B955B6258D5E2000C06016 /* ConstantsHealthKit.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConstantsHealthKit.swift; sourceTree = "<group>"; };
F8BDD4232218790E006EAB84 /* UserDefaults.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserDefaults.swift; sourceTree = "<group>"; };
F8BDD435221A0005006EAB84 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/Main.strings; sourceTree = "<group>"; };
F8BDD437221A0349006EAB84 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/Localizable.strings; sourceTree = "<group>"; };
@ -2131,6 +2135,7 @@
F8A1585A22EDB7EA007F5B5D /* ConstantsDexcomG5.swift */,
F8A1587222EDC893007F5B5D /* ConstantsDexcomShare.swift */,
F80859262364355F00F3829D /* ConstantsGlucoseChart.swift */,
F8B955B6258D5E2000C06016 /* ConstantsHealthKit.swift */,
F8A1586622EDB8BF007F5B5D /* ConstantsHomeView.swift */,
F8E5404B2522624800052CE5 /* ConstantsHousekeeping.swift */,
F8252866243E50FE0067AF77 /* ConstantsLibre.swift */,
@ -2140,12 +2145,13 @@
F8A389EC23342EB10010F405 /* ConstantsNightScout.swift */,
F8A1586022EDB844007F5B5D /* ConstantsNotifications.swift */,
F8E51D5E2448E2E8001C9E5A /* ConstantsShareWithLoop.swift */,
F85FB768255DE14600D1C39E /* ConstantsSmoothing.swift */,
F8A1586222EDB86E007F5B5D /* ConstantsSounds.swift */,
F8B955B0258BEE9D00C06016 /* ConstantsSpeakReading.swift */,
F8A1587022EDC865007F5B5D /* ConstantsSpeakReadingLanguages.swift */,
F8A1586E22EDC7EE007F5B5D /* ConstantsSuspensionPrevention.swift */,
F8AF36142455C6F700B5977B /* ConstantsTrace.swift */,
F8E3A2AA23DA520B00E5E98A /* ConstantsWatch.swift */,
F85FB768255DE14600D1C39E /* ConstantsSmoothing.swift */,
);
name = Constants;
path = xdrip/Constants;
@ -2912,6 +2918,7 @@
F8C97856242AA86B00A09483 /* CGMMiaoMiaoTransmitterDelegate.swift in Sources */,
F898EDF6234A8A5700BFB79B /* UInt32.swift in Sources */,
F816E0F32433DAA9009EE65B /* BluetoothPeripheralManager+CGMBluconTransmitterDelegate.swift in Sources */,
F8B955B1258BEE9D00C06016 /* ConstantsSpeakReading.swift in Sources */,
F8F9723223A5915900C3F17D /* M5StackReadBlePassWordTxMessage.swift in Sources */,
F8A54AAF22D686CD00934E7A /* NightScoutBgReading.swift in Sources */,
F8DF765323E34F4500063910 /* DexcomG5+CoreDataClass.swift in Sources */,
@ -2961,6 +2968,7 @@
F8F9723923A5928D00C3F17D /* M5StackBluetoothPeripheralViewModel.swift in Sources */,
F8F9722B23A5915900C3F17D /* CGMTransmitterDelegate.swift in Sources */,
F80859292364D61B00F3829D /* UserDefaults+charts.swift in Sources */,
F8B955B7258D5E2000C06016 /* ConstantsHealthKit.swift in Sources */,
F8B3A7B2226A0878004BA588 /* TextsAlerts.swift in Sources */,
F8297F46238DC4AC00D74D66 /* BluetoothPeripheralManaging.swift in Sources */,
F80D917024F85C7A006840B5 /* Libre2+BluetoothPeripheral.swift in Sources */,

View File

@ -10,6 +10,9 @@ enum ConstantsDexcomShare {
/// non us share base url
static let nonUsBaseShareUrl = "https://shareous1.dexcom.com/ShareWebServices/Services"
/// if the time between the last and last but one reading is less than minimiumTimeBetweenTwoReadingsInMinutes, then the last reading will not be uploaded - except if there's been a disconnect in between these two readings
static let minimiumTimeBetweenTwoReadingsInMinutes = 4.75
}

View File

@ -0,0 +1,8 @@
import Foundation
enum ConstantsHealthKit {
/// if the time between the last and last but one reading is less than minimiumTimeBetweenTwoReadingsInMinutes, then no new event event will be stored in healthkit - except if there's been a disconnect in between these two readings
static let minimiumTimeBetweenTwoReadingsInMinutes = 4.75
}

View File

@ -7,4 +7,8 @@ enum ConstantsNightScout {
/// - used in settings, when setting first time nightscout url
static let defaultNightScoutUrl = "https://yoursitename.herokuapp.com"
/// if the time between the last and last but one reading is less than minimiumTimeBetweenTwoReadingsInMinutes, then the last reading will not be uploaded - except if there's been a disconnect in between these two readings
static let minimiumTimeBetweenTwoReadingsInMinutes = 4.75
}

View File

@ -54,4 +54,7 @@ enum ConstantsNotifications {
/// notification identifier for xDripErrors received in RootViewController's cgmTransmitterDelegate
static let notificationIdentifierForxCGMTransmitterDelegatexDripError = "notificationIdentifierForxCGMTransmitterDelegatexDripError"
/// if the time between the last and last but one reading is less than minimiumTimeBetweenTwoReadingsInMinutes, then there will bd no notification created - except if there's been a disconnect in between these two readings
static let minimiumTimeBetweenTwoReadingsInMinutes = 4.75
}

View File

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

@ -0,0 +1,8 @@
import Foundation
enum ConstantsSpeakReading {
/// if the time between the last and last but one reading is less than minimiumTimeBetweenTwoReadingsInMinutes, then the reading will not be spoken - except if there's been a disconnect in between these two readings
static let minimiumTimeBetweenTwoReadingsInMinutes = 4.75
}

View File

@ -5,4 +5,7 @@ enum ConstantsWatch {
/// text to add as notes in glucose events
static let textInCreatedEvent = "created by xdrip"
/// if the time between the last and last but one reading is less than minimiumTimeBetweenTwoReadingsInMinutes, then no new event will be created - except if there's been a disconnect in between these two readings
static let minimiumTimeBetweenTwoReadingsInMinutes = 4.75
}

View File

@ -271,7 +271,54 @@ extension Array where Element: GlucoseData {
}
}
}
extension Array where Element: BgReading {
/// Filter out readings that are too close to each other
/// - parameters:
/// - minimumTimeBetweenTwoReadingsInMinutes : filter out readings that are to close to each other in time, minimum difference in time between two readings = minimumTimeBetweenTwoReadingsInMinutes
/// - lastConnectionStatusChangeTimeStamp : lastConnectionStatusChangeTimeStamp > timeStampLastProcessedBgReading then the first connection will be returned, even if it's less than minimumTimeBetweenTwoReadingsInMinutes away from timeStampLastProcessedBgReading
/// - timeStampLastProcessedBgReading : only readings younger than timeStampLastProcessedBgReading will be returned, if nil then this check is not done
/// - returns
/// filtered array, with readings at least minimumTimeBetweenTwoReadingsInMinutes away from each other
func filter(minimumTimeBetweenTwoReadingsInMinutes: Double, lastConnectionStatusChangeTimeStamp: Date?, timeStampLastProcessedBgReading: Date?) -> [BgReading] {
var didCheckLastConnectionStatusChangeTimeStamp = false
var timeStampLatestCheckedReading = timeStampLastProcessedBgReading
return self.filter({
if let lastConnectionStatusChangeTimeStamp = lastConnectionStatusChangeTimeStamp, let timeStampLastProcessedBgReading = timeStampLastProcessedBgReading, !didCheckLastConnectionStatusChangeTimeStamp {
didCheckLastConnectionStatusChangeTimeStamp = true
// if there was a disconnect or reconnect after the latest processed reading, then add this reading - this will only apply to the first reading
if lastConnectionStatusChangeTimeStamp.timeIntervalSince(timeStampLastProcessedBgReading) > 0.0 {
return true
}
}
var returnValue = true
if let timeStampLatestCheckedReading = timeStampLatestCheckedReading {
returnValue = $0.timeStamp.timeIntervalSince(timeStampLatestCheckedReading) > minimumTimeBetweenTwoReadingsInMinutes * 60.0
}
timeStampLatestCheckedReading = $0.timeStamp
return returnValue
})
}
}
/// source https://github.com/raywenderlich/swift-algorithm-club/tree/master/Linear%20Regression

View File

@ -66,7 +66,9 @@ class DexcomShareUploadManager:NSObject {
// MARK: - public functions
/// uploads latest BgReadings to Dexcom Share
public func upload() {
/// - parameters:
/// - lastConnectionStatusChangeTimeStamp : when was the last transmitter dis/reconnect - if nil then 1 1 1970 is used
public func upload(lastConnectionStatusChangeTimeStamp: Date?) {
// check if dexcomShare is enabled
guard UserDefaults.standard.uploadReadingstoDexcomShare else {return}
@ -87,7 +89,7 @@ class DexcomShareUploadManager:NSObject {
}
// upload
uploadBgReadingsToDexcomShare(firstAttempt: true)
uploadBgReadingsToDexcomShare(firstAttempt: true, lastConnectionStatusChangeTimeStamp: lastConnectionStatusChangeTimeStamp)
}
@ -114,7 +116,7 @@ class DexcomShareUploadManager:NSObject {
if success {
trace("in observeValue, start upload", log: self.log, category: ConstantsLog.categoryDexcomShareUploadManager, type: .info)
self.upload()
self.upload(lastConnectionStatusChangeTimeStamp: nil)
} else {
trace("in observeValue, Dexcom Share credential check failed", log: self.log, category: ConstantsLog.categoryDexcomShareUploadManager, type: .error)
@ -143,7 +145,7 @@ class DexcomShareUploadManager:NSObject {
if success {
trace("in observeValue, start upload", log: self.log, category: ConstantsLog.categoryDexcomShareUploadManager, type: .info)
self.upload()
self.upload(lastConnectionStatusChangeTimeStamp: nil)
} else {
@ -170,9 +172,11 @@ class DexcomShareUploadManager:NSObject {
// MARK: - private helper functions
/// will call StartRemoteMonitoringSession with serialNumber
/// - parameters:
/// - lastConnectionStatusChangeTimeStamp : when was the last transmitter dis/reconnect - if nil then 1 1 1970 is used
///
/// dexcomShareSessionId and UserDefaults.standard.dexcomShareSerialNumber should be not nil
private func startRemoteMonitoringSessionAndStartUpload() {
private func startRemoteMonitoringSessionAndStartUpload(lastConnectionStatusChangeTimeStamp: Date?) {
trace("in startRemoteMonitoringSessionAndStartUpload", log: log, category: ConstantsLog.categoryDexcomShareUploadManager, type: .info)
@ -274,7 +278,7 @@ class DexcomShareUploadManager:NSObject {
//there's no error, call uploadBgReadingsToDexcomShare in main thread
DispatchQueue.main.async {
self.uploadBgReadingsToDexcomShare(firstAttempt: true)
self.uploadBgReadingsToDexcomShare(firstAttempt: true, lastConnectionStatusChangeTimeStamp: lastConnectionStatusChangeTimeStamp)
}
} else {
@ -297,9 +301,10 @@ class DexcomShareUploadManager:NSObject {
/// will try to upload the latest readings - if dexcomShareSessionId is nil then first a login attempt will be done and after that an upload.
/// - parameters:
/// - firstAttempt : if true, and if dexcomShareSessionId not nil, but upload attempt fails with because dexcomShareSessionId is not valid, then a new login attempt will be done, after which a new upload attempt - if false, then no new upload attempt will be done
/// - lastConnectionStatusChangeTimeStamp : when was the last transmitter dis/reconnect - if nil then 1 1 1970 is used
///
/// firstAttempt is there to avoid that the app runs in an endless loop
private func uploadBgReadingsToDexcomShare(firstAttempt:Bool) {
private func uploadBgReadingsToDexcomShare(firstAttempt:Bool, lastConnectionStatusChangeTimeStamp: Date?) {
trace("in uploadBgReadingsToDexcomShare", log: log, category: ConstantsLog.categoryDexcomShareUploadManager, type: .info)
@ -319,7 +324,7 @@ class DexcomShareUploadManager:NSObject {
if success {
trace("in uploadBgReadingsToDexcomShare, login successful, will restart uploadBgReadingsToDexcomShare", log: self.log, category: ConstantsLog.categoryDexcomShareUploadManager, type: .info)
// retry the upload
self.uploadBgReadingsToDexcomShare(firstAttempt: firstAttempt)
self.uploadBgReadingsToDexcomShare(firstAttempt: firstAttempt, lastConnectionStatusChangeTimeStamp: lastConnectionStatusChangeTimeStamp)
} else {
trace("in uploadBgReadingsToDexcomShare, login failed, no further processing", log: self.log, category: ConstantsLog.categoryDexcomShareUploadManager, type: .error)
}
@ -328,12 +333,15 @@ class DexcomShareUploadManager:NSObject {
return
}
// get readings to upload, limit to 8 hours
// get timestamp of first reading to upload, limit to 8 hours
var timeStamp = Date(timeIntervalSinceNow: -8*60*60)
if let timeStampLatestDexcomShareUploadedBgReading = UserDefaults.standard.timeStampLatestDexcomShareUploadedBgReading {
timeStamp = timeStampLatestDexcomShareUploadedBgReading
}
let bgReadingsToUpload = bgReadingsAccessor.getLatestBgReadings(limit: nil, fromDate: timeStamp, forSensor: nil, ignoreRawData: true, ignoreCalculatedValue: false)
// get readings to upload, applying minimumTimeBetweenTwoReadingsInMinutes filter
let bgReadingsToUpload = bgReadingsAccessor.getLatestBgReadings(limit: nil, fromDate: timeStamp, forSensor: nil, ignoreRawData: true, ignoreCalculatedValue: false).filter(minimumTimeBetweenTwoReadingsInMinutes: ConstantsDexcomShare.minimiumTimeBetweenTwoReadingsInMinutes, lastConnectionStatusChangeTimeStamp: lastConnectionStatusChangeTimeStamp, timeStampLastProcessedBgReading: timeStamp)
trace(" number of readings to upload : %{public}@", log: log, category: ConstantsLog.categoryDexcomShareUploadManager, type: .info, bgReadingsToUpload.count.description)
// if no no readings to upload, no further processing
@ -422,7 +430,7 @@ class DexcomShareUploadManager:NSObject {
if success {
trace("in uploadBgReadingsToDexcomShare, new login successful", log: self.log, category: ConstantsLog.categoryDexcomShareUploadManager, type: .info)
// retry the upload
self.uploadBgReadingsToDexcomShare(firstAttempt: false)
self.uploadBgReadingsToDexcomShare(firstAttempt: false, lastConnectionStatusChangeTimeStamp: lastConnectionStatusChangeTimeStamp)
} else {
trace("in uploadBgReadingsToDexcomShare, new login failed", log: self.log, category: ConstantsLog.categoryDexcomShareUploadManager, type: .error)
}
@ -434,7 +442,7 @@ class DexcomShareUploadManager:NSObject {
} else if errorCode == "MonitoringSessionNotActive" {
// call startRemoteMonitoringSessionAndStartUpload in main thread
DispatchQueue.main.async {
self.startRemoteMonitoringSessionAndStartUpload()
self.startRemoteMonitoringSessionAndStartUpload(lastConnectionStatusChangeTimeStamp: lastConnectionStatusChangeTimeStamp)
}
return

View File

@ -50,7 +50,7 @@ public class HealthKitManager:NSObject {
healthKitInitialized = initializeHealthKit()
// do first store
storeBgReadings()
storeBgReadings(lastConnectionStatusChangeTimeStamp: nil)
}
@ -95,7 +95,9 @@ public class HealthKitManager:NSObject {
}
/// stores latest readings in healthkit, only if HK supported, authorized, enabled in settings
public func storeBgReadings() {
/// - parameters:
/// - lastConnectionStatusChangeTimeStamp : when was the last transmitter dis/reconnect - if nil then 1 1 1970 is used
public func storeBgReadings(lastConnectionStatusChangeTimeStamp: Date?) {
// healthkit setting must be on, and healthkit must be initialized successfully
if !UserDefaults.standard.storeReadingsInHealthkit || !healthKitInitialized {
@ -105,8 +107,8 @@ public class HealthKitManager:NSObject {
// bloodGlucoseType should not be nil
guard let bloodGlucoseType = bloodGlucoseType else {return}
// get readings to store, limit to 2016 = maximum 1 week - just to avoid a huge array is being returned here
let bgReadingsToStore = bgReadingsAccessor.getLatestBgReadings(limit: 2016, fromDate: UserDefaults.standard.timeStampLatestHealthKitStoreBgReading, forSensor: nil, ignoreRawData: true, ignoreCalculatedValue: false)
// get readings to store, limit to 2016 = maximum 1 week - just to avoid a huge array is being returned here, applying minimumTimeBetweenTwoReadingsInMinutes filter
let bgReadingsToStore = bgReadingsAccessor.getLatestBgReadings(limit: 2016, fromDate: UserDefaults.standard.timeStampLatestHealthKitStoreBgReading, forSensor: nil, ignoreRawData: true, ignoreCalculatedValue: false).filter(minimumTimeBetweenTwoReadingsInMinutes: ConstantsHealthKit.minimiumTimeBetweenTwoReadingsInMinutes, lastConnectionStatusChangeTimeStamp: lastConnectionStatusChangeTimeStamp, timeStampLastProcessedBgReading: UserDefaults.standard.timeStampLatestHealthKitStoreBgReading)
let bloodGlucoseUnit = HKUnit.init(from: "mg/dL")
@ -149,7 +151,7 @@ public class HealthKitManager:NSObject {
healthKitInitialized = initializeHealthKit()
// doesn't matter which if the two settings got changed, it's ok to call initialize
storeBgReadings()
storeBgReadings(lastConnectionStatusChangeTimeStamp: nil)
}

View File

@ -10,8 +10,6 @@ import Foundation
public class LoopManager:NSObject {
// MARK: - public properties
// MARK: - private properties
/// reference to coreDataManager
@ -23,6 +21,8 @@ public class LoopManager:NSObject {
/// shared UserDefaults to publish data
private let sharedUserDefaults = UserDefaults(suiteName: Bundle.main.appGroupSuiteName)
// MARK: - initializer
init(coreDataManager:CoreDataManager) {
// initialize non optional private properties
@ -34,31 +34,46 @@ public class LoopManager:NSObject {
}
// MARK: - public functions
/// share latest readings with Loop
public func share() {
/// - lastConnectionStatusChangeTimeStamp : when was the last transmitter dis/reconnect - if nil then 1 1 1970 is used
public func share(lastConnectionStatusChangeTimeStamp: Date?) {
// unwrap sharedUserDefaults
guard let sharedUserDefaults = sharedUserDefaults else {return}
// get last readings with calculated value
let lastReadings = bgReadingsAccessor.getLatestBgReadings(limit: ConstantsShareWithLoop.maxReadingsToShareWithLoop, fromDate: nil, forSensor: nil, ignoreRawData: true, ignoreCalculatedValue: false)
// if there's no readings, then no further processing
if lastReadings.count == 0 {
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)
// convert to json Dexcom Share format
var dictionary = [Dictionary<String, Any>]()
for reading in lastReadings {
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")
}
}
// 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")
}

View File

@ -51,6 +51,7 @@ public class NightScoutUploadManager:NSObject {
/// - parameters:
/// - coreDataManager : needed to get latest readings
/// - messageHandler : in case errors occur like credential check error, then this closure will be called with title and message
/// - checkIfDisReConnectAfterTimeStampFunction : function to verify if there's been a disconnect or reconnect after the timestamp of the given reading
init(coreDataManager: CoreDataManager, messageHandler:((_ title:String, _ message:String) -> Void)?) {
// init properties
@ -74,7 +75,9 @@ public class NightScoutUploadManager:NSObject {
// MARK: - public functions
/// uploads latest BgReadings to NightScout, only if nightscout enabled, not master, url and key defined, if schedule enabled then check also schedule
public func upload() {
/// - parameters:
/// - lastConnectionStatusChangeTimeStamp : when was the last transmitter dis/reconnect - if nil then 1 1 1970 is used
public func upload(lastConnectionStatusChangeTimeStamp: Date?) {
// check if NightScout is enabled
guard UserDefaults.standard.nightScoutEnabled else {return}
@ -95,7 +98,7 @@ public class NightScoutUploadManager:NSObject {
}
// upload readings
uploadBgReadingsToNightScout(siteURL: siteURL, apiKey: apiKey)
uploadBgReadingsToNightScout(siteURL: siteURL, apiKey: apiKey, lastConnectionStatusChangeTimeStamp: lastConnectionStatusChangeTimeStamp)
// upload calibrations
uploadCalibrationsToNightScout(siteURL: siteURL, apiKey: apiKey)
@ -146,7 +149,7 @@ public class NightScoutUploadManager:NSObject {
DispatchQueue.main.async {
self.callMessageHandler(withCredentialVerificationResult: success, error: error)
if success {
self.upload()
self.upload(lastConnectionStatusChangeTimeStamp: nil)
} else {
trace("in observeValue, NightScout credential check failed", log: self.oslog, category: ConstantsLog.categoryNightScoutUploadManager, type: .info)
}
@ -168,7 +171,7 @@ public class NightScoutUploadManager:NSObject {
testNightScoutCredentials(apiKey: apiKey, siteURL: siteUrl, { (success, error) in
DispatchQueue.main.async {
if success {
self.upload()
self.upload(lastConnectionStatusChangeTimeStamp: nil)
} else {
trace("in observeValue, NightScout credential check failed", log: self.oslog, category: ConstantsLog.categoryNightScoutUploadManager, type: .info)
}
@ -253,7 +256,7 @@ public class NightScoutUploadManager:NSObject {
/// - parameters:
/// - siteURL : nightscout site url
/// - apiKey : nightscout api key
private func uploadBgReadingsToNightScout(siteURL:String, apiKey:String) {
private func uploadBgReadingsToNightScout(siteURL:String, apiKey:String, lastConnectionStatusChangeTimeStamp: Date?) {
trace("in uploadBgReadingsToNightScout", log: self.oslog, category: ConstantsLog.categoryNightScoutUploadManager, type: .info)
@ -266,7 +269,8 @@ public class NightScoutUploadManager:NSObject {
}
}
let bgReadingsToUpload = bgReadingsAccessor.getLatestBgReadings(limit: nil, fromDate: timeStamp, forSensor: nil, ignoreRawData: true, ignoreCalculatedValue: false)
// get latest readings, filter : minimiumTimeBetweenTwoReadingsInMinutes beteen two readings, except for the first if a dis/reconnect occured since the latest reading
let bgReadingsToUpload = bgReadingsAccessor.getLatestBgReadings(limit: nil, fromDate: timeStamp, forSensor: nil, ignoreRawData: true, ignoreCalculatedValue: false).filter(minimumTimeBetweenTwoReadingsInMinutes: ConstantsNightScout.minimiumTimeBetweenTwoReadingsInMinutes, lastConnectionStatusChangeTimeStamp: lastConnectionStatusChangeTimeStamp, timeStampLastProcessedBgReading: timeStamp)
if bgReadingsToUpload.count > 0 {
trace(" number of readings to upload : %{public}@", log: self.oslog, category: ConstantsLog.categoryNightScoutUploadManager, type: .info, bgReadingsToUpload.count.description)

View File

@ -60,7 +60,8 @@ class BGReadingSpeaker:NSObject {
/// - no other sound is playing (via sharedAudioPlayer)
/// - there' s a recent reading less than 4.5 minutes old
/// - time since last spoken reading > interval defined by user (UserDefaults.standard.speakInterval)
public func speakNewReading() {
/// - lastConnectionStatusChangeTimeStamp : when was the last transmitter dis/reconnect - if nil then 1 1 1970 is used
public func speakNewReading(lastConnectionStatusChangeTimeStamp: Date?) {
// if speak reading not enabled, then no further processing
if !UserDefaults.standard.speakReadings {
@ -72,8 +73,8 @@ class BGReadingSpeaker:NSObject {
return
}
// get latest reading, ignore sensor, rawdata, timestamp - only 1
let lastReadings = bgReadingsAccessor.get2LatestBgReadings(minimumTimeIntervalInMinutes: 4.0)
// get latest reading, ignore sensor, rawdata, timestamp - only 1 - applying minimumTimeBetweenTwoReadingsInMinutes filter
let lastReadings = bgReadingsAccessor.get2LatestBgReadings(minimumTimeIntervalInMinutes: 4.0).filter(minimumTimeBetweenTwoReadingsInMinutes: ConstantsSpeakReading.minimiumTimeBetweenTwoReadingsInMinutes, lastConnectionStatusChangeTimeStamp: lastConnectionStatusChangeTimeStamp, timeStampLastProcessedBgReading: nil)
// if there's no readings, then no further processing
if lastReadings.count == 0 {

View File

@ -30,18 +30,19 @@ class WatchManager: NSObject {
// MARK: - public functions
/// process new readings
public func processNewReading() {
/// - lastConnectionStatusChangeTimeStamp : when was the last transmitter dis/reconnect - if nil then 1 1 1970 is used
public func processNewReading(lastConnectionStatusChangeTimeStamp: Date?) {
// check if createCalenderEvent is enabled in the settings and if so create calender event
if UserDefaults.standard.createCalendarEvent {
createCalendarEvent()
createCalendarEvent(lastConnectionStatusChangeTimeStamp: lastConnectionStatusChangeTimeStamp)
}
}
// MARK: - private functions
private func createCalendarEvent() {
private func createCalendarEvent(lastConnectionStatusChangeTimeStamp: Date?) {
// check that access to calendar is authorized by the user
guard EKEventStore.authorizationStatus(for: .event) == .authorized else {
@ -56,7 +57,7 @@ class WatchManager: NSObject {
}
// get 2 last Readings, with a calculatedValue
let lastReading = bgReadingsAccessor.get2LatestBgReadings(minimumTimeIntervalInMinutes: 4.0)//
let lastReading = bgReadingsAccessor.get2LatestBgReadings(minimumTimeIntervalInMinutes: 4.0).filter(minimumTimeBetweenTwoReadingsInMinutes: ConstantsWatch.minimiumTimeBetweenTwoReadingsInMinutes, lastConnectionStatusChangeTimeStamp: lastConnectionStatusChangeTimeStamp, timeStampLastProcessedBgReading: nil)
// there should be at least one reading
guard lastReading.count > 0 else {

View File

@ -774,19 +774,19 @@ final class RootViewController: UIViewController {
updateLabelsAndChart(overrideApplicationState: false)
}
nightScoutUploadManager?.upload()
nightScoutUploadManager?.upload(lastConnectionStatusChangeTimeStamp: lastConnectionStatusChangeTimeStamp())
healthKitManager?.storeBgReadings()
healthKitManager?.storeBgReadings(lastConnectionStatusChangeTimeStamp: lastConnectionStatusChangeTimeStamp())
bgReadingSpeaker?.speakNewReading()
bgReadingSpeaker?.speakNewReading(lastConnectionStatusChangeTimeStamp: lastConnectionStatusChangeTimeStamp())
dexcomShareUploadManager?.upload()
dexcomShareUploadManager?.upload(lastConnectionStatusChangeTimeStamp: lastConnectionStatusChangeTimeStamp())
bluetoothPeripheralManager?.sendLatestReading()
watchManager?.processNewReading()
watchManager?.processNewReading(lastConnectionStatusChangeTimeStamp: lastConnectionStatusChangeTimeStamp())
loopManager?.share()
loopManager?.share(lastConnectionStatusChangeTimeStamp: lastConnectionStatusChangeTimeStamp())
}
}
@ -1074,12 +1074,12 @@ final class RootViewController: UIViewController {
// initiate upload to NightScout, if needed
if let nightScoutUploadManager = self.nightScoutUploadManager {
nightScoutUploadManager.upload()
nightScoutUploadManager.upload(lastConnectionStatusChangeTimeStamp: self.lastConnectionStatusChangeTimeStamp())
}
// initiate upload to Dexcom Share, if needed
if let dexcomShareUploadManager = self.dexcomShareUploadManager {
dexcomShareUploadManager.upload()
dexcomShareUploadManager.upload(lastConnectionStatusChangeTimeStamp: self.lastConnectionStatusChangeTimeStamp())
}
// update labels
@ -1089,10 +1089,10 @@ final class RootViewController: UIViewController {
self.bluetoothPeripheralManager?.sendLatestReading()
// watchManager should process new reading
self.watchManager?.processNewReading()
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()
self.loopManager?.share(lastConnectionStatusChangeTimeStamp: self.lastConnectionStatusChangeTimeStamp())
}
@ -1205,7 +1205,7 @@ final class RootViewController: UIViewController {
}
// get lastReading, with a calculatedValue - no check on activeSensor because in follower mode there is no active sensor
let lastReading = bgReadingsAccessor.get2LatestBgReadings(minimumTimeIntervalInMinutes: 4.0)//
let lastReading = bgReadingsAccessor.get2LatestBgReadings(minimumTimeIntervalInMinutes: 4.0).filter(minimumTimeBetweenTwoReadingsInMinutes: ConstantsNotifications.minimiumTimeBetweenTwoReadingsInMinutes, lastConnectionStatusChangeTimeStamp: lastConnectionStatusChangeTimeStamp(), timeStampLastProcessedBgReading: nil)
// if there's no reading for active sensor with calculated value , then no reason to continue
if lastReading.count == 0 {
@ -1591,6 +1591,16 @@ final class RootViewController: UIViewController {
}
// a long function just to get the timestamp of the last disconnect or reconnect. If not known then returns 1 1 1970
private func lastConnectionStatusChangeTimeStamp() -> Date {
// this is actually unwrapping of optionals, goal is to get date of last disconnect/reconnect - all optionals should exist so it doesn't matter what is returned true or false
guard let cgmTransmitter = self.bluetoothPeripheralManager?.getCGMTransmitter(), let bluetoothTransmitter = cgmTransmitter as? BluetoothTransmitter, let bluetoothPeripheral = self.bluetoothPeripheralManager?.getBluetoothPeripheral(for: bluetoothTransmitter), let lastConnectionStatusChangeTimeStamp = bluetoothPeripheral.blePeripheral.lastConnectionStatusChangeTimeStamp else {return Date(timeIntervalSince1970: 0)}
return lastConnectionStatusChangeTimeStamp
}
}
// MARK: - conform to CGMTransmitter protocol
@ -1797,17 +1807,17 @@ extension RootViewController:NightScoutFollowerDelegate {
checkAlertsCreateNotificationAndSetAppBadge()
if let healthKitManager = healthKitManager {
healthKitManager.storeBgReadings()
healthKitManager.storeBgReadings(lastConnectionStatusChangeTimeStamp: lastConnectionStatusChangeTimeStamp())
}
if let bgReadingSpeaker = bgReadingSpeaker {
bgReadingSpeaker.speakNewReading()
bgReadingSpeaker.speakNewReading(lastConnectionStatusChangeTimeStamp: lastConnectionStatusChangeTimeStamp())
}
bluetoothPeripheralManager?.sendLatestReading()
// send also to loopmanager, not interesting for loop probably, but the data is also used for today widget
self.loopManager?.share()
self.loopManager?.share(lastConnectionStatusChangeTimeStamp: lastConnectionStatusChangeTimeStamp())
}
}