Parcourir la source

Merge pull request #880 from bastiaanv/fix/cgm-start-filter

fix: LoopKit.startDateToFilterNewData
Deniz Cengiz il y a 4 mois
Parent
commit
8c46b6b0df

+ 0 - 184
Trio/Sources/APS/CGM/DexcomSourceG5.swift

@@ -1,184 +0,0 @@
-import CGMBLEKit
-import Combine
-import Foundation
-import LoopKit
-import LoopKitUI
-import ShareClient
-
-final class DexcomSourceG5: GlucoseSource {
-    private let processQueue = DispatchQueue(label: "DexcomSource.processQueue")
-    private let glucoseStorage: GlucoseStorage!
-    var glucoseManager: FetchGlucoseManager?
-
-    var cgmManager: CGMManagerUI?
-    var cgmType: CGMType = .dexcomG5
-
-    var cgmHasValidSensorSession: Bool = false
-
-    private var promise: Future<[BloodGlucose], Error>.Promise?
-
-    init(glucoseStorage: GlucoseStorage, glucoseManager: FetchGlucoseManager) {
-        self.glucoseStorage = glucoseStorage
-        self.glucoseManager = glucoseManager
-        cgmManager = G5CGMManager
-            .init(state: TransmitterManagerState(
-                transmitterID: UserDefaults.standard
-                    .dexcomTransmitterID ?? "000000",
-                shouldSyncToRemoteService: glucoseManager.settingsManager.settings.uploadGlucose
-            ))
-        cgmManager?.cgmManagerDelegate = self
-    }
-
-    var transmitterID: String {
-        guard let cgmG5Manager = cgmManager as? G5CGMManager else { return "000000" }
-        return cgmG5Manager.transmitter.ID
-    }
-
-    func fetch(_: DispatchTimer?) -> AnyPublisher<[BloodGlucose], Never> {
-        Future<[BloodGlucose], Error> { [weak self] promise in
-            self?.promise = promise
-        }
-        .timeout(60 * 5, scheduler: processQueue, options: nil, customError: nil)
-        .replaceError(with: [])
-        .replaceEmpty(with: [])
-        .eraseToAnyPublisher()
-    }
-
-    func fetchIfNeeded() -> AnyPublisher<[BloodGlucose], Never> {
-        Future<[BloodGlucose], Error> { _ in
-            self.processQueue.async {
-                guard let cgmManager = self.cgmManager else { return }
-                cgmManager.fetchNewDataIfNeeded { result in
-                    self.processCGMReadingResult(cgmManager, readingResult: result) {
-                        // nothing to do
-                    }
-                }
-            }
-        }
-        .timeout(60, scheduler: processQueue, options: nil, customError: nil)
-        .replaceError(with: [])
-        .replaceEmpty(with: [])
-        .eraseToAnyPublisher()
-    }
-
-    deinit {
-        // dexcomManager.transmitter.stopScanning()
-    }
-}
-
-extension DexcomSourceG5: CGMManagerDelegate {
-    func deviceManager(
-        _: LoopKit.DeviceManager,
-        logEventForDeviceIdentifier deviceIdentifier: String?,
-        type _: LoopKit.DeviceLogEntryType,
-        message: String,
-        completion _: ((Error?) -> Void)?
-    ) {
-        debug(.deviceManager, "device Manager for \(String(describing: deviceIdentifier)) : \(message)")
-    }
-
-    func issueAlert(_: LoopKit.Alert) {}
-
-    func retractAlert(identifier _: LoopKit.Alert.Identifier) {}
-
-    func doesIssuedAlertExist(identifier _: LoopKit.Alert.Identifier, completion _: @escaping (Result<Bool, Error>) -> Void) {}
-
-    func lookupAllUnretracted(
-        managerIdentifier _: String,
-        completion _: @escaping (Result<[LoopKit.PersistedAlert], Error>) -> Void
-    ) {}
-
-    func lookupAllUnacknowledgedUnretracted(
-        managerIdentifier _: String,
-        completion _: @escaping (Result<[LoopKit.PersistedAlert], Error>) -> Void
-    ) {}
-
-    func recordRetractedAlert(_: LoopKit.Alert, at _: Date) {}
-
-    func cgmManagerWantsDeletion(_ manager: CGMManager) {
-        dispatchPrecondition(condition: .onQueue(.main))
-        debug(.deviceManager, " CGM Manager with identifier \(manager.managerIdentifier) wants deletion")
-        glucoseManager?.cgmGlucoseSourceType = nil
-    }
-
-    func cgmManager(_ manager: CGMManager, hasNew readingResult: CGMReadingResult) {
-        dispatchPrecondition(condition: .onQueue(.main))
-        processCGMReadingResult(manager, readingResult: readingResult) {
-            debug(.deviceManager, "DEXCOM - Direct return done")
-        }
-    }
-
-    func startDateToFilterNewData(for _: CGMManager) -> Date? {
-        dispatchPrecondition(condition: .onQueue(.main))
-        return glucoseStorage.lastGlucoseDate()
-        //  return glucoseStore.latestGlucose?.startDate
-    }
-
-    func cgmManagerDidUpdateState(_ manager: CGMManager) {
-        dispatchPrecondition(condition: .onQueue(processQueue))
-        guard let g5Manager = manager as? TransmitterManager else {
-            return
-        }
-        glucoseManager?.settingsManager.settings.uploadGlucose = g5Manager.shouldSyncToRemoteService
-        UserDefaults.standard.dexcomTransmitterID = g5Manager.rawState["transmitterID"] as? String
-    }
-
-    func credentialStoragePrefix(for _: CGMManager) -> String {
-        // return string unique to this instance of the CGMManager
-        UUID().uuidString
-    }
-
-    func cgmManager(_: CGMManager, didUpdate status: CGMManagerStatus) {
-        DispatchQueue.main.async {
-            if self.cgmHasValidSensorSession != status.hasValidSensorSession {
-                self.cgmHasValidSensorSession = status.hasValidSensorSession
-            }
-        }
-    }
-
-    private func processCGMReadingResult(
-        _: CGMManager,
-        readingResult: CGMReadingResult,
-        completion: @escaping () -> Void
-    ) {
-        debug(.deviceManager, "DEXCOM - Process CGM Reading Result launched")
-        switch readingResult {
-        case let .newData(values):
-            let bloodGlucose = values.compactMap { newGlucoseSample -> BloodGlucose? in
-                let quantity = newGlucoseSample.quantity
-                let value = Int(quantity.doubleValue(for: .milligramsPerDeciliter))
-                return BloodGlucose(
-                    _id: UUID().uuidString,
-                    sgv: value,
-                    direction: .init(trendType: newGlucoseSample.trend),
-                    date: Decimal(Int(newGlucoseSample.date.timeIntervalSince1970 * 1000)),
-                    dateString: newGlucoseSample.date,
-                    unfiltered: Decimal(value),
-                    filtered: nil,
-                    noise: nil,
-                    glucose: value,
-                    type: "sgv",
-                    transmitterID: self.transmitterID
-                )
-            }
-            promise?(.success(bloodGlucose))
-            completion()
-        case .unreliableData:
-            // loopManager.receivedUnreliableCGMReading()
-            promise?(.failure(GlucoseDataError.unreliableData))
-            completion()
-        case .noData:
-            promise?(.failure(GlucoseDataError.noData))
-            completion()
-        case let .error(error):
-            promise?(.failure(error))
-            completion()
-        }
-    }
-}
-
-extension DexcomSourceG5 {
-    func sourceInfo() -> [String: Any]? {
-        [GlucoseSourceKey.description.rawValue: "Dexcom tramsmitter ID: \(transmitterID)"]
-    }
-}

