Преглед изворни кода

Release/0.1.16 (#34)

* Fix default middleware path

* Fix basal rendering

* Additional bolus check

* Check is BG not too flat

* fix debug message

* Insulin confirm alert

* skipBolusScreenAfterCarbs option

* fix basal heingt

* fix glucose text alignment

* Bump version
Ivan пре 5 година
родитељ
комит
01af41d98e

+ 7 - 0
FreeAPS.xcodeproj/xcshareddata/xcschemes/FreeAPS X.xcscheme

@@ -298,6 +298,13 @@
             ReferencedContainer = "container:FreeAPS.xcodeproj">
          </BuildableReference>
       </BuildableProductRunnable>
+      <EnvironmentVariables>
+         <EnvironmentVariable
+            key = "CG_NUMERICS_SHOW_BACKTRACE"
+            value = ""
+            isEnabled = "YES">
+         </EnvironmentVariable>
+      </EnvironmentVariables>
    </LaunchAction>
    <ProfileAction
       buildConfiguration = "Release"

+ 1 - 1
FreeAPS/Resources/Config.xcconfig

@@ -1 +1 @@
-BUILD_VERSION = 0.1.15
+BUILD_VERSION = 0.1.16

+ 2 - 1
FreeAPS/Resources/json/defaults/freeaps/freeaps_settings.json

@@ -7,5 +7,6 @@
     "useLocalGlucoseSource": false,
     "localGlucosePort": 8080,
     "debugOptions": false,
-    "insulinReqFraction": 0.7
+    "insulinReqFraction": 0.7,
+    "skipBolusScreenAfterCarbs": false
 }

+ 14 - 3
FreeAPS/Sources/APS/APSManager.swift

