Skip to content

Commit

Permalink
Complete the carbs -> bolus flow before enacting any eventual auto bo…
Browse files Browse the repository at this point in the history
…lus (SMB) or any auto temp basal. (#706)

Prevent SMBs and temp basals (over 0u) while in Bolus View, but If more than 30 minutes since last loop - force close the bolus view
  • Loading branch information
Jon-b-m authored Jun 16, 2024
1 parent f862a5f commit a77d63a
Show file tree
Hide file tree
Showing 9 changed files with 151 additions and 2 deletions.
42 changes: 42 additions & 0 deletions FreeAPS/Sources/APS/APSManager.swift
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,9 @@ enum APSError: LocalizedError {
case apsError(message: String)
case deviceSyncError(message: String)
case manualBasalTemp(message: String)
case activeBolusViewBolus
case activeBolusViewBasal
case activeBolusViewBasalandBolus

var errorDescription: String? {
switch self {
Expand All @@ -60,6 +63,12 @@ enum APSError: LocalizedError {
return "Sync error: \(message)"
case let .manualBasalTemp(message):
return "Manual Basal Temp : \(message)"
case .activeBolusViewBolus:
return "Suggested SMB not enacted while in Bolus View"
case .activeBolusViewBasal:
return "Suggested Temp Basal (when > 0) not enacted while in Bolus View"
case .activeBolusViewBasalandBolus:
return "Suggested Temp Basal (when > 0) and SMB not enacted while in Bolus View"
}
}
}
Expand Down Expand Up @@ -552,6 +561,13 @@ final class BaseAPSManager: APSManager, Injectable {
processError(error)
return
}

guard !activeBolusView() else {
debug(.apsManager, "Not enacting while in Bolus View")
processError(APSError.activeBolusViewBolus)
return
}

let roundedAmount = pump.roundToSupportedBolusVolume(units: Double(amount))
pump.enactBolus(units: roundedAmount, activationType: .manualRecommendationAccepted) { error in
if let error = error {
Expand Down Expand Up @@ -614,6 +630,13 @@ final class BaseAPSManager: APSManager, Injectable {
processError(error)
return
}

guard !activeBolusView() || (activeBolusView() && rate == 0) else {
debug(.apsManager, "Not enacting while in Bolus View")
processError(APSError.activeBolusViewBasal)
return
}

// unable to do temp basal during manual temp basal 😁
if isManualTempBasal {
processError(APSError.manualBasalTemp(message: "Loop not possible during the manual basal temp"))
Expand Down Expand Up @@ -753,6 +776,14 @@ final class BaseAPSManager: APSManager, Injectable {
return Just(()).setFailureType(to: Error.self)
.eraseToAnyPublisher()
}

guard !self.activeBolusView() || (self.activeBolusView() && rate == 0) else {
if let units = suggested.units {
return Fail(error: APSError.activeBolusViewBasalandBolus).eraseToAnyPublisher()
}
return Fail(error: APSError.activeBolusViewBasal).eraseToAnyPublisher()
}

return pump.enactTempBasal(unitsPerHour: Double(rate), for: TimeInterval(duration * 60)).map { _ in
let temp = TempBasal(duration: duration, rate: rate, temp: .absolute, timestamp: Date())
self.storage.save(temp, as: OpenAPS.Monitor.tempBasal)
Expand All @@ -765,12 +796,18 @@ final class BaseAPSManager: APSManager, Injectable {
if let error = self.verifyStatus() {
return Fail(error: error).eraseToAnyPublisher()
}

guard let units = suggested.units else {
// It is OK, no bolus required
debug(.apsManager, "No bolus required")
return Just(()).setFailureType(to: Error.self)
.eraseToAnyPublisher()
}

guard !self.activeBolusView() else {
return Fail(error: APSError.activeBolusViewBolus).eraseToAnyPublisher()
}

return pump.enactBolus(units: Double(units), automatic: true).map { _ in
self.bolusProgress.send(0)
self.bolusAmount.send(units)
Expand Down Expand Up @@ -1242,6 +1279,11 @@ final class BaseAPSManager: APSManager, Injectable {
}
}

private func activeBolusView() -> Bool {
let defaults = UserDefaults.standard
return defaults.bool(forKey: IAPSconfig.inBolusView)
}

private func branch() -> String {
var branch = "Unknown"
if let branchFileURL = Bundle.main.url(forResource: "branch", withExtension: "txt"),
Expand Down
1 change: 1 addition & 0 deletions FreeAPS/Sources/Application/FreeAPSApp.swift
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,7 @@ import Swinject
private func isNewVersion() {
let userDefaults = UserDefaults.standard
var version = userDefaults.string(forKey: IAPSconfig.version) ?? ""
userDefaults.set(false, forKey: IAPSconfig.inBolusView)

guard version.count > 1, version == (Bundle.main.releaseVersionNumber ?? "") else {
version = Bundle.main.releaseVersionNumber ?? ""
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,9 @@
/* Button */
"Done" = "Done";

/* Bolus View footer */
"Last loop %@ minutes ago. Complete or cancel this meal/bolus transaction to allow for next loop cycle to run" = "Last loop %@ minutes ago. Complete or cancel this meal/bolus transaction to allow for next loop cycle to run";

/* Calender Option */
"Display Emojis as Labels" = "Display Emojis as Labels";

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,9 @@
/* Button */
"Done" = "Klar";

/* Bolus View footer */
"Last loop %@ minutes ago. Complete or cancel this meal/bolus transaction to allow for next loop cycle to run" = "Senaste loop kördes för %@ minuter sedan. Gör färdigt detta steg eller avbryt/stäng denna vy för att låta nästa loop kunna köras";

/* Calender Option */
"Display Emojis as Labels" = "Visa Emojis";

Expand Down
1 change: 1 addition & 0 deletions FreeAPS/Sources/Models/Configs.swift
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ public enum IAPSconfig {
static let id = "iAPS.identifier"
static let version = "iAPS.version"
static let newVersion = "iAPS.newVersion"
static let inBolusView = "iAPS.inBolusView"
static let statURL = URL(string: "https://submit.open-iaps.app")!
}

Expand Down
43 changes: 43 additions & 0 deletions FreeAPS/Sources/Modules/Bolus/BolusStateModel.swift
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,18 @@ extension Bolus {
@Published var bolusIncrement: Decimal = 0.1
@Published var eventualBG: Bool = false
@Published var minimumPrediction: Bool = false
@Published var closedLoop: Bool = false
@Published var loopDate: Date = .distantFuture
@Published var now = Date.now

let loopReminder: CGFloat = 20

private var loopFormatter: NumberFormatter {
let formatter = NumberFormatter()
formatter.numberStyle = .decimal
formatter.maximumFractionDigits = 0
return formatter
}

override func subscribe() {
setupInsulinRequired()
Expand All @@ -82,6 +94,8 @@ extension Bolus {
eventualBG = settings.settings.eventualBG
displayPredictions = settings.settings.displayPredictions
bolusIncrement = settings.preferences.bolusIncrement
closedLoop = settings.settings.closedLoop
loopDate = apsManager.lastLoopDate

if waitForSuggestionInitial {
apsManager.determineBasal()
Expand All @@ -94,6 +108,7 @@ extension Bolus {
self.insulinRecommended = 0
}
}.store(in: &lifetime)
loopDate = apsManager.lastLoopDate
}
if let notNilSugguestion = provider.suggestion {
suggestion = notNilSugguestion
Expand Down Expand Up @@ -289,6 +304,18 @@ extension Bolus {
return nil
}

func notActive() {
let defaults = UserDefaults.standard
defaults.set(false, forKey: IAPSconfig.inBolusView)
//print("Active: NO") // For testing
}

func viewActive() {
let defaults = UserDefaults.standard
defaults.set(true, forKey: IAPSconfig.inBolusView)
//print("Active: YES") // For testing
}

private func prepareData() {
if !eventualBG {
var prepareData = [
Expand All @@ -308,6 +335,15 @@ extension Bolus {
}
}

func lastLoop() -> String? {
guard closedLoop else { return nil }
guard abs(now.timeIntervalSinceNow / 60) > loopReminder else { return nil }
let minAgo = abs(loopDate.timeIntervalSinceNow / 60)

let stringAgo = loopFormatter.string(from: minAgo as NSNumber) ?? ""
return "Last loop \(stringAgo) minutes ago. Complete or cancel this meal/bolus transaction to allow for next loop cycle to run"
}

private func roundBolus(_ amount: Decimal) -> Decimal {
// Account for increments (don't use the APSManager function as that gets too slow)
Decimal(round(Double(amount / bolusIncrement))) * bolusIncrement
Expand All @@ -321,5 +357,12 @@ extension Bolus.StateModel: SuggestionObserver {
self.waitForSuggestion = false
}
setupInsulinRequired()
loopDate = apsManager.lastLoopDate

if abs(loopDate.timeIntervalSinceNow / 60) > loopReminder * 1.5 {
hideModal()
notActive()
debug(.apsManager, "Force Closing Bolus View", printToConsole: true)
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -168,6 +168,13 @@ extension Bolus {
.listRowBackground(!disabled ? Color(.systemBlue) : Color(.systemGray4))
.tint(.white)
}
footer: {
if (-1 * state.loopDate.timeIntervalSinceNow / 60) > state.loopReminder, let string = state.lastLoop() {
Text(NSLocalizedString(string, comment: "Bolus View footer"))
.padding(.top, 20).multilineTextAlignment(.center)
.foregroundStyle(.orange)
}
}
}

if state.amount <= 0 {
Expand All @@ -185,6 +192,13 @@ extension Bolus {
.listRowBackground(Color(.systemBlue))
.tint(.white)
}
footer: {
if (-1 * state.loopDate.timeIntervalSinceNow / 60) > state.loopReminder, let string = state.lastLoop() {
Text(NSLocalizedString(string, comment: "Bolus View footer"))
.padding(.top, 20).multilineTextAlignment(.center)
.foregroundStyle(.orange)
}
}
}
}
.compactSectionSpacing()
Expand All @@ -204,11 +218,15 @@ extension Bolus {
Text("Meal")
}
},
trailing: Button { state.hideModal() }
trailing: Button {
state.hideModal()
state.notActive()
}
label: { Text("Cancel") }
)
.onAppear {
configureView {
state.viewActive()
state.waitForSuggestionInitial = waitForSuggestion
state.waitForSuggestion = waitForSuggestion
state.insulinCalculated = state.calculateInsulin()
Expand Down
13 changes: 13 additions & 0 deletions FreeAPS/Sources/Modules/Bolus/View/BolusRootView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,11 @@ extension Bolus {
meal: meal,
mealEntries: mealEntries
)
.onDisappear {
if state.eventualBG {
state.notActive()
}
}
} else {
AlternativeBolusCalcRootView(
resolver: resolver,
Expand All @@ -42,6 +47,11 @@ extension Bolus {
meal: meal,
mealEntries: mealEntries
)
.onDisappear {
if !state.eventualBG {
state.notActive()
}
}
}
} else {
cleanBolusView
Expand Down Expand Up @@ -100,6 +110,9 @@ extension Bolus {
} else if fetch, !keepForNextWiew, !state.useCalc {
state.delete(deleteTwice: false, meal: meal)
}
if !state.useCalc {
state.notActive()
}
}
.dynamicTypeSize(...DynamicTypeSize.xxLarge)
.navigationTitle("Enact Bolus")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,13 @@ extension Bolus {
return formatter
}

private var loopFormatter: NumberFormatter {
let formatter = NumberFormatter()
formatter.numberStyle = .decimal
formatter.maximumFractionDigits = 0
return formatter
}

private var glucoseFormatter: NumberFormatter {
let formatter = NumberFormatter()
formatter.numberStyle = .decimal
Expand Down Expand Up @@ -129,6 +136,13 @@ extension Bolus {
.listRowBackground(!disabled ? Color(.systemBlue) : Color(.systemGray4))
.tint(.white)
}
footer: {
if (-1 * state.loopDate.timeIntervalSinceNow / 60) > state.loopReminder, let string = state.lastLoop() {
Text(NSLocalizedString(string, comment: "Bolus View footer"))
.padding(.top, 20).multilineTextAlignment(.center)
.foregroundStyle(.orange)
}
}
.alert(isPresented: $isRemoteBolusAlertPresented) {
remoteBolusAlert!
}
Expand All @@ -148,12 +162,20 @@ extension Bolus {
.listRowBackground(Color(.systemBlue))
.tint(.white)
}
footer: {
if abs(state.loopDate.timeIntervalSinceNow / 60) > state.loopReminder, let string = state.lastLoop() {
Text(NSLocalizedString(string, comment: "Bolus View footer"))
.padding(.top, 20).multilineTextAlignment(.center)
.foregroundStyle(.orange)
}
}
}
}
.compactSectionSpacing()
.dynamicTypeSize(...DynamicTypeSize.xxLarge)
.onAppear {
configureView {
state.viewActive()
state.waitForSuggestionInitial = waitForSuggestion
state.waitForSuggestion = waitForSuggestion
}
Expand All @@ -179,7 +201,10 @@ extension Bolus {
Text("Meal")
}
},
trailing: Button { state.hideModal() }
trailing: Button {
state.hideModal()
state.notActive()
}
label: { Text("Cancel") }
)
.popup(isPresented: presentInfo, alignment: .bottom, direction: .bottom, type: .default) {
Expand Down

0 comments on commit a77d63a

Please sign in to comment.