|
|
@@ -1,3 +1,5 @@
|
|
|
+
|
|
|
+import LoopKit
|
|
|
import SwiftUI
|
|
|
import Swinject
|
|
|
|
|
|
@@ -7,28 +9,59 @@ extension Bolus {
|
|
|
@Injected() var apsManager: APSManager!
|
|
|
@Injected() var broadcaster: Broadcaster!
|
|
|
@Injected() var pumpHistoryStorage: PumpHistoryStorage!
|
|
|
+ // added for bolus calculator
|
|
|
+ @Injected() var glucoseStorage: GlucoseStorage!
|
|
|
+ @Injected() var settings: SettingsManager!
|
|
|
+ @Injected() var storage: FileStorage!
|
|
|
|
|
|
+ @Published var suggestion: Suggestion?
|
|
|
@Published var amount: Decimal = 0
|
|
|
@Published var insulinRecommended: Decimal = 0
|
|
|
@Published var insulinRequired: Decimal = 0
|
|
|
- @Published var waitForSuggestion: Bool = false
|
|
|
- @Published var error: Bool = false
|
|
|
+ @Published var units: GlucoseUnits = .mmolL
|
|
|
+ @Published var percentage: Decimal = 0
|
|
|
+ @Published var threshold: Decimal = 0
|
|
|
+ @Published var maxBolus: Decimal = 0
|
|
|
@Published var errorString: Decimal = 0
|
|
|
@Published var evBG: Int = 0
|
|
|
@Published var insulin: Decimal = 0
|
|
|
- @Published var target: Decimal = 0
|
|
|
@Published var isf: Decimal = 0
|
|
|
- @Published var percentage: Decimal = 0
|
|
|
- @Published var threshold: Decimal = 0
|
|
|
+ @Published var error: Bool = false
|
|
|
@Published var minGuardBG: Decimal = 0
|
|
|
@Published var minDelta: Decimal = 0
|
|
|
@Published var expectedDelta: Decimal = 0
|
|
|
@Published var minPredBG: Decimal = 0
|
|
|
- @Published var units: GlucoseUnits = .mmolL
|
|
|
- @Published var maxBolus: Decimal = 0
|
|
|
+ @Published var waitForSuggestion: Bool = false
|
|
|
|
|
|
var waitForSuggestionInitial: Bool = false
|
|
|
|
|
|
+ // added for bolus calculator
|
|
|
+ @Published var glucose: [BloodGlucose] = []
|
|
|
+ @Published var recentGlucose: BloodGlucose?
|
|
|
+ @Published var target: Decimal = 0
|
|
|
+ @Published var cob: Decimal = 0
|
|
|
+ @Published var iob: Decimal = 0
|
|
|
+
|
|
|
+ @Published var currentBG: Decimal = 0
|
|
|
+ @Published var fifteenMinInsulin: Decimal = 0
|
|
|
+ @Published var deltaBG: Decimal = 0
|
|
|
+ @Published var targetDifferenceInsulin: Decimal = 0
|
|
|
+ @Published var wholeCobInsulin: Decimal = 0
|
|
|
+ @Published var iobInsulinReduction: Decimal = 0
|
|
|
+ @Published var wholeCalc: Decimal = 0
|
|
|
+ @Published var roundedWholeCalc: Decimal = 0
|
|
|
+ @Published var insulinCalculated: Decimal = 0
|
|
|
+ @Published var roundedInsulinCalculated: Decimal = 0
|
|
|
+ @Published var fraction: Decimal = 0
|
|
|
+ @Published var useCalc: Bool = false
|
|
|
+ @Published var basal: Decimal = 0
|
|
|
+ @Published var fattyMeals: Bool = false
|
|
|
+ @Published var fattyMealFactor: Decimal = 0
|
|
|
+ @Published var useFattyMealCorrectionFactor: Bool = false
|
|
|
+
|
|
|
+ @Published var carbRatio: Decimal = 0
|
|
|
+ @Published var currentTime: String = ""
|
|
|
+
|
|
|
override func subscribe() {
|
|
|
setupInsulinRequired()
|
|
|
broadcaster.register(SuggestionObserver.self, observer: self)
|
|
|
@@ -37,6 +70,46 @@ extension Bolus {
|
|
|
threshold = provider.suggestion?.threshold ?? 0
|
|
|
maxBolus = provider.pumpSettings().maxBolus
|
|
|
|
|
|
+ // added
|
|
|
+ fraction = settings.settings.overrideFactor
|
|
|
+ useCalc = settings.settings.useCalc
|
|
|
+ fattyMeals = settings.settings.fattyMeals
|
|
|
+ fattyMealFactor = settings.settings.fattyMealFactor
|
|
|
+
|
|
|
+ // get carb ratio entry schedule
|
|
|
+ let schedule = provider.getProfile().schedule
|
|
|
+ // get current time in same format as carb ratio entry start date
|
|
|
+ let dateFormatter = DateFormatter()
|
|
|
+ dateFormatter.dateFormat = "HH:mm:ss"
|
|
|
+ let currentTime = dateFormatter.string(from: Date())
|
|
|
+ // loop through schedule to get current carb ratio
|
|
|
+ for (index, entry) in schedule.enumerated() {
|
|
|
+ if let entryStartTimeDate = dateFormatter.date(from: entry.start) {
|
|
|
+ var entryEndTimeDate: Date
|
|
|
+
|
|
|
+ if index < schedule.count - 1 {
|
|
|
+ let nextEntry = schedule[index + 1]
|
|
|
+ if let nextEntryStartTimeDate = dateFormatter.date(from: nextEntry.start) {
|
|
|
+ let timeDifference = nextEntryStartTimeDate.timeIntervalSince(entryStartTimeDate)
|
|
|
+ entryEndTimeDate = entryStartTimeDate.addingTimeInterval(timeDifference)
|
|
|
+ } else {
|
|
|
+ continue
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ entryEndTimeDate = Date()
|
|
|
+ }
|
|
|
+ // if currentTime is between start and end of carb ratio entry -> carbRatio = currentRatio
|
|
|
+ if let currentTimeDate = dateFormatter.date(from: currentTime) {
|
|
|
+ if currentTimeDate >= entryStartTimeDate, currentTimeDate <= entryEndTimeDate {
|
|
|
+ if let currentRatio = entry.ratio as? Decimal {
|
|
|
+ carbRatio = currentRatio
|
|
|
+ break
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
if waitForSuggestionInitial {
|
|
|
apsManager.determineBasal()
|
|
|
.receive(on: DispatchQueue.main)
|
|
|
@@ -51,13 +124,80 @@ extension Bolus {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+ func getDeltaBG() {
|
|
|
+ let glucose = glucoseStorage.recent()
|
|
|
+ guard glucose.count >= 3 else { return }
|
|
|
+
|
|
|
+ let lastGlucose = glucose.last!
|
|
|
+
|
|
|
+ let thirdLastGlucose = glucose[glucose.count - 3]
|
|
|
+ let delta = Decimal(lastGlucose.glucose!) - Decimal(thirdLastGlucose.glucose!)
|
|
|
+
|
|
|
+ deltaBG = delta
|
|
|
+ }
|
|
|
+
|
|
|
+ // CALCULATIONS FOR THE BOLUS CALCULATOR
|
|
|
+ func calculateInsulin() -> Decimal {
|
|
|
+ // for mmol conversion
|
|
|
+ var conversion: Decimal = 1.0
|
|
|
+ if units == .mmolL {
|
|
|
+ conversion = 0.0555
|
|
|
+ }
|
|
|
+ // insulin needed for the current blood glucose
|
|
|
+ let targetDifference = (currentBG - target) * conversion
|
|
|
+ targetDifferenceInsulin = targetDifference / isf
|
|
|
+
|
|
|
+ // more or less insulin because of bg trend in the last 15 minutes
|
|
|
+ fifteenMinInsulin = (deltaBG * conversion) / isf
|
|
|
+
|
|
|
+ // determine whole COB for which we want to dose insulin for and then determine insulin for wholeCOB
|
|
|
+ wholeCobInsulin = cob / carbRatio
|
|
|
+
|
|
|
+ // determine how much the calculator reduces/ increases the bolus because of IOB
|
|
|
+ iobInsulinReduction = (-1) * iob
|
|
|
+
|
|
|
+ // adding everything together
|
|
|
+ // add a calc for the case that no fifteenMinInsulin is available
|
|
|
+ if deltaBG != 0 {
|
|
|
+ wholeCalc = (targetDifferenceInsulin + iobInsulinReduction + wholeCobInsulin + fifteenMinInsulin)
|
|
|
+ } else {
|
|
|
+ // add (rare) case that no glucose value is available -> maybe display warning?
|
|
|
+ // if no bg is available, ?? sets its value to 0
|
|
|
+ if currentBG == 0 {
|
|
|
+ wholeCalc = (iobInsulinReduction + wholeCobInsulin)
|
|
|
+ } else {
|
|
|
+ wholeCalc = (targetDifferenceInsulin + iobInsulinReduction + wholeCobInsulin)
|
|
|
+ }
|
|
|
+ }
|
|
|
+ // rounding
|
|
|
+ let wholeCalcAsDouble = Double(wholeCalc)
|
|
|
+ roundedWholeCalc = Decimal(round(100 * wholeCalcAsDouble) / 100)
|
|
|
+
|
|
|
+ // apply custom factor at the end of the calculations
|
|
|
+ let result = wholeCalc * fraction
|
|
|
+
|
|
|
+ // apply custom factor if fatty meal toggle in bolus calc config settings is on and the box for fatty meals is checked (in RootView)
|
|
|
+ if useFattyMealCorrectionFactor {
|
|
|
+ insulinCalculated = result * fattyMealFactor
|
|
|
+ } else {
|
|
|
+ insulinCalculated = result
|
|
|
+ }
|
|
|
+
|
|
|
+ // display no negative insulinCalculated
|
|
|
+ insulinCalculated = max(insulinCalculated, 0)
|
|
|
+ let insulinCalculatedAsDouble = Double(insulinCalculated)
|
|
|
+ roundedInsulinCalculated = Decimal(round(100 * insulinCalculatedAsDouble) / 100)
|
|
|
+
|
|
|
+ return insulinCalculated
|
|
|
+ }
|
|
|
+
|
|
|
func add() {
|
|
|
guard amount > 0 else {
|
|
|
showModal(for: nil)
|
|
|
return
|
|
|
}
|
|
|
|
|
|
- let maxAmount = Double(min(amount, maxBolus))
|
|
|
+ let maxAmount = Double(min(amount, provider.pumpSettings().maxBolus))
|
|
|
|
|
|
unlockmanager.unlock()
|
|
|
.sink { _ in } receiveValue: { [weak self] _ in
|
|
|
@@ -73,7 +213,7 @@ extension Bolus {
|
|
|
showModal(for: nil)
|
|
|
return
|
|
|
}
|
|
|
- amount = min(amount, maxBolus * 3) // Allow for 3 * Max Bolus for non-pump insulin
|
|
|
+ amount = min(amount, maxBolus * 3)
|
|
|
|
|
|
pumpHistoryStorage.storeEvents(
|
|
|
[
|
|
|
@@ -98,8 +238,6 @@ extension Bolus {
|
|
|
DispatchQueue.main.async {
|
|
|
self.insulinRequired = self.provider.suggestion?.insulinReq ?? 0
|
|
|
|
|
|
- // Manual Bolus recommendation (normally) yields a higher amount than the insulin reqiured amount computed for SMBs (auto boluses). A manual bolus threfore now (test) uses the Eventual BG for glucose prediction, whereas the insulinReg for SMBs uses the minPredBG for glucose prediction (typically lower than Eventual BG).
|
|
|
-
|
|
|
var conversion: Decimal = 1.0
|
|
|
if self.units == .mmolL {
|
|
|
conversion = 0.0555
|
|
|
@@ -109,6 +247,10 @@ extension Bolus {
|
|
|
self.insulin = self.provider.suggestion?.insulinForManualBolus ?? 0
|
|
|
self.target = self.provider.suggestion?.current_target ?? 0
|
|
|
self.isf = self.provider.suggestion?.isf ?? 0
|
|
|
+ self.iob = self.provider.suggestion?.iob ?? 0
|
|
|
+ self.currentBG = (self.provider.suggestion?.bg ?? 0)
|
|
|
+ self.cob = self.provider.suggestion?.cob ?? 0
|
|
|
+ self.basal = self.provider.suggestion?.rate ?? 0
|
|
|
|
|
|
if self.settingsManager.settings.insulinReqPercentage != 100 {
|
|
|
self.insulinRecommended = self.insulin * (self.settingsManager.settings.insulinReqPercentage / 100)
|
|
|
@@ -125,6 +267,8 @@ extension Bolus {
|
|
|
|
|
|
self.insulinRecommended = self.apsManager
|
|
|
.roundBolus(amount: max(self.insulinRecommended, 0))
|
|
|
+
|
|
|
+ self.getDeltaBG()
|
|
|
}
|
|
|
}
|
|
|
}
|