瀏覽代碼

Loop sync with Glucose

Ivan Valkou 5 年之前
父節點
當前提交
38552a7209

+ 4 - 0
FreeAPS.xcodeproj/project.pbxproj

@@ -183,6 +183,7 @@
 		38D0B3B625EBE24900CB6E88 /* Battery.swift in Sources */ = {isa = PBXBuildFile; fileRef = 38D0B3B525EBE24900CB6E88 /* Battery.swift */; };
 		38D0B3B625EBE24900CB6E88 /* Battery.swift in Sources */ = {isa = PBXBuildFile; fileRef = 38D0B3B525EBE24900CB6E88 /* Battery.swift */; };
 		38D0B3D925EC07C400CB6E88 /* CarbsEntry.swift in Sources */ = {isa = PBXBuildFile; fileRef = 38D0B3D825EC07C400CB6E88 /* CarbsEntry.swift */; };
 		38D0B3D925EC07C400CB6E88 /* CarbsEntry.swift in Sources */ = {isa = PBXBuildFile; fileRef = 38D0B3D825EC07C400CB6E88 /* CarbsEntry.swift */; };
 		38DAB280260CBB7F00F74C1A /* PumpView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 38DAB27F260CBB7F00F74C1A /* PumpView.swift */; };
 		38DAB280260CBB7F00F74C1A /* PumpView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 38DAB27F260CBB7F00F74C1A /* PumpView.swift */; };
+		38DAB28A260D349500F74C1A /* GlucoseManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 38DAB289260D349500F74C1A /* GlucoseManager.swift */; };
 		38E989DD25F5021400C0CED0 /* PumpStatus.swift in Sources */ = {isa = PBXBuildFile; fileRef = 38E989DC25F5021400C0CED0 /* PumpStatus.swift */; };
 		38E989DD25F5021400C0CED0 /* PumpStatus.swift in Sources */ = {isa = PBXBuildFile; fileRef = 38E989DC25F5021400C0CED0 /* PumpStatus.swift */; };
 		38E98A2325F52C9300C0CED0 /* Signpost.swift in Sources */ = {isa = PBXBuildFile; fileRef = 38E98A1B25F52C9300C0CED0 /* Signpost.swift */; };
 		38E98A2325F52C9300C0CED0 /* Signpost.swift in Sources */ = {isa = PBXBuildFile; fileRef = 38E98A1B25F52C9300C0CED0 /* Signpost.swift */; };
 		38E98A2425F52C9300C0CED0 /* Logger.swift in Sources */ = {isa = PBXBuildFile; fileRef = 38E98A1C25F52C9300C0CED0 /* Logger.swift */; };
 		38E98A2425F52C9300C0CED0 /* Logger.swift in Sources */ = {isa = PBXBuildFile; fileRef = 38E98A1C25F52C9300C0CED0 /* Logger.swift */; };
@@ -456,6 +457,7 @@
 		38D0B3B525EBE24900CB6E88 /* Battery.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Battery.swift; sourceTree = "<group>"; };
 		38D0B3B525EBE24900CB6E88 /* Battery.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Battery.swift; sourceTree = "<group>"; };
 		38D0B3D825EC07C400CB6E88 /* CarbsEntry.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CarbsEntry.swift; sourceTree = "<group>"; };
 		38D0B3D825EC07C400CB6E88 /* CarbsEntry.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CarbsEntry.swift; sourceTree = "<group>"; };
 		38DAB27F260CBB7F00F74C1A /* PumpView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PumpView.swift; sourceTree = "<group>"; };
 		38DAB27F260CBB7F00F74C1A /* PumpView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PumpView.swift; sourceTree = "<group>"; };
+		38DAB289260D349500F74C1A /* GlucoseManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GlucoseManager.swift; sourceTree = "<group>"; };
 		38E989DC25F5021400C0CED0 /* PumpStatus.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PumpStatus.swift; sourceTree = "<group>"; };
 		38E989DC25F5021400C0CED0 /* PumpStatus.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PumpStatus.swift; sourceTree = "<group>"; };
 		38E98A1B25F52C9300C0CED0 /* Signpost.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Signpost.swift; sourceTree = "<group>"; };
 		38E98A1B25F52C9300C0CED0 /* Signpost.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Signpost.swift; sourceTree = "<group>"; };
 		38E98A1C25F52C9300C0CED0 /* Logger.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Logger.swift; sourceTree = "<group>"; };
 		38E98A1C25F52C9300C0CED0 /* Logger.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Logger.swift; sourceTree = "<group>"; };
@@ -934,6 +936,7 @@
 			children = (
 			children = (
 				3811DF0F25CAAAE200A708ED /* APSManager.swift */,
 				3811DF0F25CAAAE200A708ED /* APSManager.swift */,
 				38BF021E25E7F0DE00579895 /* DeviceDataManager.swift */,
 				38BF021E25E7F0DE00579895 /* DeviceDataManager.swift */,
