GlucoseStorage.swift 3.0 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495
  1. import Foundation
  2. import SwiftDate
  3. import Swinject
  4. protocol GlucoseStorage {
  5. func storeGlucose(_ glucose: [BloodGlucose])
  6. func recent() -> [BloodGlucose]
  7. func syncDate() -> Date
  8. func filterTooFrequentGlucose(_ glucose: [BloodGlucose], at: Date) -> [BloodGlucose]
  9. func lastGlucoseDate() -> Date
  10. func isGlucoseFresh() -> Bool
  11. func isGlucoseNotFlat() -> Bool
  12. }
  13. final class BaseGlucoseStorage: GlucoseStorage, Injectable {
  14. private let processQueue = DispatchQueue(label: "BaseGlucoseStorage.processQueue")
  15. @Injected() private var storage: FileStorage!
  16. @Injected() private var broadcaster: Broadcaster!
  17. private enum Config {
  18. static let filterTime: TimeInterval = 4.75 * 60
  19. }
  20. init(resolver: Resolver) {
  21. injectServices(resolver)
  22. }
  23. func storeGlucose(_ glucose: [BloodGlucose]) {
  24. processQueue.sync {
  25. let file = OpenAPS.Monitor.glucose
  26. self.storage.transaction { storage in
  27. storage.append(glucose, to: file, uniqBy: \.dateString)
  28. let uniqEvents = storage.retrieve(file, as: [BloodGlucose].self)?
  29. .filter { $0.dateString.addingTimeInterval(1.days.timeInterval) > Date() }
  30. .sorted { $0.dateString > $1.dateString } ?? []
  31. let glucose = Array(uniqEvents)
  32. storage.save(glucose, as: file)
  33. DispatchQueue.main.async {
  34. self.broadcaster.notify(GlucoseObserver.self, on: .main) {
  35. $0.glucoseDidUpdate(glucose.reversed())
  36. }
  37. }
  38. }
  39. }
  40. }
  41. func syncDate() -> Date {
  42. guard let events = storage.retrieve(OpenAPS.Monitor.glucose, as: [BloodGlucose].self),
  43. let recent = events.first
  44. else {
  45. return Date().addingTimeInterval(-1.days.timeInterval)
  46. }
  47. return recent.dateString
  48. }
  49. func recent() -> [BloodGlucose] {
  50. storage.retrieve(OpenAPS.Monitor.glucose, as: [BloodGlucose].self)?.reversed() ?? []
  51. }
  52. func lastGlucoseDate() -> Date {
  53. recent().last?.dateString ?? .distantPast
  54. }
  55. func isGlucoseFresh() -> Bool {
  56. Date().timeIntervalSince(lastGlucoseDate()) <= Config.filterTime
  57. }
  58. func filterTooFrequentGlucose(_ glucose: [BloodGlucose], at date: Date) -> [BloodGlucose] {
  59. var lastDate = date
  60. var filtered: [BloodGlucose] = []
  61. for entry in glucose.reversed() {
  62. guard entry.dateString.addingTimeInterval(-Config.filterTime) > lastDate else {
  63. continue
  64. }
  65. filtered.append(entry)
  66. lastDate = entry.dateString
  67. }
  68. return filtered
  69. }
  70. func isGlucoseNotFlat() -> Bool {
  71. let last3 = recent().suffix(3)
  72. guard last3.count == 3 else { return true }
  73. return Array(last3.compactMap { $0.filtered ?? Decimal($0.sgv ?? 0) }.uniqued()).count > 1
  74. }
  75. }
  76. protocol GlucoseObserver {
  77. func glucoseDidUpdate(_ glucose: [BloodGlucose])
  78. }