|
@@ -8,12 +8,10 @@ import Swinject
|
|
|
protocol GlucoseStorage {
|
|
protocol GlucoseStorage {
|
|
|
func storeGlucose(_ glucose: [BloodGlucose])
|
|
func storeGlucose(_ glucose: [BloodGlucose])
|
|
|
func removeGlucose(ids: [String])
|
|
func removeGlucose(ids: [String])
|
|
|
- func recent() -> [BloodGlucose]
|
|
|
|
|
func syncDate() -> Date
|
|
func syncDate() -> Date
|
|
|
func filterTooFrequentGlucose(_ glucose: [BloodGlucose], at: Date) -> [BloodGlucose]
|
|
func filterTooFrequentGlucose(_ glucose: [BloodGlucose], at: Date) -> [BloodGlucose]
|
|
|
func lastGlucoseDate() -> Date
|
|
func lastGlucoseDate() -> Date
|
|
|
func isGlucoseFresh() -> Bool
|
|
func isGlucoseFresh() -> Bool
|
|
|
- func isGlucoseNotFlat() -> Bool
|
|
|
|
|
func nightscoutGlucoseNotUploaded() -> [BloodGlucose]
|
|
func nightscoutGlucoseNotUploaded() -> [BloodGlucose]
|
|
|
func nightscoutCGMStateNotUploaded() -> [NigtscoutTreatment]
|
|
func nightscoutCGMStateNotUploaded() -> [NigtscoutTreatment]
|
|
|
func nightscoutManualGlucoseNotUploaded() -> [NigtscoutTreatment]
|
|
func nightscoutManualGlucoseNotUploaded() -> [NigtscoutTreatment]
|
|
@@ -50,53 +48,53 @@ final class BaseGlucoseStorage: GlucoseStorage, Injectable {
|
|
|
func storeGlucose(_ glucose: [BloodGlucose]) {
|
|
func storeGlucose(_ glucose: [BloodGlucose]) {
|
|
|
processQueue.sync {
|
|
processQueue.sync {
|
|
|
debug(.deviceManager, "start storage glucose")
|
|
debug(.deviceManager, "start storage glucose")
|
|
|
- let file = OpenAPS.Monitor.glucose
|
|
|
|
|
- self.storage.transaction { storage in
|
|
|
|
|
- storage.append(glucose, to: file, uniqBy: \.dateString)
|
|
|
|
|
-
|
|
|
|
|
- let uniqEvents = storage.retrieve(file, as: [BloodGlucose].self)?
|
|
|
|
|
- .filter { $0.dateString.addingTimeInterval(24.hours.timeInterval) > Date() }
|
|
|
|
|
- .sorted { $0.dateString > $1.dateString } ?? []
|
|
|
|
|
- let glucose = Array(uniqEvents)
|
|
|
|
|
- storage.save(glucose, as: file)
|
|
|
|
|
-
|
|
|
|
|
- DispatchQueue.main.async {
|
|
|
|
|
- self.broadcaster.notify(GlucoseObserver.self, on: .main) {
|
|
|
|
|
- $0.glucoseDidUpdate(glucose.reversed())
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- // MARK: - Save to CoreData.
|
|
|
|
|
-
|
|
|
|
|
- var bg_ = 0
|
|
|
|
|
- var direction = ""
|
|
|
|
|
-
|
|
|
|
|
- if glucose.isNotEmpty {
|
|
|
|
|
- bg_ = glucose[0].glucose ?? 0
|
|
|
|
|
- direction = glucose[0].direction?.symbol ?? "↔︎"
|
|
|
|
|
- }
|
|
|
|
|
|
|
+// let file = OpenAPS.Monitor.glucose
|
|
|
|
|
+// self.storage.transaction { storage in
|
|
|
|
|
+// storage.append(glucose, to: file, uniqBy: \.dateString)
|
|
|
|
|
+//
|
|
|
|
|
+// let uniqEvents = storage.retrieve(file, as: [BloodGlucose].self)?
|
|
|
|
|
+// .filter { $0.dateString.addingTimeInterval(24.hours.timeInterval) > Date() }
|
|
|
|
|
+// .sorted { $0.dateString > $1.dateString } ?? []
|
|
|
|
|
+// let glucose = Array(uniqEvents)
|
|
|
|
|
+// storage.save(glucose, as: file)
|
|
|
|
|
+//
|
|
|
|
|
+// DispatchQueue.main.async {
|
|
|
|
|
+// self.broadcaster.notify(GlucoseObserver.self, on: .main) {
|
|
|
|
|
+// $0.glucoseDidUpdate(glucose.reversed())
|
|
|
|
|
+// }
|
|
|
|
|
+// }
|
|
|
|
|
+
|
|
|
|
|
+ // MARK: - Save to CoreData.
|
|
|
|
|
+
|
|
|
|
|
+ var bg_ = 0
|
|
|
|
|
+ var direction = ""
|
|
|
|
|
+
|
|
|
|
|
+ if glucose.isNotEmpty {
|
|
|
|
|
+ bg_ = glucose[0].glucose ?? 0
|
|
|
|
|
+ direction = glucose[0].direction?.symbol ?? "↔︎"
|
|
|
|
|
+ }
|
|
|
|
|
|
|
|
- if bg_ != 0 {
|
|
|
|
|
- self.coredataContext.perform {
|
|
|
|
|
- let newItem = GlucoseStored(context: self.coredataContext)
|
|
|
|
|
- newItem.id = UUID()
|
|
|
|
|
- newItem.glucose = Int16(bg_)
|
|
|
|
|
- newItem.date = Date()
|
|
|
|
|
- newItem.direction = direction
|
|
|
|
|
-
|
|
|
|
|
- if self.coredataContext.hasChanges {
|
|
|
|
|
- do {
|
|
|
|
|
- try self.coredataContext.save()
|
|
|
|
|
- debugPrint(
|
|
|
|
|
- "Glucose Storage: \(CoreDataStack.identifier) \(DebuggingIdentifiers.succeeded) saved glucose to core data"
|
|
|
|
|
- )
|
|
|
|
|
- } catch {
|
|
|
|
|
- debugPrint(
|
|
|
|
|
- "Glucose Storage: \(CoreDataStack.identifier) \(DebuggingIdentifiers.failed) failed to save glucose to core data"
|
|
|
|
|
- )
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ if bg_ != 0 {
|
|
|
|
|
+ self.coredataContext.perform {
|
|
|
|
|
+ let newItem = GlucoseStored(context: self.coredataContext)
|
|
|
|
|
+ newItem.id = UUID()
|
|
|
|
|
+ newItem.glucose = Int16(bg_)
|
|
|
|
|
+ newItem.date = Date()
|
|
|
|
|
+ newItem.direction = direction
|
|
|
|
|
+
|
|
|
|
|
+ if self.coredataContext.hasChanges {
|
|
|
|
|
+ do {
|
|
|
|
|
+ try self.coredataContext.save()
|
|
|
|
|
+ debugPrint(
|
|
|
|
|
+ "Glucose Storage: \(CoreDataStack.identifier) \(DebuggingIdentifiers.succeeded) saved glucose to core data"
|
|
|
|
|
+ )
|
|
|
|
|
+ } catch {
|
|
|
|
|
+ debugPrint(
|
|
|
|
|
+ "Glucose Storage: \(CoreDataStack.identifier) \(DebuggingIdentifiers.failed) failed to save glucose to core data"
|
|
|
|
|
+ )
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
+// }
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
@@ -178,20 +176,13 @@ final class BaseGlucoseStorage: GlucoseStorage, Injectable {
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
func syncDate() -> Date {
|
|
func syncDate() -> Date {
|
|
|
- guard let events = storage.retrieve(OpenAPS.Monitor.glucose, as: [BloodGlucose].self),
|
|
|
|
|
- let recent = events.first
|
|
|
|
|
- else {
|
|
|
|
|
- return Date().addingTimeInterval(-1.days.timeInterval)
|
|
|
|
|
- }
|
|
|
|
|
- return recent.dateString
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- func recent() -> [BloodGlucose] {
|
|
|
|
|
- storage.retrieve(OpenAPS.Monitor.glucose, as: [BloodGlucose].self)?.reversed() ?? []
|
|
|
|
|
|
|
+ // TODO: - proof logic here!
|
|
|
|
|
+ /// previously the Blood Glucose array was retrieved and the date of the first, i.e. oldest value was returned (called recent????)
|
|
|
|
|
+ fetchGlucose().last?.date ?? .distantPast
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
func lastGlucoseDate() -> Date {
|
|
func lastGlucoseDate() -> Date {
|
|
|
- recent().last?.dateString ?? .distantPast
|
|
|
|
|
|
|
+ fetchGlucose().first?.date ?? .distantPast
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
func isGlucoseFresh() -> Bool {
|
|
func isGlucoseFresh() -> Bool {
|
|
@@ -214,17 +205,99 @@ final class BaseGlucoseStorage: GlucoseStorage, Injectable {
|
|
|
return filtered
|
|
return filtered
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- func isGlucoseNotFlat() -> Bool {
|
|
|
|
|
- let count = 3 // check last 3 readings
|
|
|
|
|
- let lastReadings = Array(recent().suffix(count))
|
|
|
|
|
- let filtered = lastReadings.compactMap(\.filtered).filter { $0 != 0 }
|
|
|
|
|
- guard lastReadings.count == count, filtered.count == count else { return true }
|
|
|
|
|
- return Array(filtered.uniqued()).count != 1
|
|
|
|
|
|
|
+ // MARK: - fetching non manual Glucose, manual Glucose and the last glucose value
|
|
|
|
|
+
|
|
|
|
|
+ // TODO: -optimize this bullshit here...I would love to use the async/await pattern, but its simply not possible because you would need to change all the calls of the following functions and make them async...same shit with the NSAsynchronousFetchRequest
|
|
|
|
|
+ /// its all done on a background thread and on a separate queue so hopefully its not too heavy
|
|
|
|
|
+ /// also tried this but here again you need to make everything asynchronous...
|
|
|
|
|
+ /// let privateContext = NSManagedObjectContext(concurrencyType: .privateQueueConcurrencyType)
|
|
|
|
|
+ /// privateContext.parent = coredataContext /// merges changes to the core data context
|
|
|
|
|
+ private func fetchGlucose() -> [GlucoseStored] {
|
|
|
|
|
+ do {
|
|
|
|
|
+ debugPrint("OpenAPS: \(#function) \(DebuggingIdentifiers.succeeded) fetched glucose")
|
|
|
|
|
+ return try coredataContext.fetch(GlucoseStored.fetch(
|
|
|
|
|
+ NSPredicate.predicateForOneDayAgo,
|
|
|
|
|
+ ascending: false,
|
|
|
|
|
+ fetchLimit: 288,
|
|
|
|
|
+ batchSize: 50
|
|
|
|
|
+ ))
|
|
|
|
|
+ } catch {
|
|
|
|
|
+ debugPrint("OpenAPS: \(#function) \(DebuggingIdentifiers.failed) failed to fetch glucose")
|
|
|
|
|
+ return []
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ private func fetchLatestGlucose() -> GlucoseStored? {
|
|
|
|
|
+ do {
|
|
|
|
|
+ debugPrint("Glucose Storage: \(#function) \(DebuggingIdentifiers.succeeded) fetched glucose")
|
|
|
|
|
+ return try coredataContext.fetch(GlucoseStored.fetch(
|
|
|
|
|
+ NSPredicate.predicateFor20MinAgo,
|
|
|
|
|
+ ascending: false,
|
|
|
|
|
+ fetchLimit: 1
|
|
|
|
|
+ )).first
|
|
|
|
|
+ } catch {
|
|
|
|
|
+ debugPrint("Glucose Storage: \(#function) \(DebuggingIdentifiers.failed) failed to fetch glucose")
|
|
|
|
|
+ return nil
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ private func fetchAndProcessManualGlucose() -> [BloodGlucose] {
|
|
|
|
|
+ do {
|
|
|
|
|
+ let fetchedResults = try coredataContext.fetch(GlucoseStored.fetch(
|
|
|
|
|
+ NSPredicate.manualGlucose,
|
|
|
|
|
+ ascending: false,
|
|
|
|
|
+ fetchLimit: 288,
|
|
|
|
|
+ batchSize: 50
|
|
|
|
|
+ ))
|
|
|
|
|
+ debugPrint("Glucose Storage: \(#function) \(DebuggingIdentifiers.succeeded) fetched manual glucose")
|
|
|
|
|
+ let glucoseArray = fetchedResults.map { result in
|
|
|
|
|
+ BloodGlucose(
|
|
|
|
|
+ date: Decimal(result.date?.timeIntervalSince1970 ?? Date().timeIntervalSince1970) * 1000,
|
|
|
|
|
+ dateString: result.date ?? Date(),
|
|
|
|
|
+ unfiltered: Decimal(result.glucose),
|
|
|
|
|
+ filtered: Decimal(result.glucose),
|
|
|
|
|
+ noise: nil,
|
|
|
|
|
+ type: ""
|
|
|
|
|
+ )
|
|
|
|
|
+ }
|
|
|
|
|
+ return glucoseArray
|
|
|
|
|
+ } catch {
|
|
|
|
|
+ debugPrint("Glucose Storage: \(#function) \(DebuggingIdentifiers.failed) failed to fetch manual glucose")
|
|
|
|
|
+ return []
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ private func fetchAndProcessGlucose() -> [BloodGlucose] {
|
|
|
|
|
+ do {
|
|
|
|
|
+ let results = try coredataContext.fetch(GlucoseStored.fetch(
|
|
|
|
|
+ NSPredicate.predicateForOneDayAgo,
|
|
|
|
|
+ ascending: false,
|
|
|
|
|
+ fetchLimit: 288,
|
|
|
|
|
+ batchSize: 50
|
|
|
|
|
+ ))
|
|
|
|
|
+
|
|
|
|
|
+ debugPrint("Glucose Storage: \(#function) \(DebuggingIdentifiers.succeeded) fetched glucose")
|
|
|
|
|
+
|
|
|
|
|
+ let glucoseArray = results.map { result in
|
|
|
|
|
+ BloodGlucose(
|
|
|
|
|
+ date: Decimal(result.date?.timeIntervalSince1970 ?? Date().timeIntervalSince1970) * 1000,
|
|
|
|
|
+ dateString: result.date ?? Date(),
|
|
|
|
|
+ unfiltered: Decimal(result.glucose),
|
|
|
|
|
+ filtered: Decimal(result.glucose),
|
|
|
|
|
+ noise: nil,
|
|
|
|
|
+ type: ""
|
|
|
|
|
+ )
|
|
|
|
|
+ }
|
|
|
|
|
+ return glucoseArray
|
|
|
|
|
+ } catch {
|
|
|
|
|
+ debugPrint("Glucose Storage: \(#function) \(DebuggingIdentifiers.failed) failed to fetch glucose")
|
|
|
|
|
+ return []
|
|
|
|
|
+ }
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
func nightscoutGlucoseNotUploaded() -> [BloodGlucose] {
|
|
func nightscoutGlucoseNotUploaded() -> [BloodGlucose] {
|
|
|
let uploaded = storage.retrieve(OpenAPS.Nightscout.uploadedGlucose, as: [BloodGlucose].self) ?? []
|
|
let uploaded = storage.retrieve(OpenAPS.Nightscout.uploadedGlucose, as: [BloodGlucose].self) ?? []
|
|
|
- let recentGlucose = recent()
|
|
|
|
|
|
|
+ let recentGlucose = fetchAndProcessGlucose()
|
|
|
|
|
|
|
|
return Array(Set(recentGlucose).subtracting(Set(uploaded)))
|
|
return Array(Set(recentGlucose).subtracting(Set(uploaded)))
|
|
|
}
|
|
}
|
|
@@ -238,7 +311,8 @@ final class BaseGlucoseStorage: GlucoseStorage, Injectable {
|
|
|
func nightscoutManualGlucoseNotUploaded() -> [NigtscoutTreatment] {
|
|
func nightscoutManualGlucoseNotUploaded() -> [NigtscoutTreatment] {
|
|
|
let uploaded = (storage.retrieve(OpenAPS.Nightscout.uploadedGlucose, as: [BloodGlucose].self) ?? [])
|
|
let uploaded = (storage.retrieve(OpenAPS.Nightscout.uploadedGlucose, as: [BloodGlucose].self) ?? [])
|
|
|
.filter({ $0.type == GlucoseType.manual.rawValue })
|
|
.filter({ $0.type == GlucoseType.manual.rawValue })
|
|
|
- let recent = recent().filter({ $0.type == GlucoseType.manual.rawValue })
|
|
|
|
|
|
|
+
|
|
|
|
|
+ let recent = fetchAndProcessManualGlucose()
|
|
|
let filtered = Array(Set(recent).subtracting(Set(uploaded)))
|
|
let filtered = Array(Set(recent).subtracting(Set(uploaded)))
|
|
|
let manualReadings = filtered.map { item -> NigtscoutTreatment in
|
|
let manualReadings = filtered.map { item -> NigtscoutTreatment in
|
|
|
NigtscoutTreatment(
|
|
NigtscoutTreatment(
|
|
@@ -256,8 +330,10 @@ final class BaseGlucoseStorage: GlucoseStorage, Injectable {
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
var alarm: GlucoseAlarm? {
|
|
var alarm: GlucoseAlarm? {
|
|
|
- guard let glucose = recent().last, glucose.dateString.addingTimeInterval(20.minutes.timeInterval) > Date(),
|
|
|
|
|
- let glucoseValue = glucose.glucose else { return nil }
|
|
|
|
|
|
|
+ /// glucose can not be older than 20 minutes due to the predicate in the fetch request
|
|
|
|
|
+ guard let glucose = fetchLatestGlucose() else { return nil }
|
|
|
|
|
+
|
|
|
|
|
+ let glucoseValue = glucose.glucose
|
|
|
|
|
|
|
|
if Decimal(glucoseValue) <= settingsManager.settings.lowGlucose {
|
|
if Decimal(glucoseValue) <= settingsManager.settings.lowGlucose {
|
|
|
return .low
|
|
return .low
|