@@ -20,6 +20,7 @@ protocol APSManager {
     func enactTempBasal(rate: Double, duration: TimeInterval)
     func makeProfiles() -> AnyPublisher<Bool, Never>
     func determineBasal() -> AnyPublisher<Bool, Never>
+    func determineBasalSync()
     func roundBolus(amount: Decimal) -> Decimal
     var lastError: CurrentValueSubject<Error?, Never> { get }
     func cancelBolus()
@@ -225,6 +226,12 @@ final class BaseAPSManager: APSManager, Injectable {
             return Just(false).eraseToAnyPublisher()
         }
 
+        guard glucoseStorage.isGlucoseNotFlat() else {
+            debug(.apsManager, "Glucose data is too flat")
+            processError(APSError.glucoseError(message: "Glucose data is too flat"))
+            return Just(false).eraseToAnyPublisher()
+        }
+
         let now = Date()
         let temp = currentTemp(date: now)
 
@@ -260,6 +267,10 @@ final class BaseAPSManager: APSManager, Injectable {
         return mainPublisher
     }
 
+    func determineBasalSync() {
+        determineBasal().sink { _ in }.store(in: &lifetime)
+    }
+
     func makeProfiles() -> AnyPublisher<Bool, Never> {
         openAPS.makeProfiles(useAutotune: settings.useAutotune)
             .map { tunedProfile in
@@ -284,7 +295,7 @@ final class BaseAPSManager: APSManager, Injectable {
     private var bolusReporter: DoseProgressReporter?
 
     func enactBolus(amount: Double, isSMB: Bool) {
-        guard let pump = pumpManager, verifyStatus() else { return }
+        guard let pump = pumpManager, verifyStatus(), bolusReporter == nil else { return }
 
         let roundedAmout = pump.roundToSupportedBolusVolume(units: amount)
 
@@ -460,10 +471,10 @@ final class BaseAPSManager: APSManager, Injectable {
             return
         }
 
-        guard let pump = pumpManager, verifyStatus() else {
+        guard let pump = pumpManager, verifyStatus(), bolusReporter == nil else {
             isLooping.send(false)
             debug(.apsManager, "Invalid pump state")
-            processError(APSError.invalidPumpState(message: "Pump is busy, suspended or not set"))
+            processError(APSError.invalidPumpState(message: "Pump is bolusing, suspended or not set"))
             return
         }
 

+ 1 - 1
FreeAPS/Sources/APS/OpenAPS/OpenAPS.swift

@@ -398,7 +398,7 @@ final class OpenAPS {
             return Script(name: "Middleware", body: body)
         }
 
-        if let url = Foundation.Bundle.main.url(forResource: "js/\(name)", withExtension: "") {
+        if let url = Foundation.Bundle.main.url(forResource: "javascript/\(name)", withExtension: "") {
             return Script(name: "Middleware", body: try! String(contentsOf: url))
         }
 

+ 8 - 0
FreeAPS/Sources/APS/Storage/GlucoseStorage.swift

@@ -9,6 +9,7 @@ protocol GlucoseStorage {
     func filterTooFrequentGlucose(_ glucose: [BloodGlucose], at: Date) -> [BloodGlucose]
     func lastGlucoseDate() -> Date
     func isGlucoseFresh() -> Bool
+    func isGlucoseNotFlat() -> Bool
 }
 
 final class BaseGlucoseStorage: GlucoseStorage, Injectable {
@@ -79,6 +80,13 @@ final class BaseGlucoseStorage: GlucoseStorage, Injectable {
 
         return filtered
     }
+
+    func isGlucoseNotFlat() -> Bool {
+        let last3 = recent().suffix(3)
+        guard last3.count == 3 else { return true }
+
+        return Array(last3.compactMap { $0.filtered ?? Decimal($0.sgv ?? 0) }.uniqued()).count > 1
+    }
 }
 
 protocol GlucoseObserver {

+ 1 - 0
FreeAPS/Sources/Models/FreeAPSSettings.swift

@@ -10,4 +10,5 @@ struct FreeAPSSettings: JSON {
     var localGlucosePort: Int?
     var debugOptions: Bool?
     var insulinReqFraction: Decimal?
+    var skipBolusScreenAfterCarbs: Bool?
 }

+ 8 - 1
FreeAPS/Sources/Modules/AddCarbs/AddCarbsViewModel.swift

@@ -3,6 +3,7 @@ import SwiftUI
 extension AddCarbs {
     class ViewModel<Provider>: BaseViewModel<Provider>, ObservableObject where Provider: AddCarbsProvider {
         @Injected() var carbsStorage: CarbsStorage!
+        @Injected() var settingsManager: SettingsManager!
         @Injected() var apsManager: APSManager!
         @Published var carbs: Decimal = 0
         @Published var date = Date()
@@ -18,7 +19,13 @@ extension AddCarbs {
             carbsStorage.storeCarbs([
                 CarbsEntry(createdAt: date, carbs: carbs, enteredBy: CarbsEntry.manual)
             ])
-            showModal(for: .bolus(waitForDuggestion: true))
+
+            if settingsManager.settings.skipBolusScreenAfterCarbs ?? false {
+                apsManager.determineBasalSync()
+                showModal(for: nil)
+            } else {
+                showModal(for: .bolus(waitForDuggestion: true))
+            }
         }
     }
 }

+ 1 - 0
FreeAPS/Sources/Modules/AddCarbs/View/AddCarbsRootView.swift

@@ -26,6 +26,7 @@ extension AddCarbs {
                 Section {
                     Button { viewModel.add() }
                     label: { Text("Add") }
+                        .disabled(viewModel.carbs <= 0)
                 }
             }
             .navigationTitle("Add Carbs")

+ 21 - 2
FreeAPS/Sources/Modules/Bolus/View/BolusRootView.swift

@@ -3,6 +3,7 @@ import SwiftUI
 extension Bolus {
     struct RootView: BaseView {
         @EnvironmentObject var viewModel: ViewModel<Provider>
+        @State private var isAddInsulinAlertPresented = false
 
         private var formatter: NumberFormatter {
             let formatter = NumberFormatter()
@@ -59,17 +60,35 @@ extension Bolus {
                     Section {
                         Button { viewModel.add() }
                         label: { Text("Enact bolus") }
+                            .disabled(viewModel.amount <= 0)
 
                         if viewModel.waitForSuggestionInitial {
                             Button { viewModel.showModal(for: nil) }
                             label: { Text("Continue without bolus") }
-                        } else {
-                            Button { viewModel.addWithoutBolus() }
+                        }
+                    }
+
+                    Section {
+                        if !viewModel.waitForSuggestionInitial {
+                            Button { isAddInsulinAlertPresented = true }
                             label: { Text("Add insulin without actually bolusing") }
+                                .disabled(viewModel.amount <= 0)
                         }
                     }
                 }
             }
+            .alert(isPresented: $isAddInsulinAlertPresented) {
+                let amount = formatter.string(from: viewModel.amount as NSNumber)! + " U"
+                return Alert(
+                    title: Text("Are your sure?"),
+                    message: Text("Add \(amount) without bolusing"),
+                    primaryButton: .destructive(
+                        Text("Add"),
+                        action: { viewModel.addWithoutBolus() }
+                    ),
+                    secondaryButton: .cancel()
+                )
+            }
             .navigationTitle("Enact Bolus")
             .navigationBarTitleDisplayMode(.automatic)
             .navigationBarItems(leading: Button("Close", action: viewModel.hideModal))

+ 14 - 3
FreeAPS/Sources/Modules/Home/View/Chart/MainChartView.swift

@@ -20,7 +20,7 @@ struct MainChartView: View {
     private enum Config {
         static let endID = "End"
         static let screenHours = 5
-        static let basalHeight: CGFloat = 60
+        static let basalHeight: CGFloat = 70
         static let topYPadding: CGFloat = 20
         static let bottomYPadding: CGFloat = 50
         static let minAdditionalWidth: CGFloat = 150
@@ -28,7 +28,7 @@ struct MainChartView: View {
         static let minGlucose = 70
         static let yLinesCount = 5
         static let bolusSize: CGFloat = 8
-        static let bolusScale: CGFloat = 3
+        static let bolusScale: CGFloat = 2.5
         static let carbsSize: CGFloat = 10
         static let carbsScale: CGFloat = 0.3
     }
@@ -572,7 +572,18 @@ extension MainChartView {
         if let cached = cachedMaxBasalRate {
             return cached
         }
-        cachedMaxBasalRate = tempBasals.compactMap(\.rate).max() ?? maxBasal
+
+        let maxRegularBasalRate = max(
+            basalProfile.map(\.rate).max() ?? maxBasal,
+            autotunedBasalProfile.map(\.rate).max() ?? maxBasal
+        )
+
+        var maxTempBasalRate = tempBasals.compactMap(\.rate).max() ?? maxRegularBasalRate
+        if maxTempBasalRate == 0 {
+            maxTempBasalRate = maxRegularBasalRate
+        }
+
+        cachedMaxBasalRate = max(maxTempBasalRate, maxRegularBasalRate)
         return cachedMaxBasalRate!
     }
 

+ 1 - 1
FreeAPS/Sources/Modules/Home/View/Header/CurrentGlucoseView.swift

@@ -45,7 +45,7 @@ struct CurrentGlucoseView: View {
                 image.padding(.bottom, 2)
 
             }.padding(.leading, 4)
-            HStack(spacing: 2) {
+            HStack(alignment: .lastTextBaseline, spacing: 2) {
                 Text(
                     recentGlucose.map { dateFormatter.string(from: $0.dateString) } ?? "--"
                 ).font(.caption2).foregroundColor(.secondary)

+ 9 - 0
FreeAPS/Sources/Modules/PreferencesEditor/PreferencesEditorViewModel.swift

@@ -10,6 +10,7 @@ extension PreferencesEditor {
         @Published var unitsIndex = 1
         @Published var allowAnnouncements = false
         @Published var insulinReqFraction: Decimal = 0.7
+        @Published var skipBolusScreenAfterCarbs = false
 
         @Published var decimalFields: [Field<Decimal>] = []
         @Published var boolFields: [Field<Bool>] = []
@@ -26,6 +27,7 @@ extension PreferencesEditor {
             insulinCurveField.value = preferences.curve
             insulinCurveField.settable = self
             insulinReqFraction = settingsManager.settings.insulinReqFraction ?? 0.7
+            skipBolusScreenAfterCarbs = settingsManager.settings.skipBolusScreenAfterCarbs ?? false
 
             $unitsIndex
                 .removeDuplicates()
@@ -48,6 +50,13 @@ extension PreferencesEditor {
                 }
                 .store(in: &lifetime)
 
+            $skipBolusScreenAfterCarbs
+                .removeDuplicates()
+                .sink { [weak self] skip in
+                    self?.settingsManager.settings.skipBolusScreenAfterCarbs = skip
+                }
+                .store(in: &lifetime)
+
             boolFields = [
                 Field(
                     displayName: "Rewind Resets Autosens",

+ 2 - 0
FreeAPS/Sources/Modules/PreferencesEditor/View/PreferencesEditorRootView.swift

@@ -24,6 +24,8 @@ extension PreferencesEditor {
                         Text("Recommended Insulin Fraction")
                         DecimalTextField("", value: $viewModel.insulinReqFraction, formatter: formatter)
                     }
+
+                    Toggle("Skip Bolus screen after carbs", isOn: $viewModel.skipBolusScreenAfterCarbs)
                 }
 
                 Section(header: Text("OpenAPS")) {