+ 0 - 195
Trio/Sources/APS/CGM/DexcomSourceG6.swift

@@ -1,195 +0,0 @@
-import CGMBLEKit
-import Combine
-import Foundation
-import LoopKit
-import LoopKitUI
-import ShareClient
-
-final class DexcomSourceG6: GlucoseSource {
-    private let processQueue = DispatchQueue(label: "DexcomSource.processQueue")
-    private let glucoseStorage: GlucoseStorage!
-    var glucoseManager: FetchGlucoseManager?
-
-    var cgmManager: CGMManagerUI?
-    var cgmType: CGMType = .dexcomG6
-
-    var cgmHasValidSensorSession: Bool = false
-
-    private var promise: Future<[BloodGlucose], Error>.Promise?
-
-    init(glucoseStorage: GlucoseStorage, glucoseManager: FetchGlucoseManager) {
-        self.glucoseStorage = glucoseStorage
-        self.glucoseManager = glucoseManager
-        cgmManager = G6CGMManager
-            .init(state: TransmitterManagerState(
-                transmitterID: UserDefaults.standard
-                    .dexcomTransmitterID ?? "000000",
-                shouldSyncToRemoteService: glucoseManager.settingsManager.settings.uploadGlucose
-            ))
-        cgmManager?.delegateQueue = processQueue
-        cgmManager?.cgmManagerDelegate = self
-    }
-
-    var transmitterID: String {
-        guard let cgmG6Manager = cgmManager as? G6CGMManager else { return "000000" }
-        return cgmG6Manager.transmitter.ID
-    }
-
-    func fetch(_: DispatchTimer?) -> AnyPublisher<[BloodGlucose], Never> {
-        Future<[BloodGlucose], Error> { [weak self] promise in
-            self?.promise = promise
-        }
-        .timeout(60 * 5, scheduler: processQueue, options: nil, customError: nil)
-        .replaceError(with: [])
-        .replaceEmpty(with: [])
-        .eraseToAnyPublisher()
-    }
-
-    func fetchIfNeeded() -> AnyPublisher<[BloodGlucose], Never> {
-        Future<[BloodGlucose], Error> { _ in
-            self.processQueue.async {
-                guard let cgmManager = self.cgmManager else { return }
-                cgmManager.fetchNewDataIfNeeded { result in
-                    self.processCGMReadingResult(cgmManager, readingResult: result) {
-                        // nothing to do
-                    }
-                }
-            }
-        }
-        .timeout(60, scheduler: processQueue, options: nil, customError: nil)
-        .replaceError(with: [])
-        .replaceEmpty(with: [])
-        .eraseToAnyPublisher()
-    }
-
-    deinit {
-        // dexcomManager.transmitter.stopScanning()
-    }
-}
-
-extension DexcomSourceG6: CGMManagerDelegate {
-    func deviceManager(
-        _: LoopKit.DeviceManager,
-        logEventForDeviceIdentifier deviceIdentifier: String?,
-        type _: LoopKit.DeviceLogEntryType,
-        message: String,
-        completion _: ((Error?) -> Void)?
-    ) {
-        debug(.deviceManager, "device Manager for \(String(describing: deviceIdentifier)) : \(message)")
-    }
-
-    func issueAlert(_: LoopKit.Alert) {}
-
-    func retractAlert(identifier _: LoopKit.Alert.Identifier) {}
-
-    func doesIssuedAlertExist(identifier _: LoopKit.Alert.Identifier, completion _: @escaping (Result<Bool, Error>) -> Void) {}
-
-    func lookupAllUnretracted(
-        managerIdentifier _: String,
-        completion _: @escaping (Result<[LoopKit.PersistedAlert], Error>) -> Void
-    ) {}
-
-    func lookupAllUnacknowledgedUnretracted(
-        managerIdentifier _: String,
-        completion _: @escaping (Result<[LoopKit.PersistedAlert], Error>) -> Void
-    ) {}
-
-    func recordRetractedAlert(_: LoopKit.Alert, at _: Date) {}
-
-    func cgmManagerWantsDeletion(_ manager: CGMManager) {
-        dispatchPrecondition(condition: .onQueue(processQueue))
-        debug(.deviceManager, " CGM Manager with identifier \(manager.managerIdentifier) wants deletion")
-        glucoseManager?.cgmGlucoseSourceType = nil
-    }
-
-    func cgmManager(_ manager: CGMManager, hasNew readingResult: CGMReadingResult) {
-        dispatchPrecondition(condition: .onQueue(processQueue))
-        processCGMReadingResult(manager, readingResult: readingResult) {
-            debug(.deviceManager, "DEXCOM - Direct return done")
-        }
-    }
-
-    func startDateToFilterNewData(for _: CGMManager) -> Date? {
-        dispatchPrecondition(condition: .onQueue(processQueue))
-        return glucoseStorage.lastGlucoseDate()
-        //  return glucoseStore.latestGlucose?.startDate
-    }
-
-    func cgmManagerDidUpdateState(_ manager: CGMManager) {
-        dispatchPrecondition(condition: .onQueue(processQueue))
-        guard let g6Manager = manager as? TransmitterManager else {
-            return
-        }
-        glucoseManager?.settingsManager.settings.uploadGlucose = g6Manager.shouldSyncToRemoteService
-        UserDefaults.standard.dexcomTransmitterID = g6Manager.rawState["transmitterID"] as? String
-    }
-
-    func credentialStoragePrefix(for _: CGMManager) -> String {
-        // return string unique to this instance of the CGMManager
-        UUID().uuidString
-    }
-
-    func cgmManager(_: CGMManager, didUpdate status: CGMManagerStatus) {
-        processQueue.async {
-            if self.cgmHasValidSensorSession != status.hasValidSensorSession {
-                self.cgmHasValidSensorSession = status.hasValidSensorSession
-            }
-        }
-    }
-
-    private func processCGMReadingResult(
-        _: CGMManager,
-        readingResult: CGMReadingResult,
-        completion: @escaping () -> Void
-    ) {
-        debug(.deviceManager, "DEXCOM - Process CGM Reading Result launched with \(readingResult)")
-        switch readingResult {
-        case let .newData(values):
-            if let cgmG6Manager = cgmManager as? G6CGMManager,
-               let activationDate = cgmG6Manager.latestReading?.activationDate,
-               let sessionStartDate = cgmG6Manager.latestReading?.sessionStartDate
-            {
-                let bloodGlucose = values.compactMap { newGlucoseSample -> BloodGlucose? in
-                    let quantity = newGlucoseSample.quantity
-                    let value = Int(quantity.doubleValue(for: .milligramsPerDeciliter))
-                    return BloodGlucose(
-                        _id: UUID().uuidString,
-                        sgv: value,
-                        direction: .init(trendType: newGlucoseSample.trend),
-                        date: Decimal(Int(newGlucoseSample.date.timeIntervalSince1970 * 1000)),
-                        dateString: newGlucoseSample.date,
-                        unfiltered: Decimal(value),
-                        filtered: nil,
-                        noise: nil,
-                        glucose: value,
-                        type: "sgv",
-                        activationDate: activationDate,
-                        sessionStartDate: sessionStartDate,
-                        transmitterID: self.transmitterID
-                    )
-                }
-                promise?(.success(bloodGlucose))
-                completion()
-            } else {
-                // Handle the case where activationDate or sessionStartDate is nil
-                completion()
-            }
-        case .unreliableData:
-            // loopManager.receivedUnreliableCGMReading()
-            promise?(.failure(GlucoseDataError.unreliableData))
-            completion()
-        case .noData:
-            promise?(.failure(GlucoseDataError.noData))
-            completion()
-        case let .error(error):
-            promise?(.failure(error))
-            completion()
-        }
-    }
-}
-
-extension DexcomSourceG6 {
-    func sourceInfo() -> [String: Any]? {
-        [GlucoseSourceKey.description.rawValue: "Dexcom tramsmitter ID: \(transmitterID)"]
-    }
-}

