AndroidAPS/app/src/main/java/info/nightscout/androidaps/utils/BolusWizard.kt

351 lines
16 KiB
Kotlin
Raw Normal View History

2019-06-16 09:00:47 +02:00
package info.nightscout.androidaps.utils
import android.content.Context
import android.content.Intent
import info.nightscout.androidaps.MainApp
import info.nightscout.androidaps.R
2019-12-22 21:37:26 +01:00
import info.nightscout.androidaps.activities.ErrorHelperActivity
2019-06-16 09:00:47 +02:00
import info.nightscout.androidaps.data.DetailedBolusInfo
import info.nightscout.androidaps.data.Profile
import info.nightscout.androidaps.db.CareportalEvent
import info.nightscout.androidaps.db.Source
import info.nightscout.androidaps.db.TempTarget
import info.nightscout.androidaps.events.EventRefreshOverview
import info.nightscout.androidaps.interfaces.Constraint
import info.nightscout.androidaps.interfaces.PluginType
import info.nightscout.androidaps.interfaces.PumpDescription
import info.nightscout.androidaps.interfaces.PumpInterface
import info.nightscout.androidaps.logging.L
import info.nightscout.androidaps.plugins.aps.loop.LoopPlugin
2019-10-14 17:23:31 +02:00
import info.nightscout.androidaps.plugins.bus.RxBus
2019-06-16 09:00:47 +02:00
import info.nightscout.androidaps.plugins.configBuilder.ConfigBuilderPlugin
import info.nightscout.androidaps.plugins.configBuilder.ProfileFunctions
import info.nightscout.androidaps.plugins.iob.iobCobCalculator.GlucoseStatus
2019-07-25 23:32:00 +02:00
import info.nightscout.androidaps.plugins.iob.iobCobCalculator.IobCobCalculatorPlugin
2019-06-16 09:00:47 +02:00
import info.nightscout.androidaps.plugins.treatments.TreatmentsPlugin
import info.nightscout.androidaps.queue.Callback
import org.json.JSONException
import org.json.JSONObject
import org.slf4j.LoggerFactory
import java.util.*
import kotlin.math.abs
2019-06-16 09:00:47 +02:00
2019-06-21 13:23:37 +02:00
class BolusWizard @JvmOverloads constructor(val profile: Profile,
val profileName: String,
val tempTarget: TempTarget?,
val carbs: Int,
val cob: Double,
val bg: Double,
val correction: Double,
private val percentageCorrection: Double = 100.0,
private val useBg: Boolean,
private val useCob: Boolean,
private val includeBolusIOB: Boolean,
private val includeBasalIOB: Boolean,
private val useSuperBolus: Boolean,
private val useTT: Boolean,
private val useTrend: Boolean,
val notes: String = "",
private val carbTime: Int = 0
) {
2019-06-16 09:00:47 +02:00
private val log = LoggerFactory.getLogger(L.CORE)
// Intermediate
var sens = 0.0
2019-06-21 13:23:37 +02:00
private set
2019-06-16 09:00:47 +02:00
var ic = 0.0
2019-06-21 13:23:37 +02:00
private set
2019-06-16 09:00:47 +02:00
var glucoseStatus: GlucoseStatus? = null
2019-06-21 13:23:37 +02:00
private set
2019-06-16 09:00:47 +02:00
private var targetBGLow = 0.0
2019-06-21 13:23:37 +02:00
private var targetBGHigh = 0.0
2019-06-21 13:23:37 +02:00
private var bgDiff = 0.0
2019-06-16 09:00:47 +02:00
var insulinFromBG = 0.0
2019-06-21 13:23:37 +02:00
private set
2019-06-16 09:00:47 +02:00
var insulinFromCarbs = 0.0
2019-06-21 13:23:37 +02:00
private set
var insulinFromBolusIOB = 0.0
private set
var insulinFromBasalsIOB = 0.0
private set
2019-06-16 09:00:47 +02:00
var insulinFromCorrection = 0.0
2019-06-21 13:23:37 +02:00
private set
2019-06-16 09:00:47 +02:00
var insulinFromSuperBolus = 0.0
2019-06-21 13:23:37 +02:00
private set
2019-06-16 09:00:47 +02:00
var insulinFromCOB = 0.0
2019-06-21 13:23:37 +02:00
private set
2019-06-16 09:00:47 +02:00
var insulinFromTrend = 0.0
2019-06-21 13:23:37 +02:00
private set
2019-06-16 09:00:47 +02:00
var trend = 0.0
2019-06-21 13:23:37 +02:00
private set
2019-06-16 09:00:47 +02:00
private var accepted = false
2019-06-16 09:00:47 +02:00
// Result
var calculatedTotalInsulin: Double = 0.0
2019-06-21 13:23:37 +02:00
private set
2019-06-16 09:00:47 +02:00
var totalBeforePercentageAdjustment: Double = 0.0
2019-06-21 13:23:37 +02:00
private set
2019-06-16 09:00:47 +02:00
var carbsEquivalent: Double = 0.0
2019-06-21 13:23:37 +02:00
private set
2019-06-16 09:00:47 +02:00
var insulinAfterConstraints: Double = 0.0
2019-06-21 13:23:37 +02:00
private set
init {
doCalc()
}
2019-06-16 09:00:47 +02:00
2019-06-21 13:23:37 +02:00
private fun doCalc() {
2019-06-16 09:00:47 +02:00
// Insulin from BG
2019-11-12 00:01:58 +01:00
sens = Profile.fromMgdlToUnits(profile.isfMgdl, ProfileFunctions.getSystemUnits())
targetBGLow = Profile.fromMgdlToUnits(profile.targetLowMgdl, ProfileFunctions.getSystemUnits())
targetBGHigh = Profile.fromMgdlToUnits(profile.targetHighMgdl, ProfileFunctions.getSystemUnits())
2019-06-16 09:00:47 +02:00
if (useTT && tempTarget != null) {
2019-11-12 00:01:58 +01:00
targetBGLow = Profile.fromMgdlToUnits(tempTarget.low, ProfileFunctions.getSystemUnits())
targetBGHigh = Profile.fromMgdlToUnits(tempTarget.high, ProfileFunctions.getSystemUnits())
2019-06-16 09:00:47 +02:00
}
if (useBg && bg > 0) {
bgDiff = when {
bg in targetBGLow..targetBGHigh -> 0.0
2019-12-22 21:37:26 +01:00
bg <= targetBGLow -> bg - targetBGLow
else -> bg - targetBGHigh
2019-06-16 09:00:47 +02:00
}
insulinFromBG = bgDiff / sens
}
// Insulin from 15 min trend
glucoseStatus = GlucoseStatus.getGlucoseStatusData()
2019-06-21 13:23:37 +02:00
glucoseStatus?.let {
if (useTrend) {
trend = it.short_avgdelta
2019-11-12 00:01:58 +01:00
insulinFromTrend = Profile.fromMgdlToUnits(trend, ProfileFunctions.getSystemUnits()) * 3 / sens
2019-06-21 13:23:37 +02:00
}
2019-06-16 09:00:47 +02:00
}
// Insulin from carbs
2019-06-16 09:00:47 +02:00
ic = profile.ic
insulinFromCarbs = carbs / ic
insulinFromCOB = if (useCob) (cob / ic) else 0.0
// Insulin from IOB
// IOB calculation
val treatments = TreatmentsPlugin.getPlugin()
treatments.updateTotalIOBTreatments()
val bolusIob = treatments.lastCalculationTreatments.round()
treatments.updateTotalIOBTempBasals()
val basalIob = treatments.lastCalculationTempBasals.round()
2019-06-21 13:23:37 +02:00
insulinFromBolusIOB = if (includeBolusIOB) -bolusIob.iob else 0.0
insulinFromBasalsIOB = if (includeBasalIOB) -basalIob.basaliob else 0.0
2019-06-16 09:00:47 +02:00
// Insulin from correction
insulinFromCorrection = correction
// Insulin from superbolus for 2h. Get basal rate now and after 1h
if (useSuperBolus) {
insulinFromSuperBolus = profile.basal
var timeAfter1h = System.currentTimeMillis()
timeAfter1h += T.hours(1).msecs()
insulinFromSuperBolus += profile.getBasal(timeAfter1h)
}
// Total
2019-06-21 13:23:37 +02:00
calculatedTotalInsulin = insulinFromBG + insulinFromTrend + insulinFromCarbs + insulinFromBolusIOB + insulinFromBasalsIOB + insulinFromCorrection + insulinFromSuperBolus + insulinFromCOB
2019-06-16 09:00:47 +02:00
// Percentage adjustment
totalBeforePercentageAdjustment = calculatedTotalInsulin
if (calculatedTotalInsulin > 0) {
calculatedTotalInsulin = calculatedTotalInsulin * percentageCorrection / 100.0
}
if (calculatedTotalInsulin < 0) {
carbsEquivalent = (-calculatedTotalInsulin) * ic
calculatedTotalInsulin = 0.0
}
2019-08-05 16:04:16 +02:00
val bolusStep = ConfigBuilderPlugin.getPlugin().activePump?.pumpDescription?.bolusStep
2019-12-22 21:37:26 +01:00
?: 0.1
2019-06-16 09:00:47 +02:00
calculatedTotalInsulin = Round.roundTo(calculatedTotalInsulin, bolusStep)
insulinAfterConstraints = MainApp.getConstraintChecker().applyBolusConstraints(Constraint(calculatedTotalInsulin)).value()
log.debug(this.toString())
}
2019-06-21 11:41:50 +02:00
private fun nsJSON(): JSONObject {
2019-06-16 09:00:47 +02:00
val boluscalcJSON = JSONObject()
try {
boluscalcJSON.put("profile", profileName)
boluscalcJSON.put("notes", notes)
boluscalcJSON.put("eventTime", DateUtil.toISOString(Date()))
boluscalcJSON.put("targetBGLow", targetBGLow)
boluscalcJSON.put("targetBGHigh", targetBGHigh)
boluscalcJSON.put("isf", sens)
boluscalcJSON.put("ic", ic)
2019-06-21 13:23:37 +02:00
boluscalcJSON.put("iob", -(insulinFromBolusIOB + insulinFromBasalsIOB))
boluscalcJSON.put("bolusiob", insulinFromBolusIOB)
boluscalcJSON.put("basaliob", insulinFromBasalsIOB)
2019-06-16 09:00:47 +02:00
boluscalcJSON.put("bolusiobused", includeBolusIOB)
boluscalcJSON.put("basaliobused", includeBasalIOB)
boluscalcJSON.put("bg", bg)
boluscalcJSON.put("insulinbg", insulinFromBG)
boluscalcJSON.put("insulinbgused", useBg)
boluscalcJSON.put("bgdiff", bgDiff)
boluscalcJSON.put("insulincarbs", insulinFromCarbs)
boluscalcJSON.put("carbs", carbs)
boluscalcJSON.put("cob", cob)
boluscalcJSON.put("cobused", useCob)
boluscalcJSON.put("insulincob", insulinFromCOB)
boluscalcJSON.put("othercorrection", correction)
boluscalcJSON.put("insulinsuperbolus", insulinFromSuperBolus)
boluscalcJSON.put("insulintrend", insulinFromTrend)
boluscalcJSON.put("insulin", calculatedTotalInsulin)
boluscalcJSON.put("superbolusused", useSuperBolus)
boluscalcJSON.put("insulinsuperbolus", insulinFromSuperBolus)
boluscalcJSON.put("trendused", useTrend)
boluscalcJSON.put("insulintrend", insulinFromTrend)
boluscalcJSON.put("trend", trend)
boluscalcJSON.put("ttused", useTT)
boluscalcJSON.put("percentageCorrection", percentageCorrection)
2019-06-16 09:00:47 +02:00
} catch (e: JSONException) {
log.error("Unhandled exception", e)
}
return boluscalcJSON
}
2019-06-21 13:23:37 +02:00
private fun confirmMessageAfterConstraints(pump: PumpInterface): String {
2019-06-16 09:00:47 +02:00
2019-12-22 21:37:26 +01:00
var confirmMessage = ""
if (insulinAfterConstraints > 0) {
val pct = if (percentageCorrection != 100.0) " (" + percentageCorrection.toInt() + "%)" else ""
confirmMessage += "<br/>" + MainApp.gs(R.string.bolus) + ": " + "<font color='" + MainApp.gc(R.color.bolus) + "'>" + DecimalFormatter.toPumpSupportedBolus(insulinAfterConstraints) + "U" + pct + "</font>"
}
2019-07-25 23:32:00 +02:00
if (carbs > 0) {
var timeShift = ""
if (carbTime > 0) {
timeShift += " ( +" + MainApp.gs(R.string.mins, carbTime) + " )"
} else if (carbTime < 0) {
timeShift += " ( -" + MainApp.gs(R.string.mins, carbTime) + " )"
}
confirmMessage += "<br/>" + MainApp.gs(R.string.carbs) + ": " + "<font color='" + MainApp.gc(R.color.carbs) + "'>" + carbs + "g" + timeShift + "</font>"
}
if (insulinFromCOB > 0) {
confirmMessage += "<br/>" + MainApp.gs(R.string.insulinFromCob, MainApp.gc(R.color.cobAlert), insulinFromBolusIOB + insulinFromBasalsIOB + insulinFromCOB + insulinFromBG)
2019-07-25 23:32:00 +02:00
val absorptionRate = IobCobCalculatorPlugin.getPlugin().slowAbsorptionPercentage(60)
if (absorptionRate > .25)
confirmMessage += "<br/>" + MainApp.gs(R.string.slowabsorptiondetected, MainApp.gc(R.color.cobAlert), (absorptionRate * 100).toInt())
}
if (abs(insulinAfterConstraints - calculatedTotalInsulin) > pump.pumpDescription.pumpType.determineCorrectBolusStepSize(insulinAfterConstraints)) {
2019-07-25 23:32:00 +02:00
confirmMessage += "<br/>" + MainApp.gs(R.string.bolusconstraintappliedwarning, MainApp.gc(R.color.warning), calculatedTotalInsulin, insulinAfterConstraints)
2019-06-16 09:00:47 +02:00
}
return confirmMessage
}
fun confirmAndExecute(context: Context) {
val profile = ProfileFunctions.getInstance().profile ?: return
val pump = ConfigBuilderPlugin.getPlugin().activePump ?: return
2019-06-16 09:00:47 +02:00
if (calculatedTotalInsulin > 0.0 || carbs > 0.0) {
2019-12-22 21:37:26 +01:00
if (accepted) {
log.debug("guarding: already accepted")
return
}
accepted = true
2019-06-16 09:00:47 +02:00
2019-12-22 21:37:26 +01:00
val confirmMessage = confirmMessageAfterConstraints(pump)
2019-06-16 09:00:47 +02:00
2019-12-22 21:37:26 +01:00
OKDialog.showConfirmation(context, MainApp.gs(R.string.boluswizard), HtmlHelper.fromHtml(confirmMessage), Runnable {
if (insulinAfterConstraints > 0 || carbs > 0) {
if (useSuperBolus) {
2020-02-04 22:25:07 +01:00
log.debug("USER ENTRY: SUPERBOLUS TBR")
2019-12-22 21:37:26 +01:00
val loopPlugin = LoopPlugin.getPlugin()
if (loopPlugin.isEnabled(PluginType.LOOP)) {
loopPlugin.superBolusTo(System.currentTimeMillis() + 2 * 60L * 60 * 1000)
RxBus.send(EventRefreshOverview("WizardDialog"))
2019-06-16 09:00:47 +02:00
}
2019-12-22 21:37:26 +01:00
val pump1 = ConfigBuilderPlugin.getPlugin().activePump
if (pump1?.pumpDescription?.tempBasalStyle == PumpDescription.ABSOLUTE) {
ConfigBuilderPlugin.getPlugin().commandQueue.tempBasalAbsolute(0.0, 120, true, profile, object : Callback() {
2019-06-16 09:00:47 +02:00
override fun run() {
if (!result.success) {
val i = Intent(MainApp.instance(), ErrorHelperActivity::class.java)
i.putExtra("soundid", R.raw.boluserror)
i.putExtra("status", result.comment)
2019-12-22 21:37:26 +01:00
i.putExtra("title", MainApp.gs(R.string.tempbasaldeliveryerror))
2019-06-16 09:00:47 +02:00
i.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
MainApp.instance().startActivity(i)
}
}
})
} else {
2019-12-22 21:37:26 +01:00
ConfigBuilderPlugin.getPlugin().commandQueue.tempBasalPercent(0, 120, true, profile, object : Callback() {
override fun run() {
if (!result.success) {
val i = Intent(MainApp.instance(), ErrorHelperActivity::class.java)
i.putExtra("soundid", R.raw.boluserror)
i.putExtra("status", result.comment)
i.putExtra("title", MainApp.gs(R.string.tempbasaldeliveryerror))
i.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
MainApp.instance().startActivity(i)
}
}
})
2019-06-16 09:00:47 +02:00
}
}
2019-12-22 21:37:26 +01:00
val detailedBolusInfo = DetailedBolusInfo()
detailedBolusInfo.eventType = CareportalEvent.BOLUSWIZARD
detailedBolusInfo.insulin = insulinAfterConstraints
detailedBolusInfo.carbs = carbs.toDouble()
detailedBolusInfo.context = context
detailedBolusInfo.glucose = bg
detailedBolusInfo.glucoseType = "Manual"
detailedBolusInfo.carbTime = carbTime
detailedBolusInfo.boluscalc = nsJSON()
detailedBolusInfo.source = Source.USER
detailedBolusInfo.notes = notes
2020-02-04 22:25:07 +01:00
log.debug("USER ENTRY: BOLUS insulin $insulinAfterConstraints carbs: $carbs")
2019-12-22 21:37:26 +01:00
if (detailedBolusInfo.insulin > 0 || ConfigBuilderPlugin.getPlugin().activePump?.pumpDescription?.storesCarbInfo == true) {
ConfigBuilderPlugin.getPlugin().commandQueue.bolus(detailedBolusInfo, object : Callback() {
override fun run() {
if (!result.success) {
val i = Intent(MainApp.instance(), ErrorHelperActivity::class.java)
i.putExtra("soundid", R.raw.boluserror)
i.putExtra("status", result.comment)
i.putExtra("title", MainApp.gs(R.string.treatmentdeliveryerror))
i.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
MainApp.instance().startActivity(i)
}
}
})
} else {
TreatmentsPlugin.getPlugin().addToHistoryTreatment(detailedBolusInfo, false)
}
2019-06-16 09:00:47 +02:00
}
2019-12-22 21:37:26 +01:00
})
2019-06-16 09:00:47 +02:00
}
}
}