+				38DAB289260D349500F74C1A /* GlucoseManager.swift */,
 				38A504F625DDA0E200C5B9E8 /* Extensions */,
 				38A504F625DDA0E200C5B9E8 /* Extensions */,
 				388E5A5825B6F0070019842D /* OpenAPS */,
 				388E5A5825B6F0070019842D /* OpenAPS */,
 				38A0362725ECF05300FCBB52 /* Storage */,
 				38A0362725ECF05300FCBB52 /* Storage */,
@@ -1671,6 +1674,7 @@
 				38FE826A25CC82DB001FF17A /* NetworkService.swift in Sources */,
 				38FE826A25CC82DB001FF17A /* NetworkService.swift in Sources */,
 				3883581C25EE79BB00E024B2 /* DecimalTextField.swift in Sources */,
 				3883581C25EE79BB00E024B2 /* DecimalTextField.swift in Sources */,
 				3811DE6C25C9D62600A708ED /* OnboardingDataFlow.swift in Sources */,
 				3811DE6C25C9D62600A708ED /* OnboardingDataFlow.swift in Sources */,
+				38DAB28A260D349500F74C1A /* GlucoseManager.swift in Sources */,
 				3811DE2425C9D48300A708ED /* MainViewModel.swift in Sources */,
 				3811DE2425C9D48300A708ED /* MainViewModel.swift in Sources */,
 				3811DE3F25C9D4A100A708ED /* SettingsViewModel.swift in Sources */,
 				3811DE3F25C9D4A100A708ED /* SettingsViewModel.swift in Sources */,
 				38B4F3CB25E502E200E76A18 /* WeakObjectSet.swift in Sources */,
 				38B4F3CB25E502E200E76A18 /* WeakObjectSet.swift in Sources */,

+ 1 - 2
FreeAPS/Sources/APS/APSManager.swift