+ 0 - 103
Trio/Sources/APS/CGM/LibreTransmitterSource.swift

@@ -1,103 +0,0 @@
-import Combine
-import Foundation
-import LibreTransmitter
-import LoopKitUI
-import Swinject
-
-protocol LibreTransmitterSource: GlucoseSource {
-    var manager: LibreTransmitterManager? { get set }
-}
-
-final class BaseLibreTransmitterSource: LibreTransmitterSource, Injectable {
-    var cgmManager: CGMManagerUI?
-    var cgmType: CGMType = .libreTransmitter
-
-    private let processQueue = DispatchQueue(label: "BaseLibreTransmitterSource.processQueue")
-
-    @Injected() var glucoseStorage: GlucoseStorage!
-    @Injected() var calibrationService: CalibrationService!
-
-    private var promise: Future<[BloodGlucose], Error>.Promise?
-
-    var glucoseManager: FetchGlucoseManager?
-
-    var manager: LibreTransmitterManager? {
-        didSet {
-            configured = manager != nil
-            manager?.cgmManagerDelegate = self
-        }
-    }
-
-    @Persisted(key: "LibreTransmitterManager.configured") private(set) var configured = false
-
-    init(resolver: Resolver) {
-        if configured {
-            manager = LibreTransmitterManager()
-            manager?.cgmManagerDelegate = self
-        }
-        injectServices(resolver)
-    }
-
-    func fetch(_: DispatchTimer?) -> AnyPublisher<[BloodGlucose], Never> {
-        Future<[BloodGlucose], Error> { [weak self] promise in
-            self?.promise = promise
-        }
-        .timeout(60, scheduler: processQueue, options: nil, customError: nil)
-        .replaceError(with: [])
-        .replaceEmpty(with: [])
-        .eraseToAnyPublisher()
-    }
-
-    func fetchIfNeeded() -> AnyPublisher<[BloodGlucose], Never> {
-        fetch(nil)
-    }
-
-    func sourceInfo() -> [String: Any]? {
-        if let battery = manager?.battery {
-            return ["transmitterBattery": battery]
-        }
-        return nil
-    }
-}
-
-extension BaseLibreTransmitterSource: LibreTransmitterManagerDelegate {
-    var queue: DispatchQueue { processQueue }
-
-    func startDateToFilterNewData(for _: LibreTransmitterManager) -> Date? {
-        glucoseStorage.syncDate()
-    }
-
-    func cgmManager(_ manager: LibreTransmitterManager, hasNew result: Result<[LibreGlucose], Error>) {
-        switch result {
-        case let .success(newGlucose):
-            let glucose = newGlucose.map { value -> BloodGlucose in
-                BloodGlucose(
-                    _id: UUID().uuidString,
-                    sgv: Int(value.glucose),
-                    direction: manager.glucoseDisplay?.trendType
-                        .map { .init(trendType: $0) },
-                    date: Decimal(Int(value.startDate.timeIntervalSince1970 * 1000)),
-                    dateString: value.startDate,
-                    unfiltered: Decimal(value.unsmoothedGlucose),
-                    filtered: nil,
-                    noise: nil,
-                    glucose: Int(value.glucose),
-                    type: "sgv",
-                    activationDate: value.sensorStartDate ?? manager.sensorStartDate,
-                    sessionStartDate: value.sensorStartDate ?? manager.sensorStartDate,
-                    transmitterID: manager.sensorSerialNumber
-                )
-            }
-            NSLog("Debug Libre \(glucose)")
-            promise?(.success(glucose))
-
-        case let .failure(error):
-            warning(.service, "LibreTransmitter error:", error: error)
-            promise?(.failure(error))
-        }
-    }
-
-    func overcalibration(for _: LibreTransmitterManager) -> ((Double) -> (Double))? {
-        calibrationService.calibrate
-    }
-}