@@ -104,8 +104,7 @@ final class BaseAPSManager: APSManager, Injectable {
 
 
     private func loop() {
     private func loop() {
         isLooping.send(true)
         isLooping.send(true)
-        Publishers.CombineLatest3(
-            nightscout.fetchGlucose(),
+        Publishers.CombineLatest(
             nightscout.fetchCarbs(),
             nightscout.fetchCarbs(),
             nightscout.fetchTempTargets()
             nightscout.fetchTempTargets()
         )
         )

+ 1 - 11
FreeAPS/Sources/APS/DeviceDataManager.swift

@@ -15,7 +15,7 @@ protocol DeviceDataManager {
     var recommendsLoop: PassthroughSubject<Void, Never> { get }
     var recommendsLoop: PassthroughSubject<Void, Never> { get }
     var pumpName: CurrentValueSubject<String, Never> { get }
     var pumpName: CurrentValueSubject<String, Never> { get }
     var pumpExpiresAtDate: CurrentValueSubject<Date?, Never> { get }
     var pumpExpiresAtDate: CurrentValueSubject<Date?, Never> { get }
-    func heartbeat() 
+    func heartbeat()
 }
 }
 
 
 private let staticPumpManagers: [PumpManagerUI.Type] = [
 private let staticPumpManagers: [PumpManagerUI.Type] = [
@@ -66,20 +66,10 @@ final class BaseDeviceDataManager: DeviceDataManager, Injectable {
     let pumpExpiresAtDate = CurrentValueSubject<Date?, Never>(nil)
     let pumpExpiresAtDate = CurrentValueSubject<Date?, Never>(nil)
     let pumpName = CurrentValueSubject<String, Never>("Pump")
     let pumpName = CurrentValueSubject<String, Never>("Pump")
 
 
-    var heartBeat: Timer!
-
     init(resolver: Resolver) {
     init(resolver: Resolver) {
         injectServices(resolver)
         injectServices(resolver)
         setupPumpManager()
         setupPumpManager()
         UIDevice.current.isBatteryMonitoringEnabled = true
         UIDevice.current.isBatteryMonitoringEnabled = true
-
-        if pumpManager is MockPumpManager {
-            heartBeat = Timer.scheduledTimer(withTimeInterval: 60, repeats: true) { _ in
-                debug(.deviceManager, "Timer Heartbeat")
-                self.updatePumpData()
-            }
-        }
-
         updatePumpData()
         updatePumpData()
     }
     }
 
 

+ 39 - 0
FreeAPS/Sources/APS/GlucoseManager.swift

@@ -0,0 +1,39 @@
+import Combine
+import Foundation
+import SwiftDate
+import Swinject
+
+protocol GlucoseManager {}
+
+final class BaseGlucoseManager: GlucoseManager, Injectable {
+    private let processQueue = DispatchQueue(label: "BaseGlucoseManager.processQueue")
+    @Injected() var glucoseStogare: GlucoseStorage!
+    @Injected() var nightscoutManager: NightscoutManager!
+    @Injected() var apsManager: APSManager!
+
+    private var lifetime = Set<AnyCancellable>()
+    private let timer = Timer.publish(every: 10, on: .main, in: .common).autoconnect()
+
+    init(resolver: Resolver) {
+        injectServices(resolver)
+        subscribe()
+    }
+
+    private func subscribe() {
+        timer
+            .receive(on: processQueue)
+            .flatMap { date -> AnyPublisher<[BloodGlucose], Never> in
+                guard self.glucoseStogare.syncDate().timeIntervalSince1970 + 4.minutes.timeInterval <= date.timeIntervalSince1970
+                else {
+                    return Just([]).eraseToAnyPublisher()
+                }
+                return self.nightscoutManager.fetchGlucose()
+            }
+            .sink { glucose in
+                if !glucose.isEmpty {
+                    self.apsManager.heartbeatNow()
+                }
+            }
+            .store(in: &lifetime)
+    }
+}

+ 1 - 1
FreeAPS/Sources/APS/Storage/GlucoseStorage.swift

@@ -43,7 +43,7 @@ final class BaseGlucoseStorage: GlucoseStorage, Injectable {
         else {
         else {
             return Date().addingTimeInterval(-1.days.timeInterval)
             return Date().addingTimeInterval(-1.days.timeInterval)
         }
         }
-        return recent.dateString.addingTimeInterval(-6.minutes.timeInterval)
+        return recent.dateString.addingTimeInterval(1.minutes.timeInterval)
     }
     }
 
 
     func recent() -> [BloodGlucose] {
     func recent() -> [BloodGlucose] {

+ 1 - 0
FreeAPS/Sources/Application/FreeAPSApp.swift

@@ -29,6 +29,7 @@ private extension Swinject.Resolver {
         resolver.resolve(AppearanceManager.self)!.setupGlobalAppearance()
         resolver.resolve(AppearanceManager.self)!.setupGlobalAppearance()
         _ = resolver.resolve(DeviceDataManager.self)!
         _ = resolver.resolve(DeviceDataManager.self)!
         _ = resolver.resolve(APSManager.self)!
         _ = resolver.resolve(APSManager.self)!
+        _ = resolver.resolve(GlucoseManager.self)!
     }
     }
 
 
     var body: some Scene {
     var body: some Scene {

+ 1 - 0
FreeAPS/Sources/Containers/APSContainer.swift

@@ -7,5 +7,6 @@ enum APSContainer: DependeciesContainer {
     static func register(container: Container) {
     static func register(container: Container) {
         container.register(DeviceDataManager.self) { _ in BaseDeviceDataManager(resolver: resolver) }
         container.register(DeviceDataManager.self) { _ in BaseDeviceDataManager(resolver: resolver) }
         container.register(APSManager.self) { _ in BaseAPSManager(resolver: resolver) }
         container.register(APSManager.self) { _ in BaseAPSManager(resolver: resolver) }
+        container.register(GlucoseManager.self) { _ in BaseGlucoseManager(resolver: resolver) }
     }
     }
 }
 }

+ 1 - 1
FreeAPS/Sources/Modules/Home/HomeViewModel.swift

@@ -53,7 +53,7 @@ extension Home {
             setStatusTitle()
             setStatusTitle()
 
 
             if closedLoop,
             if closedLoop,
-               enactedSuggestion?.deliverAt == suggestion?.deliverAt || (suggestion?.rate == nil && suggestion?.units == nil)
+               enactedSuggestion?.deliverAt == suggestion?.deliverAt, suggestion?.rate != nil || suggestion?.units != nil
             {
             {
                 lastLoopDate = enactedSuggestion?.timestamp ?? .distantPast
                 lastLoopDate = enactedSuggestion?.timestamp ?? .distantPast
             } else {
             } else {

+ 2 - 1
FreeAPS/Sources/Services/Network/NetworkService.swift

@@ -14,7 +14,8 @@ enum NetworkError: Error, LocalizedError {
 
 
 struct NetworkService {
 struct NetworkService {
     func run(_ request: URLRequest) -> AnyPublisher<Data, Error> {
     func run(_ request: URLRequest) -> AnyPublisher<Data, Error> {
-        URLSession.shared
+        debug(.nightscout, "Request at \(request.url!.absoluteString)")
+        return URLSession.shared
             .dataTaskPublisher(for: request)
             .dataTaskPublisher(for: request)
             .tryMap { data, response in
             .tryMap { data, response in
                 let code = (response as! HTTPURLResponse).statusCode
                 let code = (response as! HTTPURLResponse).statusCode

+ 4 - 4
FreeAPS/Sources/Services/Network/NightscoutManager.swift

@@ -4,7 +4,7 @@ import Swinject
 import UIKit
 import UIKit
 
 
 protocol NightscoutManager {
 protocol NightscoutManager {
-    func fetchGlucose() -> AnyPublisher<Void, Never>
+    func fetchGlucose() -> AnyPublisher<[BloodGlucose], Never>
     func fetchCarbs() -> AnyPublisher<Void, Never>
     func fetchCarbs() -> AnyPublisher<Void, Never>
     func fetchTempTargets() -> AnyPublisher<Void, Never>
     func fetchTempTargets() -> AnyPublisher<Void, Never>
     func fetchAnnouncements() -> AnyPublisher<Void, Never>
     func fetchAnnouncements() -> AnyPublisher<Void, Never>
@@ -51,9 +51,9 @@ final class BaseNightscoutManager: NightscoutManager, Injectable {
         broadcaster.register(TempTargetsObserver.self, observer: self)
         broadcaster.register(TempTargetsObserver.self, observer: self)
     }
     }
 
 
-    func fetchGlucose() -> AnyPublisher<Void, Never> {
+    func fetchGlucose() -> AnyPublisher<[BloodGlucose], Never> {
         guard let nightscout = nightscoutAPI else {
         guard let nightscout = nightscoutAPI else {
-            return Just(()).eraseToAnyPublisher()
+            return Just([]).eraseToAnyPublisher()
         }
         }
 
 
         let since = glucoseStorage.syncDate()
         let since = glucoseStorage.syncDate()
@@ -65,7 +65,7 @@ final class BaseNightscoutManager: NightscoutManager, Injectable {
             .replaceError(with: [])
             .replaceError(with: [])
             .map {
             .map {
                 self.glucoseStorage.storeGlucose($0)
                 self.glucoseStorage.storeGlucose($0)
-                return ()
+                return $0
             }
             }
             .eraseToAnyPublisher()
             .eraseToAnyPublisher()
     }
     }