+ 2 - 10
Trio/Sources/APS/CGM/PluginSource.swift

@@ -144,17 +144,9 @@ extension PluginSource: CGMManagerDelegate {
     }
 
     func startDateToFilterNewData(for _: CGMManager) -> Date? {
-        var date: Date?
+        dispatchPrecondition(condition: .onQueue(processQueue))
 
-        processQueue.async { [weak self] in
-            guard let self = self else { return }
-
-            dispatchPrecondition(condition: .onQueue(self.processQueue))
-
-            date = glucoseStorage.lastGlucoseDate()
-        }
-
-        return date
+        return glucoseStorage.lastGlucoseDate()
     }
 
     func cgmManagerDidUpdateState(_ cgmManager: CGMManager) {

+ 9 - 9
Trio/Sources/APS/Storage/GlucoseStorage.swift

@@ -15,7 +15,7 @@ protocol GlucoseStorage {
     func isGlucoseDataFresh(_ glucoseDate: Date?) -> Bool
     func syncDate() -> Date
     func filterTooFrequentGlucose(_ glucose: [BloodGlucose], at: Date) -> [BloodGlucose]
-    func lastGlucoseDate() -> Date
+    func lastGlucoseDate() -> Date?
     func isGlucoseFresh() -> Bool
     func getGlucoseNotYetUploadedToNightscout() async throws -> [BloodGlucose]
     func getCGMStateNotYetUploadedToNightscout() async throws -> [NightscoutTreatment]
@@ -343,27 +343,27 @@ final class BaseGlucoseStorage: GlucoseStorage, Injectable {
         return fetchedDate
     }
 
-    func lastGlucoseDate() -> Date {
-        let fr = GlucoseStored.fetchRequest()
-        fr.predicate = NSPredicate.predicateForOneDayAgo
-        fr.sortDescriptors = [NSSortDescriptor(keyPath: \GlucoseStored.date, ascending: false)]
-        fr.fetchLimit = 1
+    func lastGlucoseDate() -> Date? {
+        let fetchRequest = GlucoseStored.fetchRequest()
+        fetchRequest.predicate = NSPredicate.predicateForOneDayAgo
+        fetchRequest.sortDescriptors = [NSSortDescriptor(keyPath: \GlucoseStored.date, ascending: false)]
+        fetchRequest.fetchLimit = 1
 
         var date: Date?
         context.performAndWait {
             do {
-                let results = try self.context.fetch(fr)
+                let results = try self.context.fetch(fetchRequest)
                 date = results.first?.date
             } catch let error as NSError {
                 debug(.storage, "Fetch error: \(DebuggingIdentifiers.failed) \(error), \(error.userInfo)")
             }
         }
 
-        return date ?? .distantPast
+        return date
     }
 
     func isGlucoseFresh() -> Bool {
-        Date().timeIntervalSince(lastGlucoseDate()) <= Config.filterTime
+        Date().timeIntervalSince(lastGlucoseDate() ?? .distantPast) <= Config.filterTime
     }
 
     func filterTooFrequentGlucose(_ glucose: [BloodGlucose], at date: Date) -> [BloodGlucose] {