diff --git a/app/src/main/java/info/nightscout/androidaps/db/CompatDBHelper.kt b/app/src/main/java/info/nightscout/androidaps/db/CompatDBHelper.kt index dad08a143a..75c323f0d4 100644 --- a/app/src/main/java/info/nightscout/androidaps/db/CompatDBHelper.kt +++ b/app/src/main/java/info/nightscout/androidaps/db/CompatDBHelper.kt @@ -2,11 +2,9 @@ package info.nightscout.androidaps.db import info.nightscout.androidaps.database.AppRepository import info.nightscout.androidaps.database.entities.* -import info.nightscout.androidaps.events.EventFoodDatabaseChanged -import info.nightscout.androidaps.events.EventNewBG -import info.nightscout.androidaps.events.EventTempTargetChange -import info.nightscout.androidaps.events.EventTherapyEventChange -import info.nightscout.androidaps.events.EventTreatmentChange +import info.nightscout.androidaps.database.entities.ExtendedBolus +import info.nightscout.androidaps.database.entities.TemporaryBasal +import info.nightscout.androidaps.events.* import info.nightscout.androidaps.logging.AAPSLogger import info.nightscout.androidaps.logging.LTag import info.nightscout.androidaps.plugins.bus.RxBusWrapper @@ -56,5 +54,13 @@ class CompatDBHelper @Inject constructor( aapsLogger.debug(LTag.DATABASE, "Firing EventFoodDatabaseChanged") rxBus.send(EventTreatmentChange()) } + it.filterIsInstance().firstOrNull()?.let { + aapsLogger.debug(LTag.DATABASE, "Firing EventTempBasalChange") + rxBus.send(EventTempBasalChange()) + } + it.filterIsInstance().firstOrNull()?.let { + aapsLogger.debug(LTag.DATABASE, "Firing EventExtendedBolusChange") + rxBus.send(EventExtendedBolusChange()) + } } } \ No newline at end of file diff --git a/app/src/main/java/info/nightscout/androidaps/dialogs/TempBasalDialog.kt b/app/src/main/java/info/nightscout/androidaps/dialogs/TempBasalDialog.kt index 22cabe2540..b138d23579 100644 --- a/app/src/main/java/info/nightscout/androidaps/dialogs/TempBasalDialog.kt +++ b/app/src/main/java/info/nightscout/androidaps/dialogs/TempBasalDialog.kt @@ -12,11 +12,7 @@ import info.nightscout.androidaps.database.entities.ValueWithUnit import info.nightscout.androidaps.database.entities.UserEntry.Action import info.nightscout.androidaps.database.entities.UserEntry.Sources import info.nightscout.androidaps.databinding.DialogTempbasalBinding -import info.nightscout.androidaps.interfaces.ActivePluginProvider -import info.nightscout.androidaps.interfaces.CommandQueueProvider -import info.nightscout.androidaps.interfaces.Constraint -import info.nightscout.androidaps.interfaces.ProfileFunction -import info.nightscout.androidaps.interfaces.PumpDescription +import info.nightscout.androidaps.interfaces.* import info.nightscout.androidaps.logging.UserEntryLogger import info.nightscout.androidaps.plugins.configBuilder.ConstraintChecker import info.nightscout.androidaps.queue.Callback @@ -131,12 +127,12 @@ class TempBasalDialog : DialogFragmentWithDate() { uel.log(Action.TEMP_BASAL, Sources.TempBasalDialog, ValueWithUnit.Percent(percent), ValueWithUnit.Minute(durationInMinutes)) - commandQueue.tempBasalPercent(percent, durationInMinutes, true, profile, callback) + commandQueue.tempBasalPercent(percent, durationInMinutes, true, profile, PumpSync.TemporaryBasalType.NORMAL, callback) } else { uel.log(Action.TEMP_BASAL, Sources.TempBasalDialog, ValueWithUnit.Insulin(absolute), ValueWithUnit.Minute(durationInMinutes)) - commandQueue.tempBasalAbsolute(absolute, durationInMinutes, true, profile, callback) + commandQueue.tempBasalAbsolute(absolute, durationInMinutes, true, profile, PumpSync.TemporaryBasalType.NORMAL, callback) } }) } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/aps/loop/LoopPlugin.kt b/app/src/main/java/info/nightscout/androidaps/plugins/aps/loop/LoopPlugin.kt index 12596f3f33..e3a7395a2a 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/aps/loop/LoopPlugin.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/aps/loop/LoopPlugin.kt @@ -20,8 +20,8 @@ import info.nightscout.androidaps.database.entities.TherapyEvent import info.nightscout.androidaps.database.entities.UserEntry.Action import info.nightscout.androidaps.database.entities.UserEntry.Sources import info.nightscout.androidaps.database.entities.ValueWithUnit -import info.nightscout.androidaps.database.transactions.InsertTherapyEventAnnouncementTransaction import info.nightscout.androidaps.database.transactions.InsertIfNewByTimestampTherapyEventTransaction +import info.nightscout.androidaps.database.transactions.InsertTherapyEventAnnouncementTransaction import info.nightscout.androidaps.events.EventAcceptOpenLoopChange import info.nightscout.androidaps.events.EventAutosensCalculationFinished import info.nightscout.androidaps.events.EventNewBG @@ -37,7 +37,6 @@ import info.nightscout.androidaps.plugins.aps.loop.events.EventNewOpenLoopNotifi import info.nightscout.androidaps.plugins.bus.RxBusWrapper import info.nightscout.androidaps.plugins.configBuilder.ConstraintChecker import info.nightscout.androidaps.plugins.configBuilder.RunningConfiguration -import info.nightscout.androidaps.plugins.general.nsclient.NSUpload import info.nightscout.androidaps.plugins.general.overview.events.EventDismissNotification import info.nightscout.androidaps.plugins.general.overview.events.EventNewNotification import info.nightscout.androidaps.plugins.general.overview.notifications.Notification @@ -82,7 +81,6 @@ open class LoopPlugin @Inject constructor( private val iobCobCalculatorPlugin: IobCobCalculatorPlugin, private val receiverStatusStore: ReceiverStatusStore, private val fabricPrivacy: FabricPrivacy, - private val nsUpload: NSUpload, private val dateUtil: DateUtil, private val uel: UserEntryLogger, private val repository: AppRepository, @@ -104,6 +102,7 @@ open class LoopPlugin @Inject constructor( private var carbsSuggestionsSuspendedUntil: Long = 0 private var prevCarbsreq = 0 override var lastRun: LastRun? = null + override fun onStart() { createNotificationChannel() super.onStart() @@ -310,155 +309,154 @@ open class LoopPlugin @Inject constructor( aapsLogger.debug(LTag.APS, "SMB requested but still in 3 min interval") resultAfterConstraints.smb = 0.0 } - if (lastRun != null && lastRun!!.constraintsProcessed != null) { - prevCarbsreq = lastRun!!.constraintsProcessed!!.carbsReq - } - if (lastRun == null) lastRun = LastRun() - lastRun!!.request = apsResult - lastRun!!.constraintsProcessed = resultAfterConstraints - lastRun!!.lastAPSRun = DateUtil.now() - lastRun!!.source = (usedAPS as PluginBase).name - lastRun!!.tbrSetByPump = null - lastRun!!.smbSetByPump = null - lastRun!!.lastTBREnact = 0 - lastRun!!.lastTBRRequest = 0 - lastRun!!.lastSMBEnact = 0 - lastRun!!.lastSMBRequest = 0 - buildDeviceStatus(dateUtil, this, iobCobCalculatorPlugin, profileFunction, - activePlugin.activePump, receiverStatusStore, runningConfiguration, - BuildConfig.VERSION_NAME + "-" + BuildConfig.BUILDVERSION)?.also { - repository.insert(it) - } + prevCarbsreq = lastRun?.constraintsProcessed?.carbsReq ?: prevCarbsreq + lastRun = (lastRun ?: LastRun()).also { lastRun -> + lastRun.request = apsResult + lastRun.constraintsProcessed = resultAfterConstraints + lastRun.lastAPSRun = DateUtil.now() + lastRun.source = (usedAPS as PluginBase).name + lastRun.tbrSetByPump = null + lastRun.smbSetByPump = null + lastRun.lastTBREnact = 0 + lastRun.lastTBRRequest = 0 + lastRun.lastSMBEnact = 0 + lastRun.lastSMBRequest = 0 + buildDeviceStatus(dateUtil, this, iobCobCalculatorPlugin, profileFunction, + activePlugin.activePump, receiverStatusStore, runningConfiguration, + BuildConfig.VERSION_NAME + "-" + BuildConfig.BUILDVERSION)?.also { + repository.insert(it) + } - if (isSuspended) { - aapsLogger.debug(LTag.APS, resourceHelper.gs(R.string.loopsuspended)) - rxBus.send(EventLoopSetLastRunGui(resourceHelper.gs(R.string.loopsuspended))) - return - } - if (pump.isSuspended()) { - aapsLogger.debug(LTag.APS, resourceHelper.gs(R.string.pumpsuspended)) - rxBus.send(EventLoopSetLastRunGui(resourceHelper.gs(R.string.pumpsuspended))) - return - } - val closedLoopEnabled = constraintChecker.isClosedLoopAllowed() - if (closedLoopEnabled.value()) { - if (allowNotification) { - if (resultAfterConstraints.isCarbsRequired - && resultAfterConstraints.carbsReq >= sp.getInt(R.string.key_smb_enable_carbs_suggestions_threshold, 0) && carbsSuggestionsSuspendedUntil < System.currentTimeMillis() && !treatmentTimeThreshold(-15)) { - if (sp.getBoolean(R.string.key_enable_carbs_required_alert_local, true) && !sp.getBoolean(R.string.key_raise_notifications_as_android_notifications, true)) { - val carbReqLocal = Notification(Notification.CARBS_REQUIRED, resultAfterConstraints.carbsRequiredText, Notification.NORMAL) - rxBus.send(EventNewNotification(carbReqLocal)) - } - if (sp.getBoolean(R.string.key_ns_create_announcements_from_carbs_req, false)) { - disposable += repository.runTransaction(InsertTherapyEventAnnouncementTransaction(resultAfterConstraints.carbsRequiredText)).subscribe() - } - if (sp.getBoolean(R.string.key_enable_carbs_required_alert_local, true) && sp.getBoolean(R.string.key_raise_notifications_as_android_notifications, true)) { - val intentAction5m = Intent(context, CarbSuggestionReceiver::class.java) - intentAction5m.putExtra("ignoreDuration", 5) - val pendingIntent5m = PendingIntent.getBroadcast(context, 1, intentAction5m, PendingIntent.FLAG_UPDATE_CURRENT) - val actionIgnore5m = NotificationCompat.Action(R.drawable.ic_notif_aaps, resourceHelper.gs(R.string.ignore5m, "Ignore 5m"), pendingIntent5m) - val intentAction15m = Intent(context, CarbSuggestionReceiver::class.java) - intentAction15m.putExtra("ignoreDuration", 15) - val pendingIntent15m = PendingIntent.getBroadcast(context, 1, intentAction15m, PendingIntent.FLAG_UPDATE_CURRENT) - val actionIgnore15m = NotificationCompat.Action(R.drawable.ic_notif_aaps, resourceHelper.gs(R.string.ignore15m, "Ignore 15m"), pendingIntent15m) - val intentAction30m = Intent(context, CarbSuggestionReceiver::class.java) - intentAction30m.putExtra("ignoreDuration", 30) - val pendingIntent30m = PendingIntent.getBroadcast(context, 1, intentAction30m, PendingIntent.FLAG_UPDATE_CURRENT) - val actionIgnore30m = NotificationCompat.Action(R.drawable.ic_notif_aaps, resourceHelper.gs(R.string.ignore30m, "Ignore 30m"), pendingIntent30m) - val builder = NotificationCompat.Builder(context, CHANNEL_ID) - builder.setSmallIcon(R.drawable.notif_icon) - .setContentTitle(resourceHelper.gs(R.string.carbssuggestion)) - .setContentText(resultAfterConstraints.carbsRequiredText) - .setAutoCancel(true) - .setPriority(Notification.IMPORTANCE_HIGH) - .setCategory(Notification.CATEGORY_ALARM) - .addAction(actionIgnore5m) - .addAction(actionIgnore15m) - .addAction(actionIgnore30m) - .setVisibility(NotificationCompat.VISIBILITY_PUBLIC) - .setVibrate(longArrayOf(1000, 1000, 1000, 1000, 1000)) - val mNotificationManager = context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager + if (isSuspended) { + aapsLogger.debug(LTag.APS, resourceHelper.gs(R.string.loopsuspended)) + rxBus.send(EventLoopSetLastRunGui(resourceHelper.gs(R.string.loopsuspended))) + return + } + if (pump.isSuspended()) { + aapsLogger.debug(LTag.APS, resourceHelper.gs(R.string.pumpsuspended)) + rxBus.send(EventLoopSetLastRunGui(resourceHelper.gs(R.string.pumpsuspended))) + return + } + val closedLoopEnabled = constraintChecker.isClosedLoopAllowed() + if (closedLoopEnabled.value()) { + if (allowNotification) { + if (resultAfterConstraints.isCarbsRequired + && resultAfterConstraints.carbsReq >= sp.getInt(R.string.key_smb_enable_carbs_suggestions_threshold, 0) && carbsSuggestionsSuspendedUntil < System.currentTimeMillis() && !treatmentTimeThreshold(-15)) { + if (sp.getBoolean(R.string.key_enable_carbs_required_alert_local, true) && !sp.getBoolean(R.string.key_raise_notifications_as_android_notifications, true)) { + val carbReqLocal = Notification(Notification.CARBS_REQUIRED, resultAfterConstraints.carbsRequiredText, Notification.NORMAL) + rxBus.send(EventNewNotification(carbReqLocal)) + } + if (sp.getBoolean(R.string.key_ns_create_announcements_from_carbs_req, false)) { + disposable += repository.runTransaction(InsertTherapyEventAnnouncementTransaction(resultAfterConstraints.carbsRequiredText)).subscribe() + } + if (sp.getBoolean(R.string.key_enable_carbs_required_alert_local, true) && sp.getBoolean(R.string.key_raise_notifications_as_android_notifications, true)) { + val intentAction5m = Intent(context, CarbSuggestionReceiver::class.java) + intentAction5m.putExtra("ignoreDuration", 5) + val pendingIntent5m = PendingIntent.getBroadcast(context, 1, intentAction5m, PendingIntent.FLAG_UPDATE_CURRENT) + val actionIgnore5m = NotificationCompat.Action(R.drawable.ic_notif_aaps, resourceHelper.gs(R.string.ignore5m, "Ignore 5m"), pendingIntent5m) + val intentAction15m = Intent(context, CarbSuggestionReceiver::class.java) + intentAction15m.putExtra("ignoreDuration", 15) + val pendingIntent15m = PendingIntent.getBroadcast(context, 1, intentAction15m, PendingIntent.FLAG_UPDATE_CURRENT) + val actionIgnore15m = NotificationCompat.Action(R.drawable.ic_notif_aaps, resourceHelper.gs(R.string.ignore15m, "Ignore 15m"), pendingIntent15m) + val intentAction30m = Intent(context, CarbSuggestionReceiver::class.java) + intentAction30m.putExtra("ignoreDuration", 30) + val pendingIntent30m = PendingIntent.getBroadcast(context, 1, intentAction30m, PendingIntent.FLAG_UPDATE_CURRENT) + val actionIgnore30m = NotificationCompat.Action(R.drawable.ic_notif_aaps, resourceHelper.gs(R.string.ignore30m, "Ignore 30m"), pendingIntent30m) + val builder = NotificationCompat.Builder(context, CHANNEL_ID) + builder.setSmallIcon(R.drawable.notif_icon) + .setContentTitle(resourceHelper.gs(R.string.carbssuggestion)) + .setContentText(resultAfterConstraints.carbsRequiredText) + .setAutoCancel(true) + .setPriority(Notification.IMPORTANCE_HIGH) + .setCategory(Notification.CATEGORY_ALARM) + .addAction(actionIgnore5m) + .addAction(actionIgnore15m) + .addAction(actionIgnore30m) + .setVisibility(NotificationCompat.VISIBILITY_PUBLIC) + .setVibrate(longArrayOf(1000, 1000, 1000, 1000, 1000)) + val mNotificationManager = context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager - // mId allows you to update the notification later on. - mNotificationManager.notify(Constants.notificationID, builder.build()) - rxBus.send(EventNewOpenLoopNotification()) + // mId allows you to update the notification later on. + mNotificationManager.notify(Constants.notificationID, builder.build()) + rxBus.send(EventNewOpenLoopNotification()) - //only send to wear if Native notifications are turned off - if (!sp.getBoolean(R.string.key_raise_notifications_as_android_notifications, true)) { - // Send to Wear - rxBus.send(EventWearInitiateAction("changeRequest")) + //only send to wear if Native notifications are turned off + if (!sp.getBoolean(R.string.key_raise_notifications_as_android_notifications, true)) { + // Send to Wear + rxBus.send(EventWearInitiateAction("changeRequest")) + } + } + } else { + //If carbs were required previously, but are no longer needed, dismiss notifications + if (prevCarbsreq > 0) { + dismissSuggestion() + rxBus.send(EventDismissNotification(Notification.CARBS_REQUIRED)) } } - } else { - //If carbs were required previously, but are no longer needed, dismiss notifications - if (prevCarbsreq > 0) { - dismissSuggestion() - rxBus.send(EventDismissNotification(Notification.CARBS_REQUIRED)) - } } - } - if (resultAfterConstraints.isChangeRequested - && !commandQueue.bolusInQueue() - && !commandQueue.isRunning(Command.CommandType.BOLUS)) { - val waiting = PumpEnactResult(injector) - waiting.queued = true - if (resultAfterConstraints.tempBasalRequested) lastRun!!.tbrSetByPump = waiting - if (resultAfterConstraints.bolusRequested) lastRun!!.smbSetByPump = waiting - rxBus.send(EventLoopUpdateGui()) - fabricPrivacy.logCustom("APSRequest") - applyTBRRequest(resultAfterConstraints, profile, object : Callback() { - override fun run() { - if (result.enacted || result.success) { - lastRun!!.tbrSetByPump = result - lastRun!!.lastTBRRequest = lastRun!!.lastAPSRun - lastRun!!.lastTBREnact = DateUtil.now() - rxBus.send(EventLoopUpdateGui()) - applySMBRequest(resultAfterConstraints, object : Callback() { - override fun run() { - // Callback is only called if a bolus was actually requested - if (result.enacted || result.success) { - lastRun!!.smbSetByPump = result - lastRun!!.lastSMBRequest = lastRun!!.lastAPSRun - lastRun!!.lastSMBEnact = DateUtil.now() - } else { - Thread { - SystemClock.sleep(1000) - invoke("tempBasalFallback", allowNotification, true) - }.start() + if (resultAfterConstraints.isChangeRequested + && !commandQueue.bolusInQueue() + && !commandQueue.isRunning(Command.CommandType.BOLUS)) { + val waiting = PumpEnactResult(injector) + waiting.queued = true + if (resultAfterConstraints.tempBasalRequested) lastRun.tbrSetByPump = waiting + if (resultAfterConstraints.bolusRequested) lastRun.smbSetByPump = waiting + rxBus.send(EventLoopUpdateGui()) + fabricPrivacy.logCustom("APSRequest") + applyTBRRequest(resultAfterConstraints, profile, object : Callback() { + override fun run() { + if (result.enacted || result.success) { + lastRun.tbrSetByPump = result + lastRun.lastTBRRequest = lastRun.lastAPSRun + lastRun.lastTBREnact = DateUtil.now() + rxBus.send(EventLoopUpdateGui()) + applySMBRequest(resultAfterConstraints, object : Callback() { + override fun run() { + // Callback is only called if a bolus was actually requested + if (result.enacted || result.success) { + lastRun.smbSetByPump = result + lastRun.lastSMBRequest = lastRun.lastAPSRun + lastRun.lastSMBEnact = DateUtil.now() + } else { + Thread { + SystemClock.sleep(1000) + invoke("tempBasalFallback", allowNotification, true) + }.start() + } + rxBus.send(EventLoopUpdateGui()) } - rxBus.send(EventLoopUpdateGui()) - } - }) - } else { - lastRun!!.tbrSetByPump = result - lastRun!!.lastTBRRequest = lastRun!!.lastAPSRun + }) + } else { + lastRun.tbrSetByPump = result + lastRun.lastTBRRequest = lastRun.lastAPSRun + } + rxBus.send(EventLoopUpdateGui()) } - rxBus.send(EventLoopUpdateGui()) - } - }) - } else { - lastRun!!.tbrSetByPump = null - lastRun!!.smbSetByPump = null - } - } else { - if (resultAfterConstraints.isChangeRequested && allowNotification) { - val builder = NotificationCompat.Builder(context, CHANNEL_ID) - builder.setSmallIcon(R.drawable.notif_icon) - .setContentTitle(resourceHelper.gs(R.string.openloop_newsuggestion)) - .setContentText(resultAfterConstraints.toString()) - .setAutoCancel(true) - .setPriority(Notification.IMPORTANCE_HIGH) - .setCategory(Notification.CATEGORY_ALARM) - .setVisibility(NotificationCompat.VISIBILITY_PUBLIC) - if (sp.getBoolean(R.string.key_wear_control, false)) { - builder.setLocalOnly(true) + }) + } else { + lastRun.tbrSetByPump = null + lastRun.smbSetByPump = null + } + } else { + if (resultAfterConstraints.isChangeRequested && allowNotification) { + val builder = NotificationCompat.Builder(context, CHANNEL_ID) + builder.setSmallIcon(R.drawable.notif_icon) + .setContentTitle(resourceHelper.gs(R.string.openloop_newsuggestion)) + .setContentText(resultAfterConstraints.toString()) + .setAutoCancel(true) + .setPriority(Notification.IMPORTANCE_HIGH) + .setCategory(Notification.CATEGORY_ALARM) + .setVisibility(NotificationCompat.VISIBILITY_PUBLIC) + if (sp.getBoolean(R.string.key_wear_control, false)) { + builder.setLocalOnly(true) + } + presentSuggestion(builder) + } else if (allowNotification) { + dismissSuggestion() } - presentSuggestion(builder) - } else if (allowNotification) { - dismissSuggestion() } + rxBus.send(EventLoopUpdateGui()) } - rxBus.send(EventLoopUpdateGui()) } finally { aapsLogger.debug(LTag.APS, "invoke end") } @@ -501,24 +499,28 @@ open class LoopPlugin @Inject constructor( } fun acceptChangeRequest() { - val profile = profileFunction.getProfile() - applyTBRRequest(lastRun!!.constraintsProcessed, profile, object : Callback() { - override fun run() { - if (result.enacted) { - lastRun!!.tbrSetByPump = result - lastRun!!.lastTBRRequest = lastRun!!.lastAPSRun - lastRun!!.lastTBREnact = DateUtil.now() - lastRun!!.lastOpenModeAccept = DateUtil.now() - buildDeviceStatus(dateUtil, this@LoopPlugin, iobCobCalculatorPlugin, profileFunction, - activePlugin.activePump, receiverStatusStore, runningConfiguration, - BuildConfig.VERSION_NAME + "-" + BuildConfig.BUILDVERSION)?.also { - repository.insert(it) + val profile = profileFunction.getProfile() ?: return + lastRun?.let { lastRun -> + lastRun.constraintsProcessed?.let { constraintsProcessed -> + applyTBRRequest(constraintsProcessed, profile, object : Callback() { + override fun run() { + if (result.enacted) { + lastRun.tbrSetByPump = result + lastRun.lastTBRRequest = lastRun.lastAPSRun + lastRun.lastTBREnact = DateUtil.now() + lastRun.lastOpenModeAccept = DateUtil.now() + buildDeviceStatus(dateUtil, this@LoopPlugin, iobCobCalculatorPlugin, profileFunction, + activePlugin.activePump, receiverStatusStore, runningConfiguration, + BuildConfig.VERSION_NAME + "-" + BuildConfig.BUILDVERSION)?.also { + repository.insert(it) + } + sp.incInt(R.string.key_ObjectivesmanualEnacts) + } + rxBus.send(EventAcceptOpenLoopChange()) } - sp.incInt(R.string.key_ObjectivesmanualEnacts) - } - rxBus.send(EventAcceptOpenLoopChange()) + }) } - }) + } fabricPrivacy.logCustom("AcceptTemp") } @@ -526,8 +528,8 @@ open class LoopPlugin @Inject constructor( * expect absolute request and allow both absolute and percent response based on pump capabilities * TODO: update pump drivers to support APS request in % */ - private fun applyTBRRequest(request: APSResult?, profile: Profile?, callback: Callback?) { - if (!request!!.tempBasalRequested) { + private fun applyTBRRequest(request: APSResult, profile: Profile, callback: Callback?) { + if (!request.tempBasalRequested) { callback?.result(PumpEnactResult(injector).enacted(false).success(true).comment(R.string.nochangerequested))?.run() return } @@ -566,7 +568,7 @@ open class LoopPlugin @Inject constructor( uel.log(Action.TEMP_BASAL, Sources.Loop, ValueWithUnit.Percent(request.percent), ValueWithUnit.Minute(request.duration)) - commandQueue.tempBasalPercent(request.percent, request.duration, false, profile!!, callback) + commandQueue.tempBasalPercent(request.percent, request.duration, false, profile, PumpSync.TemporaryBasalType.NORMAL, callback) } } else { if (request.rate == 0.0 && request.duration == 0 || abs(request.rate - pump.baseBasalRate) < pump.pumpDescription.basalStep) { @@ -589,7 +591,7 @@ open class LoopPlugin @Inject constructor( uel.log(Action.TEMP_BASAL, Sources.Loop, ValueWithUnit.UnitPerHour(request.rate), ValueWithUnit.Minute(request.duration)) - commandQueue.tempBasalAbsolute(request.rate, request.duration, false, profile!!, callback) + commandQueue.tempBasalAbsolute(request.rate, request.duration, false, profile, PumpSync.TemporaryBasalType.NORMAL, callback) } } } @@ -640,7 +642,7 @@ open class LoopPlugin @Inject constructor( val pump = activePlugin.activePump disconnectTo(System.currentTimeMillis() + durationInMinutes * 60 * 1000L) if (pump.pumpDescription.tempBasalStyle == PumpDescription.ABSOLUTE) { - commandQueue.tempBasalAbsolute(0.0, durationInMinutes, true, profile!!, object : Callback() { + commandQueue.tempBasalAbsolute(0.0, durationInMinutes, true, profile!!, PumpSync.TemporaryBasalType.EMULATED_PUMP_SUSPEND, object : Callback() { override fun run() { if (!result.success) { ErrorHelperActivity.runAlarm(context, result.comment, resourceHelper.gs(R.string.tempbasaldeliveryerror), info.nightscout.androidaps.dana.R.raw.boluserror) @@ -648,7 +650,7 @@ open class LoopPlugin @Inject constructor( } }) } else { - commandQueue.tempBasalPercent(0, durationInMinutes, true, profile!!, object : Callback() { + commandQueue.tempBasalPercent(0, durationInMinutes, true, profile!!, PumpSync.TemporaryBasalType.EMULATED_PUMP_SUSPEND, object : Callback() { override fun run() { if (!result.success) { ErrorHelperActivity.runAlarm(context, result.comment, resourceHelper.gs(R.string.tempbasaldeliveryerror), info.nightscout.androidaps.dana.R.raw.boluserror) diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/smsCommunicator/SmsCommunicatorPlugin.kt b/app/src/main/java/info/nightscout/androidaps/plugins/general/smsCommunicator/SmsCommunicatorPlugin.kt index f9c602dee3..8c5e97ea9a 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/smsCommunicator/SmsCommunicatorPlugin.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/smsCommunicator/SmsCommunicatorPlugin.kt @@ -182,7 +182,8 @@ class SmsCommunicatorPlugin @Inject constructor( override fun doWork(): Result { val bundle = dataWorker.pickupBundle(inputData.getLong(DataWorker.STORE_KEY, -1)) ?: return Result.failure(workDataOf("Error" to "missing input data")) - val format = bundle.getString("format") ?: return Result.failure(workDataOf("Error" to "missing format in input data")) + val format = bundle.getString("format") + ?: return Result.failure(workDataOf("Error" to "missing format in input data")) val pdus = bundle["pdus"] as Array<*> for (pdu in pdus) { val message = SmsMessage.createFromPdu(pdu as ByteArray, format) @@ -636,7 +637,7 @@ class SmsCommunicatorPlugin @Inject constructor( receivedSms.processed = true messageToConfirm = AuthRequest(injector, receivedSms, reply, passCode, object : SmsAction(tempBasalPct, duration) { override fun run() { - commandQueue.tempBasalPercent(anInteger(), secondInteger(), true, profile, object : Callback() { + commandQueue.tempBasalPercent(anInteger(), secondInteger(), true, profile, PumpSync.TemporaryBasalType.NORMAL, object : Callback() { override fun run() { if (result.success) { var replyText = if (result.isPercent) String.format(resourceHelper.gs(R.string.smscommunicator_tempbasalset_percent), result.percent, result.duration) else String.format(resourceHelper.gs(R.string.smscommunicator_tempbasalset), result.absolute, result.duration) @@ -678,7 +679,7 @@ class SmsCommunicatorPlugin @Inject constructor( receivedSms.processed = true messageToConfirm = AuthRequest(injector, receivedSms, reply, passCode, object : SmsAction(tempBasal, duration) { override fun run() { - commandQueue.tempBasalAbsolute(aDouble(), secondInteger(), true, profile, object : Callback() { + commandQueue.tempBasalAbsolute(aDouble(), secondInteger(), true, profile, PumpSync.TemporaryBasalType.NORMAL, object : Callback() { override fun run() { if (result.success) { var replyText = if (result.isPercent) String.format(resourceHelper.gs(R.string.smscommunicator_tempbasalset_percent), result.percent, result.duration) diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/pump/mdi/MDIPlugin.kt b/app/src/main/java/info/nightscout/androidaps/plugins/pump/mdi/MDIPlugin.kt index 58c2af56f8..a260ed0482 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/pump/mdi/MDIPlugin.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/pump/mdi/MDIPlugin.kt @@ -93,7 +93,7 @@ class MDIPlugin @Inject constructor( } override fun stopBolusDelivering() {} - override fun setTempBasalAbsolute(absoluteRate: Double, durationInMinutes: Int, profile: Profile, enforceNew: Boolean): PumpEnactResult { + override fun setTempBasalAbsolute(absoluteRate: Double, durationInMinutes: Int, profile: Profile, enforceNew: Boolean, tbrType: PumpSync.TemporaryBasalType): PumpEnactResult { val result = PumpEnactResult(injector) result.success = false result.comment = resourceHelper.gs(R.string.pumperror) @@ -101,7 +101,7 @@ class MDIPlugin @Inject constructor( return result } - override fun setTempBasalPercent(percent: Int, durationInMinutes: Int, profile: Profile, enforceNew: Boolean): PumpEnactResult { + override fun setTempBasalPercent(percent: Int, durationInMinutes: Int, profile: Profile, enforceNew: Boolean, tbrType: PumpSync.TemporaryBasalType): PumpEnactResult { val result = PumpEnactResult(injector) result.success = false result.comment = resourceHelper.gs(R.string.pumperror) diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/pump/virtual/VirtualPumpPlugin.kt b/app/src/main/java/info/nightscout/androidaps/plugins/pump/virtual/VirtualPumpPlugin.kt index 11822e797a..d2e761f3b3 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/pump/virtual/VirtualPumpPlugin.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/pump/virtual/VirtualPumpPlugin.kt @@ -222,7 +222,7 @@ open class VirtualPumpPlugin @Inject constructor( } override fun stopBolusDelivering() {} - override fun setTempBasalAbsolute(absoluteRate: Double, durationInMinutes: Int, profile: Profile, enforceNew: Boolean): PumpEnactResult { + override fun setTempBasalAbsolute(absoluteRate: Double, durationInMinutes: Int, profile: Profile, enforceNew: Boolean, tbrType: PumpSync.TemporaryBasalType): PumpEnactResult { val tempBasal = TemporaryBasal(injector) .date(System.currentTimeMillis()) .absolute(absoluteRate) @@ -242,7 +242,7 @@ open class VirtualPumpPlugin @Inject constructor( return result } - override fun setTempBasalPercent(percent: Int, durationInMinutes: Int, profile: Profile, enforceNew: Boolean): PumpEnactResult { + override fun setTempBasalPercent(percent: Int, durationInMinutes: Int, profile: Profile, enforceNew: Boolean, tbrType: PumpSync.TemporaryBasalType): PumpEnactResult { val tempBasal = TemporaryBasal(injector) .date(System.currentTimeMillis()) .percent(percent) diff --git a/app/src/main/java/info/nightscout/androidaps/queue/CommandQueue.kt b/app/src/main/java/info/nightscout/androidaps/queue/CommandQueue.kt index 70b56e858d..6d0881da2c 100644 --- a/app/src/main/java/info/nightscout/androidaps/queue/CommandQueue.kt +++ b/app/src/main/java/info/nightscout/androidaps/queue/CommandQueue.kt @@ -22,6 +22,7 @@ import info.nightscout.androidaps.interfaces.ActivePluginProvider import info.nightscout.androidaps.interfaces.CommandQueueProvider import info.nightscout.androidaps.interfaces.Constraint import info.nightscout.androidaps.interfaces.ProfileFunction +import info.nightscout.androidaps.interfaces.PumpSync import info.nightscout.androidaps.logging.AAPSLogger import info.nightscout.androidaps.logging.LTag import info.nightscout.androidaps.plugins.bus.RxBusWrapper @@ -280,7 +281,7 @@ open class CommandQueue @Inject constructor( } // returns true if command is queued - override fun tempBasalAbsolute(absoluteRate: Double, durationInMinutes: Int, enforceNew: Boolean, profile: Profile, callback: Callback?): Boolean { + override fun tempBasalAbsolute(absoluteRate: Double, durationInMinutes: Int, enforceNew: Boolean, profile: Profile, tbrType: PumpSync.TemporaryBasalType, callback: Callback?): Boolean { if (!enforceNew && isRunning(CommandType.TEMPBASAL)) { callback?.result(executingNowError())?.run() return false @@ -289,13 +290,13 @@ open class CommandQueue @Inject constructor( removeAll(CommandType.TEMPBASAL) val rateAfterConstraints = constraintChecker.applyBasalConstraints(Constraint(absoluteRate), profile).value() // add new command to queue - add(CommandTempBasalAbsolute(injector, rateAfterConstraints, durationInMinutes, enforceNew, profile, callback)) + add(CommandTempBasalAbsolute(injector, rateAfterConstraints, durationInMinutes, enforceNew, profile, tbrType, callback)) notifyAboutNewCommand() return true } // returns true if command is queued - override fun tempBasalPercent(percent: Int, durationInMinutes: Int, enforceNew: Boolean, profile: Profile, callback: Callback?): Boolean { + override fun tempBasalPercent(percent: Int, durationInMinutes: Int, enforceNew: Boolean, profile: Profile, tbrType: PumpSync.TemporaryBasalType, callback: Callback?): Boolean { if (!enforceNew && isRunning(CommandType.TEMPBASAL)) { callback?.result(executingNowError())?.run() return false @@ -304,7 +305,7 @@ open class CommandQueue @Inject constructor( removeAll(CommandType.TEMPBASAL) val percentAfterConstraints = constraintChecker.applyBasalPercentConstraints(Constraint(percent), profile).value() // add new command to queue - add(CommandTempBasalPercent(injector, percentAfterConstraints, durationInMinutes, enforceNew, profile, callback)) + add(CommandTempBasalPercent(injector, percentAfterConstraints, durationInMinutes, enforceNew, profile, tbrType, callback)) notifyAboutNewCommand() return true } diff --git a/app/src/main/java/info/nightscout/androidaps/queue/commands/CommandTempBasalAbsolute.kt b/app/src/main/java/info/nightscout/androidaps/queue/commands/CommandTempBasalAbsolute.kt index 488aa3bb7f..21c70509f1 100644 --- a/app/src/main/java/info/nightscout/androidaps/queue/commands/CommandTempBasalAbsolute.kt +++ b/app/src/main/java/info/nightscout/androidaps/queue/commands/CommandTempBasalAbsolute.kt @@ -3,6 +3,7 @@ package info.nightscout.androidaps.queue.commands import dagger.android.HasAndroidInjector import info.nightscout.androidaps.data.Profile import info.nightscout.androidaps.interfaces.ActivePluginProvider +import info.nightscout.androidaps.interfaces.PumpSync import info.nightscout.androidaps.logging.LTag import info.nightscout.androidaps.queue.Callback import javax.inject.Inject @@ -13,13 +14,14 @@ class CommandTempBasalAbsolute( private val durationInMinutes: Int, private val enforceNew: Boolean, private val profile: Profile, + private val tbrType: PumpSync.TemporaryBasalType, callback: Callback? ) : Command(injector, CommandType.TEMPBASAL, callback) { @Inject lateinit var activePlugin: ActivePluginProvider override fun execute() { - val r = activePlugin.activePump.setTempBasalAbsolute(absoluteRate, durationInMinutes, profile, enforceNew) + val r = activePlugin.activePump.setTempBasalAbsolute(absoluteRate, durationInMinutes, profile, enforceNew, tbrType) aapsLogger.debug(LTag.PUMPQUEUE, "Result rate: $absoluteRate durationInMinutes: $durationInMinutes success: ${r.success} enacted: ${r.enacted}") callback?.result(r)?.run() } diff --git a/app/src/main/java/info/nightscout/androidaps/queue/commands/CommandTempBasalPercent.kt b/app/src/main/java/info/nightscout/androidaps/queue/commands/CommandTempBasalPercent.kt index 8bfdbb73b7..1a00c8910e 100644 --- a/app/src/main/java/info/nightscout/androidaps/queue/commands/CommandTempBasalPercent.kt +++ b/app/src/main/java/info/nightscout/androidaps/queue/commands/CommandTempBasalPercent.kt @@ -3,6 +3,7 @@ package info.nightscout.androidaps.queue.commands import dagger.android.HasAndroidInjector import info.nightscout.androidaps.data.Profile import info.nightscout.androidaps.interfaces.ActivePluginProvider +import info.nightscout.androidaps.interfaces.PumpSync import info.nightscout.androidaps.logging.LTag import info.nightscout.androidaps.queue.Callback import javax.inject.Inject @@ -13,13 +14,14 @@ class CommandTempBasalPercent( private val durationInMinutes: Int, private val enforceNew: Boolean, private val profile: Profile, + private val tbrType: PumpSync.TemporaryBasalType, callback: Callback? ) : Command(injector, CommandType.TEMPBASAL, callback) { @Inject lateinit var activePlugin: ActivePluginProvider override fun execute() { - val r = activePlugin.activePump.setTempBasalPercent(percent, durationInMinutes, profile, enforceNew) + val r = activePlugin.activePump.setTempBasalPercent(percent, durationInMinutes, profile, enforceNew, tbrType) aapsLogger.debug(LTag.PUMPQUEUE, "Result percent: $percent durationInMinutes: $durationInMinutes success: ${r.success} enacted: ${r.enacted}") callback?.result(r)?.run() } diff --git a/app/src/main/java/info/nightscout/androidaps/utils/wizard/BolusWizard.kt b/app/src/main/java/info/nightscout/androidaps/utils/wizard/BolusWizard.kt index 4c64b1830a..977511e6a4 100644 --- a/app/src/main/java/info/nightscout/androidaps/utils/wizard/BolusWizard.kt +++ b/app/src/main/java/info/nightscout/androidaps/utils/wizard/BolusWizard.kt @@ -381,7 +381,7 @@ class BolusWizard @Inject constructor( } if (pump.pumpDescription.tempBasalStyle == PumpDescription.ABSOLUTE) { - commandQueue.tempBasalAbsolute(0.0, 120, true, profile, object : Callback() { + commandQueue.tempBasalAbsolute(0.0, 120, true, profile, PumpSync.TemporaryBasalType.NORMAL, object : Callback() { override fun run() { if (!result.success) { ErrorHelperActivity.runAlarm(ctx, result.comment, resourceHelper.gs(R.string.tempbasaldeliveryerror), R.raw.boluserror) @@ -389,7 +389,7 @@ class BolusWizard @Inject constructor( } }) } else { - commandQueue.tempBasalPercent(0, 120, true, profile, object : Callback() { + commandQueue.tempBasalPercent(0, 120, true, profile, PumpSync.TemporaryBasalType.NORMAL, object : Callback() { override fun run() { if (!result.success) { val i = Intent(ctx, ErrorHelperActivity::class.java) diff --git a/combo/src/main/java/info/nightscout/androidaps/plugins/pump/combo/ComboPlugin.java b/combo/src/main/java/info/nightscout/androidaps/plugins/pump/combo/ComboPlugin.java index 82fbbe0340..ae38482ddd 100644 --- a/combo/src/main/java/info/nightscout/androidaps/plugins/pump/combo/ComboPlugin.java +++ b/combo/src/main/java/info/nightscout/androidaps/plugins/pump/combo/ComboPlugin.java @@ -40,6 +40,7 @@ import info.nightscout.androidaps.interfaces.ProfileFunction; import info.nightscout.androidaps.interfaces.PumpDescription; import info.nightscout.androidaps.interfaces.PumpInterface; import info.nightscout.androidaps.interfaces.PumpPluginBase; +import info.nightscout.androidaps.interfaces.PumpSync; import info.nightscout.androidaps.interfaces.TreatmentsInterface; import info.nightscout.androidaps.logging.AAPSLogger; import info.nightscout.androidaps.logging.LTag; @@ -711,7 +712,7 @@ public class ComboPlugin extends PumpPluginBase implements PumpInterface, Constr * the new value (and thus still has the old duration of e.g. 1 min) expires?) */ @NonNull @Override - public PumpEnactResult setTempBasalAbsolute(double absoluteRate, int durationInMinutes, @NonNull Profile profile, boolean force) { + public PumpEnactResult setTempBasalAbsolute(double absoluteRate, int durationInMinutes, @NonNull Profile profile, boolean force, PumpSync.TemporaryBasalType tbrType) { getAapsLogger().debug(LTag.PUMP, "setTempBasalAbsolute called with a rate of " + absoluteRate + " for " + durationInMinutes + " min."); int unroundedPercentage = Double.valueOf(absoluteRate / getBaseBasalRate() * 100).intValue(); int roundedPercentage = (int) (Math.round(absoluteRate / getBaseBasalRate() * 10) * 10); @@ -729,7 +730,7 @@ public class ComboPlugin extends PumpPluginBase implements PumpInterface, Constr * is or isn't running at the moment */ @NonNull @Override - public PumpEnactResult setTempBasalPercent(int percent, int durationInMinutes, @NonNull Profile profile, boolean forceNew) { + public PumpEnactResult setTempBasalPercent(int percent, int durationInMinutes, @NonNull Profile profile, boolean forceNew, PumpSync.TemporaryBasalType tbrType) { return setTempBasalPercent(percent, durationInMinutes); } diff --git a/core/src/main/java/info/nightscout/androidaps/interfaces/CommandQueueProvider.kt b/core/src/main/java/info/nightscout/androidaps/interfaces/CommandQueueProvider.kt index 877dac9809..049ce54013 100644 --- a/core/src/main/java/info/nightscout/androidaps/interfaces/CommandQueueProvider.kt +++ b/core/src/main/java/info/nightscout/androidaps/interfaces/CommandQueueProvider.kt @@ -22,8 +22,8 @@ interface CommandQueueProvider { fun stopPump(callback: Callback?) fun startPump(callback: Callback?) fun setTBROverNotification(callback: Callback?, enable: Boolean) - fun tempBasalAbsolute(absoluteRate: Double, durationInMinutes: Int, enforceNew: Boolean, profile: Profile, callback: Callback?): Boolean - fun tempBasalPercent(percent: Int, durationInMinutes: Int, enforceNew: Boolean, profile: Profile, callback: Callback?): Boolean + fun tempBasalAbsolute(absoluteRate: Double, durationInMinutes: Int, enforceNew: Boolean, profile: Profile, tbrType: PumpSync.TemporaryBasalType, callback: Callback?): Boolean + fun tempBasalPercent(percent: Int, durationInMinutes: Int, enforceNew: Boolean, profile: Profile, tbrType: PumpSync.TemporaryBasalType, callback: Callback?): Boolean fun extendedBolus(insulin: Double, durationInMinutes: Int, callback: Callback?): Boolean fun cancelTempBasal(enforceNew: Boolean, callback: Callback?): Boolean fun cancelExtended(callback: Callback?): Boolean diff --git a/core/src/main/java/info/nightscout/androidaps/interfaces/PumpInterface.kt b/core/src/main/java/info/nightscout/androidaps/interfaces/PumpInterface.kt index 43802833c5..429939b98e 100644 --- a/core/src/main/java/info/nightscout/androidaps/interfaces/PumpInterface.kt +++ b/core/src/main/java/info/nightscout/androidaps/interfaces/PumpInterface.kt @@ -13,7 +13,7 @@ import org.json.JSONObject /** * This interface defines the communication from AAPS-core to pump drivers. - * Pump drivers communicate data changes back to AAPS-core using {@link info.nightscout.androidaps.interfaces.PumpSync}. + * Pump drivers communicate data changes back to AAPS-core using [info.nightscout.androidaps.interfaces.PumpSync]. * * Created by mike on 04.06.2016. */ @@ -46,22 +46,95 @@ interface PumpInterface { * * @param detailedBolusInfo it's the caller's responsibility to ensure the request can be satisfied by the pump, * e.g. DBI will not contain carbs if the pump can't store carbs. + * @return PumpEnactResult */ fun deliverTreatment(detailedBolusInfo: DetailedBolusInfo): PumpEnactResult - fun stopBolusDelivering() - fun setTempBasalAbsolute(absoluteRate: Double, durationInMinutes: Int, profile: Profile, enforceNew: Boolean): PumpEnactResult - fun setTempBasalPercent(percent: Int, durationInMinutes: Int, profile: Profile, enforceNew: Boolean): PumpEnactResult - fun setExtendedBolus(insulin: Double, durationInMinutes: Int): PumpEnactResult - //some pumps might set a very short temp close to 100% as cancelling a temp can be noisy - //when the cancel request is requested by the user (forced), the pump should always do a real cancel + /** + * Stopping of performed bolus requested by user + */ + fun stopBolusDelivering() + + /** + * Request a TRB in absolute units [U/h] + * + * Driver is responsible for conversion to % if absolute rate is not supported by pump + * + * @param absoluteRate rate in U/h + * @param durationInMinutes duration + * @param profile only help for for U/h -> % transformation + * @param enforceNew if true drive should force new TBR (ie. stop current, + * and set new even if the same rate is requested + * @param tbrType tbrType for storing to DB [NORMAL,EMULATED_PUMP_SUSPEND,PUMP_SUSPEND,SUPERBOLUS] + * @return PumpEnactResult.success if TBR set, + * PumpEnactResult.enacted if new TBR set + * (if the same TBR rate is requested && enforceNew == false driver can keep + * running TBR. In this case return will be success = true, enacted = false) + */ + fun setTempBasalAbsolute(absoluteRate: Double, durationInMinutes: Int, profile: Profile, enforceNew: Boolean, tbrType: PumpSync.TemporaryBasalType): PumpEnactResult + + /** + * Request a TRB in % + * + * Driver is responsible for conversion to u/h if % is not supported by pump + * + * @param percent rate in % (100% is equal to not running TBR, 0% is zero temping) + * @param durationInMinutes duration + * @param profile only help for for U/h -> % transformation + * @param enforceNew if true drive should force new TBR (ie. stop current, + * and set new even if the same rate is requested + * @param tbrType tbrType for storing to DB [NORMAL,EMULATED_PUMP_SUSPEND,PUMP_SUSPEND,SUPERBOLUS] + * @return PumpEnactResult.success if TBR set, + * PumpEnactResult.enacted if new TBR set + * (if the same TBR rate is requested && enforceNew == false driver can keep + * running TBR. In this case return will be success = true, enacted = false) + */ + fun setTempBasalPercent(percent: Int, durationInMinutes: Int, profile: Profile, enforceNew: Boolean, tbrType: PumpSync.TemporaryBasalType): PumpEnactResult + + /** + * Cancel current TBR if a TBR is running + * + * some pumps might set a very short temp close to 100% as cancelling a temp can be noisy + * when the cancel request is requested by the user (forced), the pump should always do a real cancel + * + * @param enforceNew if true disable workaround above + * @return PumpEnactResult.success if TBR is canceled + */ fun cancelTempBasal(enforceNew: Boolean): PumpEnactResult + + fun setExtendedBolus(insulin: Double, durationInMinutes: Int): PumpEnactResult fun cancelExtendedBolus(): PumpEnactResult - // Status to be passed to NS + /** + * Status to be passed to NS + * + * This info is displayed when user hover over pump pill in NS + * + * @return JSON with information + */ fun getJSONStatus(profile: Profile, profileName: String, version: String): JSONObject + + /** + * Manufacturer type. Usually defined by used plugin + * + * @return ManufacturerType + */ fun manufacturer(): ManufacturerType + + /** + * Pump model + * + * If new model is covered by driver, model and it's capabilities must be added to [info.nightscout.androidaps.plugins.pump.common.defs.PumpType] + * + * @return PumpType + */ fun model(): PumpType + + /** + * Serial number + * + * Real serial number from device or "unique" generated for paired pump if not possible + */ fun serialNumber(): String // Pump capabilities diff --git a/core/src/main/java/info/nightscout/androidaps/interfaces/PumpSync.kt b/core/src/main/java/info/nightscout/androidaps/interfaces/PumpSync.kt index c76312df87..9a380c2731 100644 --- a/core/src/main/java/info/nightscout/androidaps/interfaces/PumpSync.kt +++ b/core/src/main/java/info/nightscout/androidaps/interfaces/PumpSync.kt @@ -1,10 +1,11 @@ package info.nightscout.androidaps.interfaces import info.nightscout.androidaps.data.DetailedBolusInfo +import info.nightscout.androidaps.database.entities.TemporaryBasal import info.nightscout.androidaps.plugins.pump.common.defs.PumpType /** - * This interface allows pump drivers to push data changes (creation and update of treatments) back to AAPS-core. + * This interface allows pump drivers to push data changes (creation and update of treatments, temporary basals and extended boluses) back to AAPS-core. * * Intended use cases for handling bolus treatments: * @@ -20,6 +21,11 @@ import info.nightscout.androidaps.plugins.pump.common.defs.PumpType * bolus. */ interface PumpSync { + + /* + * BOLUSES & CARBS + */ + /** * Create bolus with temporary id * @@ -42,7 +48,7 @@ interface PumpSync { * @param pumpSerial pump serial number * @return true if new record is created **/ - fun addBolusWithTempId(timestamp: Long, amount: Double, temporaryId: Long, type: DetailedBolusInfo.BolusType, pumpType: PumpType, pumpSerial: String) : Boolean + fun addBolusWithTempId(timestamp: Long, amount: Double, temporaryId: Long, type: DetailedBolusInfo.BolusType, pumpType: PumpType, pumpSerial: String): Boolean /** * Synchronization of boluses with temporary id @@ -66,7 +72,7 @@ interface PumpSync { * @param pumpSerial pump serial number * @return true if record is successfully updated **/ - fun syncBolusWithTempId(timestamp: Long, amount: Double, temporaryId: Long, type: DetailedBolusInfo.BolusType?, pumpId: Long?, pumpType: PumpType, pumpSerial: String) : Boolean + fun syncBolusWithTempId(timestamp: Long, amount: Double, temporaryId: Long, type: DetailedBolusInfo.BolusType?, pumpId: Long?, pumpType: PumpType, pumpSerial: String): Boolean /** * Synchronization of boluses @@ -85,7 +91,7 @@ interface PumpSync { * @param pumpSerial pump serial number * @return true if new record is created **/ - fun syncBolusWithPumpId(timestamp: Long, amount: Double, type: DetailedBolusInfo.BolusType?, pumpId: Long, pumpType: PumpType, pumpSerial: String) : Boolean + fun syncBolusWithPumpId(timestamp: Long, amount: Double, type: DetailedBolusInfo.BolusType?, pumpId: Long, pumpType: PumpType, pumpSerial: String): Boolean /** * Synchronization of carbs @@ -103,7 +109,11 @@ interface PumpSync { * @param pumpSerial pump serial number * @return true if new record is created **/ - fun syncCarbsWithTimestamp(timestamp: Long, amount: Double, pumpId: Long?, pumpType: PumpType, pumpSerial: String) : Boolean + fun syncCarbsWithTimestamp(timestamp: Long, amount: Double, pumpId: Long?, pumpType: PumpType, pumpSerial: String): Boolean + + /* + * THERAPY EVENTS + */ /** * Synchronization of events like CANNULA_CHANGE @@ -139,4 +149,69 @@ interface PumpSync { * @param pumpSerial pump serial number **/ fun insertAnnouncement(error: String, pumpId: Long? = null, pumpType: PumpType, pumpSerial: String) + + /* + * TEMPORARY BASALS + */ + + enum class TemporaryBasalType { + NORMAL, + EMULATED_PUMP_SUSPEND, // Initiated by AAPS as zero TBR + PUMP_SUSPEND, // Initiated on PUMP + SUPERBOLUS; + + fun toDbType(): TemporaryBasal.Type = + when (this) { + NORMAL -> TemporaryBasal.Type.NORMAL + EMULATED_PUMP_SUSPEND -> TemporaryBasal.Type.EMULATED_PUMP_SUSPEND + PUMP_SUSPEND -> TemporaryBasal.Type.PUMP_SUSPEND + SUPERBOLUS -> TemporaryBasal.Type.SUPERBOLUS + } + } + + /** + * Synchronization of temporary basals + * + * Search for combination of pumpId, PumpType, pumpSerial + * + * If exists, timestamp, duration, rate and type (if provided) is updated + * If db record doesn't exist, new record is created. + * If overlap another running TBR, running is cut off + * isValid field is preserved + * + * @param timestamp timestamp of event from pump history + * @param rate TBR rate in U/h or % (value of 100% is equal to no TBR) + * @param duration duration in milliseconds + * @param isAbsolute is TBR in U/h or % ? + * @param type type of TBR, from request sent to the driver + * @param pumpId pump id from history + * @param pumpType pump type like PumpType.ACCU_CHEK_COMBO + * @param pumpSerial pump serial number + * @return true if new record is created + **/ + + fun syncTemporaryBasalWithPumpId(timestamp: Long, rate: Double, duration: Long, isAbsolute: Boolean, type: TemporaryBasalType?, pumpId: Long, pumpType: PumpType, pumpSerial: String): Boolean + + /** + * Synchronization of temporary basals end event + * (for pumps having separate event for end of TBR or not having history) + * (not useful for pump modifying duration in history log) + * + * Search first for a TBR with combination of endPumpId, pumpType, pumpSerial + * if found assume, some running TBR has been already cut off and ignore data. False is returned + * + * Search for running TBR with combination of pumpType, pumpSerial + * + * If exists, + * currently running record is cut off by provided timestamp (ie duration is adjusted) + * endPumpId is stored to running record + * If db record doesn't exist data is ignored and false returned + * + * @param timestamp timestamp of event from pump history + * @param endPumpId pump id of ending event from history + * @param pumpType pump type like PumpType.ACCU_CHEK_COMBO + * @param pumpSerial pump serial number + * @return true if running record is found and ended by changing duration + **/ + fun syncStopTemporaryBasalWithPumpId(timestamp: Long, endPumpId: Long, pumpType: PumpType, pumpSerial: String): Boolean } \ No newline at end of file diff --git a/core/src/main/java/info/nightscout/androidaps/plugins/pump/PumpSyncImplementation.kt b/core/src/main/java/info/nightscout/androidaps/plugins/pump/PumpSyncImplementation.kt index 86c92742ab..039f4a8b1f 100644 --- a/core/src/main/java/info/nightscout/androidaps/plugins/pump/PumpSyncImplementation.kt +++ b/core/src/main/java/info/nightscout/androidaps/plugins/pump/PumpSyncImplementation.kt @@ -5,6 +5,7 @@ import info.nightscout.androidaps.database.AppRepository import info.nightscout.androidaps.database.embedments.InterfaceIDs import info.nightscout.androidaps.database.entities.Bolus import info.nightscout.androidaps.database.entities.Carbs +import info.nightscout.androidaps.database.entities.TemporaryBasal import info.nightscout.androidaps.database.entities.TherapyEvent import info.nightscout.androidaps.database.transactions.* import info.nightscout.androidaps.interfaces.PumpSync @@ -22,7 +23,7 @@ class PumpSyncImplementation @Inject constructor( private val disposable = CompositeDisposable() - override fun addBolusWithTempId(timestamp: Long, amount: Double, temporaryId: Long, type: DetailedBolusInfo.BolusType, pumpType: PumpType, pumpSerial: String) : Boolean { + override fun addBolusWithTempId(timestamp: Long, amount: Double, temporaryId: Long, type: DetailedBolusInfo.BolusType, pumpType: PumpType, pumpSerial: String): Boolean { val bolus = Bolus( timestamp = timestamp, amount = amount, @@ -34,10 +35,10 @@ class PumpSyncImplementation @Inject constructor( ) ) repository.runTransactionForResult(InsertPumpBolusWithTempIdTransaction(bolus)) - .doOnError { aapsLogger.error(LTag.DATABASE, "Error while saving carbs", it) } + .doOnError { aapsLogger.error(LTag.DATABASE, "Error while saving bolus", it) } .blockingGet() .also { result -> - result.inserted.forEach { aapsLogger.debug(LTag.DATABASE, "Inserted carbs $it") } + result.inserted.forEach { aapsLogger.debug(LTag.DATABASE, "Inserted bolus $it") } return result.inserted.size > 0 } } @@ -55,10 +56,10 @@ class PumpSyncImplementation @Inject constructor( ) ) repository.runTransactionForResult(SyncPumpBolusWithTempIdTransaction(bolus, type?.toDBbBolusType())) - .doOnError { aapsLogger.error(LTag.DATABASE, "Error while saving carbs", it) } + .doOnError { aapsLogger.error(LTag.DATABASE, "Error while saving bolus", it) } .blockingGet() .also { result -> - result.updated.forEach { aapsLogger.debug(LTag.DATABASE, "Updated carbs $it") } + result.updated.forEach { aapsLogger.debug(LTag.DATABASE, "Updated bolus $it") } return result.updated.size > 0 } } @@ -75,11 +76,11 @@ class PumpSyncImplementation @Inject constructor( ) ) repository.runTransactionForResult(SyncPumpBolusTransaction(bolus, type?.toDBbBolusType())) - .doOnError { aapsLogger.error(LTag.DATABASE, "Error while saving carbs", it) } + .doOnError { aapsLogger.error(LTag.DATABASE, "Error while saving bolus", it) } .blockingGet() .also { result -> - result.inserted.forEach { aapsLogger.debug(LTag.DATABASE, "Inserted carbs $it") } - result.updated.forEach { aapsLogger.debug(LTag.DATABASE, "Updated carbs $it") } + result.inserted.forEach { aapsLogger.debug(LTag.DATABASE, "Inserted bolus $it") } + result.updated.forEach { aapsLogger.debug(LTag.DATABASE, "Updated bolus $it") } return result.inserted.size > 0 } } @@ -130,4 +131,42 @@ class PumpSyncImplementation @Inject constructor( .subscribe() } + /* + * TEMPORARY BASALS + */ + + override fun syncTemporaryBasalWithPumpId(timestamp: Long, rate: Double, duration: Long, isAbsolute: Boolean, type: PumpSync.TemporaryBasalType?, pumpId: Long, pumpType: PumpType, pumpSerial: String): Boolean { + val temporaryBasal = TemporaryBasal( + timestamp = timestamp, + rate = rate, + duration = duration, + type = type?.toDbType() ?: TemporaryBasal.Type.NORMAL, + isAbsolute = isAbsolute, + interfaceIDs_backing = InterfaceIDs( + pumpId = pumpId, + pumpType = pumpType.toDbPumpType(), + pumpSerial = pumpSerial + ) + ) + repository.runTransactionForResult(SyncPumpTemporaryBasalTransaction(temporaryBasal, type?.toDbType())) + .doOnError { aapsLogger.error(LTag.DATABASE, "Error while temporary basal", it) } + .blockingGet() + .also { result -> + result.inserted.forEach { aapsLogger.debug(LTag.DATABASE, "Inserted temporary basal $it") } + result.updated.forEach { aapsLogger.debug(LTag.DATABASE, "Updated temporary basal $it") } + return result.inserted.size > 0 + } + } + + override fun syncStopTemporaryBasalWithPumpId(timestamp: Long, endPumpId: Long, pumpType: PumpType, pumpSerial: String): Boolean { + repository.runTransactionForResult(SyncPumpCancelTemporaryBasalIfAnyTransaction(timestamp, endPumpId, pumpType.toDbPumpType(), pumpSerial)) + .doOnError { aapsLogger.error(LTag.DATABASE, "Error while saving temporary basal", it) } + .blockingGet() + .also { result -> + result.updated.forEach { + aapsLogger.debug(LTag.DATABASE, "Updated temporary basal $it") + } + return result.updated.size > 0 + } + } } \ No newline at end of file diff --git a/danar/src/main/java/info/nightscout/androidaps/danaRKorean/DanaRKoreanPlugin.java b/danar/src/main/java/info/nightscout/androidaps/danaRKorean/DanaRKoreanPlugin.java index 0a15328ea3..acb9ea580c 100644 --- a/danar/src/main/java/info/nightscout/androidaps/danaRKorean/DanaRKoreanPlugin.java +++ b/danar/src/main/java/info/nightscout/androidaps/danaRKorean/DanaRKoreanPlugin.java @@ -213,7 +213,7 @@ public class DanaRKoreanPlugin extends AbstractDanaRPlugin { // This is called from APS @NonNull @Override - public PumpEnactResult setTempBasalAbsolute(double absoluteRate, int durationInMinutes, @NonNull Profile profile, boolean enforceNew) { + public PumpEnactResult setTempBasalAbsolute(double absoluteRate, int durationInMinutes, @NonNull Profile profile, boolean enforceNew, @NonNull PumpSync.TemporaryBasalType tbrType) { // Recheck pump status if older than 30 min //This should not be needed while using queue because connection should be done before calling this PumpEnactResult result = new PumpEnactResult(getInjector()); @@ -281,7 +281,7 @@ public class DanaRKoreanPlugin extends AbstractDanaRPlugin { } // Convert duration from minutes to hours aapsLogger.debug(LTag.PUMP, "setTempBasalAbsolute: Setting temp basal " + percentRate + "% for " + durationInMinutes + " minutes (doLowTemp || doHighTemp)"); - return setTempBasalPercent(percentRate, durationInMinutes, profile, false); + return setTempBasalPercent(percentRate, durationInMinutes, profile, false, tbrType); } if (doExtendedTemp) { // Check if some temp is already in progress diff --git a/danar/src/main/java/info/nightscout/androidaps/danaRv2/DanaRv2Plugin.java b/danar/src/main/java/info/nightscout/androidaps/danaRv2/DanaRv2Plugin.java index 9f1fb6f616..25d4af08f5 100644 --- a/danar/src/main/java/info/nightscout/androidaps/danaRv2/DanaRv2Plugin.java +++ b/danar/src/main/java/info/nightscout/androidaps/danaRv2/DanaRv2Plugin.java @@ -218,7 +218,7 @@ public class DanaRv2Plugin extends AbstractDanaRPlugin { // This is called from APS @NonNull @Override - public PumpEnactResult setTempBasalAbsolute(double absoluteRate, int durationInMinutes, @NonNull Profile profile, boolean enforceNew) { + public PumpEnactResult setTempBasalAbsolute(double absoluteRate, int durationInMinutes, @NonNull Profile profile, boolean enforceNew, @NonNull PumpSync.TemporaryBasalType tbrType) { PumpEnactResult result = new PumpEnactResult(getInjector()); @@ -262,7 +262,7 @@ public class DanaRv2Plugin extends AbstractDanaRPlugin { // Convert duration from minutes to hours aapsLogger.debug(LTag.PUMP, "setTempBasalAbsolute: Setting temp basal " + percentRate + "% for " + durationInMinutes + " minutes (doLowTemp || doHighTemp)"); if (percentRate == 0 && durationInMinutes > 30) { - result = setTempBasalPercent(percentRate, durationInMinutes, profile, enforceNew); + result = setTempBasalPercent(percentRate, durationInMinutes, profile, enforceNew, tbrType); } else { // use special APS temp basal call ... 100+/15min .... 100-/30min result = setHighTempBasalPercent(percentRate, durationInMinutes); @@ -281,7 +281,7 @@ public class DanaRv2Plugin extends AbstractDanaRPlugin { } @NonNull @Override - public PumpEnactResult setTempBasalPercent(int percent, int durationInMinutes, @NonNull Profile profile, boolean enforceNew) { + public PumpEnactResult setTempBasalPercent(int percent, int durationInMinutes, @NonNull Profile profile, boolean enforceNew, @NonNull PumpSync.TemporaryBasalType tbrType) { DanaPump pump = danaPump; PumpEnactResult result = new PumpEnactResult(getInjector()); percent = constraintChecker.applyBasalPercentConstraints(new Constraint<>(percent), profile).value(); diff --git a/danar/src/main/java/info/nightscout/androidaps/danar/AbstractDanaRPlugin.java b/danar/src/main/java/info/nightscout/androidaps/danar/AbstractDanaRPlugin.java index 0927406653..971bb33221 100644 --- a/danar/src/main/java/info/nightscout/androidaps/danar/AbstractDanaRPlugin.java +++ b/danar/src/main/java/info/nightscout/androidaps/danar/AbstractDanaRPlugin.java @@ -218,7 +218,7 @@ public abstract class AbstractDanaRPlugin extends PumpPluginBase implements Pump } @NonNull @Override - public PumpEnactResult setTempBasalPercent(int percent, int durationInMinutes, @NonNull Profile profile, boolean enforceNew) { + public PumpEnactResult setTempBasalPercent(int percent, int durationInMinutes, @NonNull Profile profile, boolean enforceNew, @NonNull PumpSync.TemporaryBasalType tbrType) { DanaPump pump = danaPump; PumpEnactResult result = new PumpEnactResult(getInjector()); percent = constraintChecker.applyBasalPercentConstraints(new Constraint<>(percent), profile).value(); diff --git a/danar/src/main/java/info/nightscout/androidaps/danar/DanaRPlugin.java b/danar/src/main/java/info/nightscout/androidaps/danar/DanaRPlugin.java index 5d52f0cd2d..075c87f4e2 100644 --- a/danar/src/main/java/info/nightscout/androidaps/danar/DanaRPlugin.java +++ b/danar/src/main/java/info/nightscout/androidaps/danar/DanaRPlugin.java @@ -205,7 +205,7 @@ public class DanaRPlugin extends AbstractDanaRPlugin { // This is called from APS @NonNull @Override - public PumpEnactResult setTempBasalAbsolute(double absoluteRate, int durationInMinutes, @NonNull Profile profile, boolean enforceNew) { + public PumpEnactResult setTempBasalAbsolute(double absoluteRate, int durationInMinutes, @NonNull Profile profile, boolean enforceNew, @NonNull PumpSync.TemporaryBasalType tbrType) { // Recheck pump status if older than 30 min //This should not be needed while using queue because connection should be done before calling this PumpEnactResult result = new PumpEnactResult(getInjector()); @@ -273,7 +273,7 @@ public class DanaRPlugin extends AbstractDanaRPlugin { } // Convert duration from minutes to hours aapsLogger.debug(LTag.PUMP, "setTempBasalAbsolute: Setting temp basal " + percentRate + "% for " + durationInMinutes + " minutes (doLowTemp || doHighTemp)"); - return setTempBasalPercent(percentRate, durationInMinutes, profile, false); + return setTempBasalPercent(percentRate, durationInMinutes, profile, false, tbrType); } if (doExtendedTemp) { // Check if some temp is already in progress diff --git a/danars/src/main/java/info/nightscout/androidaps/danars/DanaRSPlugin.kt b/danars/src/main/java/info/nightscout/androidaps/danars/DanaRSPlugin.kt index d358e24216..70b21f9c84 100644 --- a/danars/src/main/java/info/nightscout/androidaps/danars/DanaRSPlugin.kt +++ b/danars/src/main/java/info/nightscout/androidaps/danars/DanaRSPlugin.kt @@ -278,7 +278,7 @@ class DanaRSPlugin @Inject constructor( if (carbTime == 0) carbTime-- // better set 1 min back to prevents clash with insulin detailedBolusInfo.carbTime = 0 detailedBolusInfoStorage.add(detailedBolusInfo) // will be picked up on reading history - val t = EventOverviewBolusProgress.Treatment(0.0, 0, detailedBolusInfo.bolusType == DetailedBolusInfo.BolusType.SMB); + val t = EventOverviewBolusProgress.Treatment(0.0, 0, detailedBolusInfo.bolusType == DetailedBolusInfo.BolusType.SMB) var connectionOK = false if (detailedBolusInfo.insulin > 0 || carbs > 0) connectionOK = danaRSService?.bolus(detailedBolusInfo.insulin, carbs.toInt(), DateUtil.now() + T.mins(carbTime.toLong()).msecs(), t) ?: false @@ -315,7 +315,7 @@ class DanaRSPlugin @Inject constructor( // This is called from APS @Synchronized - override fun setTempBasalAbsolute(absoluteRate: Double, durationInMinutes: Int, profile: Profile, enforceNew: Boolean): PumpEnactResult { + override fun setTempBasalAbsolute(absoluteRate: Double, durationInMinutes: Int, profile: Profile, enforceNew: Boolean, tbrType: PumpSync.TemporaryBasalType): PumpEnactResult { var result = PumpEnactResult(injector) val absoluteAfterConstrain = constraintChecker.applyBasalConstraints(Constraint(absoluteRate), profile).value() val doTempOff = baseBasalRate - absoluteAfterConstrain == 0.0 @@ -367,7 +367,7 @@ class DanaRSPlugin @Inject constructor( // Convert duration from minutes to hours aapsLogger.debug(LTag.PUMP, "setTempBasalAbsolute: Setting temp basal $percentRate% for $durationInMinutes minutes (doLowTemp || doHighTemp)") result = if (percentRate == 0 && durationInMinutes > 30) { - setTempBasalPercent(percentRate, durationInMinutes, profile, enforceNew) + setTempBasalPercent(percentRate, durationInMinutes, profile, enforceNew, tbrType) } else { // use special APS temp basal call ... 100+/15min .... 100-/30min setHighTempBasalPercent(percentRate) @@ -387,7 +387,7 @@ class DanaRSPlugin @Inject constructor( } @Synchronized - override fun setTempBasalPercent(percent: Int, durationInMinutes: Int, profile: Profile, enforceNew: Boolean): PumpEnactResult { + override fun setTempBasalPercent(percent: Int, durationInMinutes: Int, profile: Profile, enforceNew: Boolean, tbrType: PumpSync.TemporaryBasalType): PumpEnactResult { val result = PumpEnactResult(injector) var percentAfterConstraint = constraintChecker.applyBasalPercentConstraints(Constraint(percent), profile).value() if (percentAfterConstraint < 0) { diff --git a/database/src/main/java/info/nightscout/androidaps/database/daos/TemporaryBasalDao.kt b/database/src/main/java/info/nightscout/androidaps/database/daos/TemporaryBasalDao.kt index b2b88a1c7a..bc01def71b 100644 --- a/database/src/main/java/info/nightscout/androidaps/database/daos/TemporaryBasalDao.kt +++ b/database/src/main/java/info/nightscout/androidaps/database/daos/TemporaryBasalDao.kt @@ -5,7 +5,6 @@ import androidx.room.Query import info.nightscout.androidaps.database.TABLE_TEMPORARY_BASALS import info.nightscout.androidaps.database.embedments.InterfaceIDs import info.nightscout.androidaps.database.entities.TemporaryBasal -import io.reactivex.Flowable import io.reactivex.Maybe import io.reactivex.Single @@ -18,4 +17,43 @@ internal interface TemporaryBasalDao : TraceableDao { @Query("DELETE FROM $TABLE_TEMPORARY_BASALS") override fun deleteAllEntries() + + @Query("SELECT * FROM $TABLE_TEMPORARY_BASALS WHERE pumpId = :pumpId AND pumpType = :pumpType AND pumpSerial = :pumpSerial AND referenceId IS NULL") + fun findByPumpIds(pumpId: Long, pumpType: InterfaceIDs.PumpType, pumpSerial: String): TemporaryBasal? + + @Query("SELECT * FROM $TABLE_TEMPORARY_BASALS WHERE endPumpId = :endPumpId AND pumpType = :pumpType AND pumpSerial = :pumpSerial AND referenceId IS NULL") + fun findByPumpEndIds(endPumpId: Long, pumpType: InterfaceIDs.PumpType, pumpSerial: String): TemporaryBasal? + + @Query("SELECT * FROM $TABLE_TEMPORARY_BASALS WHERE nightscoutId = :nsId AND referenceId IS NULL") + fun findByNSId(nsId: String): TemporaryBasal? + + @Query("SELECT * FROM $TABLE_TEMPORARY_BASALS WHERE timestamp <= :timestamp AND (timestamp + duration) > :timestamp AND pumpType = :pumpType AND pumpSerial = :pumpSerial AND referenceId IS NULL AND isValid = 1 ORDER BY timestamp DESC LIMIT 1") + fun getTemporaryBasalActiveAt(timestamp: Long, pumpType: InterfaceIDs.PumpType, pumpSerial: String): Maybe + + @Query("SELECT * FROM $TABLE_TEMPORARY_BASALS WHERE timestamp <= :timestamp AND (timestamp + duration) > :timestamp AND referenceId IS NULL AND isValid = 1 ORDER BY timestamp DESC LIMIT 1") + fun getTemporaryBasalActiveAt(timestamp: Long): Maybe + + @Query("SELECT * FROM $TABLE_TEMPORARY_BASALS WHERE timestamp >= :timestamp AND isValid = 1 AND referenceId IS NULL ORDER BY timestamp ASC") + fun getTemporaryBasalDataFromTime(timestamp: Long): Single> + + @Query("SELECT * FROM $TABLE_TEMPORARY_BASALS WHERE timestamp >= :timestamp AND referenceId IS NULL ORDER BY timestamp ASC") + fun getTemporaryBasalDataIncludingInvalidFromTime(timestamp: Long): Single> + + @Query("SELECT * FROM $TABLE_TEMPORARY_BASALS WHERE isValid = 1 AND referenceId IS NULL ORDER BY timestamp ASC") + fun getTemporaryBasalData(): Single> + + @Query("SELECT * FROM $TABLE_TEMPORARY_BASALS WHERE referenceId = :id ORDER BY id DESC LIMIT 1") + fun getLastHistoryRecord(id: Long): TemporaryBasal? + + // This query will be used with v3 to get all changed records + @Query("SELECT * FROM $TABLE_TEMPORARY_BASALS WHERE id > :id AND referenceId IS NULL OR id IN (SELECT DISTINCT referenceId FROM $TABLE_TEMPORARY_BASALS WHERE id > :id) ORDER BY id ASC") + fun getModifiedFrom(id: Long): Single> + + // for WS we need 1 record only + @Query("SELECT * FROM $TABLE_TEMPORARY_BASALS WHERE id > :id ORDER BY id ASC limit 1") + fun getNextModifiedOrNewAfter(id: Long): Maybe + + @Query("SELECT * FROM $TABLE_TEMPORARY_BASALS WHERE id = :referenceId") + fun getCurrentFromHistoric(referenceId: Long): Maybe + } \ No newline at end of file diff --git a/database/src/main/java/info/nightscout/androidaps/database/entities/TemporaryBasal.kt b/database/src/main/java/info/nightscout/androidaps/database/entities/TemporaryBasal.kt index 01f034ae86..53be33f22f 100644 --- a/database/src/main/java/info/nightscout/androidaps/database/entities/TemporaryBasal.kt +++ b/database/src/main/java/info/nightscout/androidaps/database/entities/TemporaryBasal.kt @@ -22,6 +22,7 @@ data class TemporaryBasal( override var referenceId: Long? = null, @Embedded override var interfaceIDs_backing: InterfaceIDs? = InterfaceIDs(), + var endPumpId: Long? = null, // Some pumps provide separate start and end events in history. Even event pump id can be stored here override var timestamp: Long, override var utcOffset: Long = TimeZone.getDefault().getOffset(timestamp).toLong(), var type: Type, diff --git a/database/src/main/java/info/nightscout/androidaps/database/transactions/SyncPumpCancelTemporaryBasalIfAnyTransaction.kt b/database/src/main/java/info/nightscout/androidaps/database/transactions/SyncPumpCancelTemporaryBasalIfAnyTransaction.kt new file mode 100644 index 0000000000..82cb7fb304 --- /dev/null +++ b/database/src/main/java/info/nightscout/androidaps/database/transactions/SyncPumpCancelTemporaryBasalIfAnyTransaction.kt @@ -0,0 +1,30 @@ +package info.nightscout.androidaps.database.transactions + +import info.nightscout.androidaps.database.embedments.InterfaceIDs +import info.nightscout.androidaps.database.entities.TemporaryBasal +import info.nightscout.androidaps.database.interfaces.end + +class SyncPumpCancelTemporaryBasalIfAnyTransaction( + private val timestamp: Long, private val endPumpId: Long, private val pumpType: InterfaceIDs.PumpType, private val pumpSerial: String +) : Transaction() { + + override fun run(): TransactionResult { + val result = TransactionResult() + val existing = database.temporaryBasalDao.findByPumpEndIds(endPumpId, pumpType, pumpSerial) + if (existing != null) // assume TBR has been cut already + return result + val current = database.temporaryBasalDao.getTemporaryBasalActiveAt(timestamp, pumpType, pumpSerial).blockingGet() + if (current != null) { + current.end = timestamp + current.endPumpId = endPumpId + database.temporaryBasalDao.updateExistingEntry(current) + result.updated.add(current) + } + return result + } + + class TransactionResult { + + val updated = mutableListOf() + } +} \ No newline at end of file diff --git a/database/src/main/java/info/nightscout/androidaps/database/transactions/SyncPumpTemporaryBasalTransaction.kt b/database/src/main/java/info/nightscout/androidaps/database/transactions/SyncPumpTemporaryBasalTransaction.kt new file mode 100644 index 0000000000..5949a79653 --- /dev/null +++ b/database/src/main/java/info/nightscout/androidaps/database/transactions/SyncPumpTemporaryBasalTransaction.kt @@ -0,0 +1,44 @@ +package info.nightscout.androidaps.database.transactions + +import info.nightscout.androidaps.database.entities.TemporaryBasal +import info.nightscout.androidaps.database.interfaces.end + +/** + * Creates or updates the Temporary basal from pump synchronization + */ +class SyncPumpTemporaryBasalTransaction( + private val temporaryBasal: TemporaryBasal, + private val type: TemporaryBasal.Type? // extra parameter because field is not nullable in TemporaryBasal.class +) : Transaction() { + + override fun run(): TransactionResult { + temporaryBasal.interfaceIDs.pumpId ?: temporaryBasal.interfaceIDs.pumpType ?: temporaryBasal.interfaceIDs.pumpSerial ?: + throw IllegalStateException("Some pump ID is null") + val result = TransactionResult() + val current = database.temporaryBasalDao.findByPumpIds(temporaryBasal.interfaceIDs.pumpId!!, temporaryBasal.interfaceIDs.pumpType!!, temporaryBasal.interfaceIDs.pumpSerial!!) + if (current != null) { + current.timestamp = temporaryBasal.timestamp + current.rate = temporaryBasal.rate + current.duration = temporaryBasal.duration + current.type = type ?: current.type + database.temporaryBasalDao.updateExistingEntry(current) + result.updated.add(current) + } else { + val running = database.temporaryBasalDao.getTemporaryBasalActiveAt(temporaryBasal.timestamp, temporaryBasal.interfaceIDs.pumpType!!, temporaryBasal.interfaceIDs.pumpSerial!!).blockingGet() + if (running != null) { + running.end = temporaryBasal.timestamp + database.temporaryBasalDao.updateExistingEntry(running) + result.updated.add(running) + } + database.temporaryBasalDao.insertNewEntry(temporaryBasal) + result.inserted.add(temporaryBasal) + } + return result + } + + class TransactionResult { + + val inserted = mutableListOf() + val updated = mutableListOf() + } +} \ No newline at end of file diff --git a/insight/src/main/java/info/nightscout/androidaps/plugins/pump/insight/LocalInsightPlugin.java b/insight/src/main/java/info/nightscout/androidaps/plugins/pump/insight/LocalInsightPlugin.java index db7c41121d..923bc8bcd5 100644 --- a/insight/src/main/java/info/nightscout/androidaps/plugins/pump/insight/LocalInsightPlugin.java +++ b/insight/src/main/java/info/nightscout/androidaps/plugins/pump/insight/LocalInsightPlugin.java @@ -694,7 +694,7 @@ public class LocalInsightPlugin extends PumpPluginBase implements PumpInterface, } @NonNull @Override - public PumpEnactResult setTempBasalAbsolute(double absoluteRate, int durationInMinutes, @NonNull Profile profile, boolean enforceNew) { + public PumpEnactResult setTempBasalAbsolute(double absoluteRate, int durationInMinutes, @NonNull Profile profile, boolean enforceNew, @NonNull PumpSync.TemporaryBasalType tbrType) { PumpEnactResult result = new PumpEnactResult(getInjector()); if (activeBasalRate == null) return result; if (activeBasalRate.getActiveBasalRate() == 0) return result; @@ -722,13 +722,13 @@ public class LocalInsightPlugin extends PumpPluginBase implements PumpInterface, result.comment(cancelTBRResult.getComment()); } } else { - return setTempBasalPercent((int) Math.round(percent), durationInMinutes, profile, enforceNew); + return setTempBasalPercent((int) Math.round(percent), durationInMinutes, profile, enforceNew, tbrType); } } else { result.comment(cancelEBResult.getComment()); } } else { - return setTempBasalPercent((int) Math.round(percent), durationInMinutes, profile, enforceNew); + return setTempBasalPercent((int) Math.round(percent), durationInMinutes, profile, enforceNew, tbrType); } try { fetchStatus(); @@ -744,7 +744,7 @@ public class LocalInsightPlugin extends PumpPluginBase implements PumpInterface, } @NonNull @Override - public PumpEnactResult setTempBasalPercent(int percent, int durationInMinutes, @NonNull Profile profile, boolean enforceNew) { + public PumpEnactResult setTempBasalPercent(int percent, int durationInMinutes, @NonNull Profile profile, boolean enforceNew, @NonNull PumpSync.TemporaryBasalType tbrType) { PumpEnactResult result = new PumpEnactResult(getInjector()); percent = (int) Math.round(((double) percent) / 10d) * 10; if (percent == 100) return cancelTempBasal(true); diff --git a/medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/common/PumpPluginAbstract.java b/medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/common/PumpPluginAbstract.java index b6bc68a47a..5195da9e36 100644 --- a/medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/common/PumpPluginAbstract.java +++ b/medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/common/PumpPluginAbstract.java @@ -27,6 +27,7 @@ import info.nightscout.androidaps.interfaces.PluginDescription; import info.nightscout.androidaps.interfaces.PumpDescription; import info.nightscout.androidaps.interfaces.PumpInterface; import info.nightscout.androidaps.interfaces.PumpPluginBase; +import info.nightscout.androidaps.interfaces.PumpSync; import info.nightscout.androidaps.logging.AAPSLogger; import info.nightscout.androidaps.logging.LTag; import info.nightscout.androidaps.plugins.bus.RxBusWrapper; @@ -247,14 +248,14 @@ public abstract class PumpPluginAbstract extends PumpPluginBase implements PumpI @NonNull @Override - public PumpEnactResult setTempBasalAbsolute(double absoluteRate, int durationInMinutes, @NonNull Profile profile, boolean enforceNew) { + public PumpEnactResult setTempBasalAbsolute(double absoluteRate, int durationInMinutes, @NonNull Profile profile, boolean enforceNew, @NonNull PumpSync.TemporaryBasalType tbrType) { aapsLogger.debug(LTag.PUMP, "setTempBasalAbsolute [PumpPluginAbstract] - Not implemented."); return getOperationNotSupportedWithCustomText(R.string.pump_operation_not_supported_by_pump_driver); } @NonNull @Override - public PumpEnactResult setTempBasalPercent(int percent, int durationInMinutes, @NonNull Profile profile, boolean enforceNew) { + public PumpEnactResult setTempBasalPercent(int percent, int durationInMinutes, @NonNull Profile profile, boolean enforceNew, @NonNull PumpSync.TemporaryBasalType tbrType) { aapsLogger.debug(LTag.PUMP, "setTempBasalPercent [PumpPluginAbstract] - Not implemented."); return getOperationNotSupportedWithCustomText(R.string.pump_operation_not_supported_by_pump_driver); } diff --git a/medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/MedtronicPumpPlugin.java b/medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/MedtronicPumpPlugin.java index 48cdc84322..8fb40886f5 100644 --- a/medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/MedtronicPumpPlugin.java +++ b/medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/MedtronicPumpPlugin.java @@ -40,6 +40,7 @@ import info.nightscout.androidaps.interfaces.CommandQueueProvider; import info.nightscout.androidaps.interfaces.PluginDescription; import info.nightscout.androidaps.interfaces.PluginType; import info.nightscout.androidaps.interfaces.PumpInterface; +import info.nightscout.androidaps.interfaces.PumpSync; import info.nightscout.androidaps.logging.AAPSLogger; import info.nightscout.androidaps.logging.LTag; import info.nightscout.androidaps.plugins.bus.RxBusWrapper; @@ -952,8 +953,7 @@ public class MedtronicPumpPlugin extends PumpPluginAbstract implements PumpInter // if enforceNew===true current temp basal is canceled and new TBR set (duration is prolonged), // if false and the same rate is requested enacted=false and success=true is returned and TBR is not changed @NonNull @Override - public PumpEnactResult setTempBasalAbsolute(double absoluteRate, int durationInMinutes, Profile profile, - boolean enforceNew) { + public PumpEnactResult setTempBasalAbsolute(double absoluteRate, int durationInMinutes, Profile profile, boolean enforceNew, @NonNull PumpSync.TemporaryBasalType tbrType) { setRefreshButtonEnabled(false); @@ -1064,14 +1064,14 @@ public class MedtronicPumpPlugin extends PumpPluginAbstract implements PumpInter @NonNull @Override - public PumpEnactResult setTempBasalPercent(int percent, int durationInMinutes, @NonNull Profile profile, boolean enforceNew) { + public PumpEnactResult setTempBasalPercent(int percent, int durationInMinutes, @NonNull Profile profile, boolean enforceNew, @NonNull PumpSync.TemporaryBasalType tbrType) { if (percent == 0) { - return setTempBasalAbsolute(0.0d, durationInMinutes, profile, enforceNew); + return setTempBasalAbsolute(0.0d, durationInMinutes, profile, enforceNew, tbrType); } else { double absoluteValue = profile.getBasal() * (percent / 100.0d); absoluteValue = pumpDescription.getPumpType().determineCorrectBasalSize(absoluteValue); aapsLogger.warn(LTag.PUMP, "setTempBasalPercent [MedtronicPumpPlugin] - You are trying to use setTempBasalPercent with percent other then 0% (" + percent + "). This will start setTempBasalAbsolute, with calculated value (" + absoluteValue + "). Result might not be 100% correct."); - return setTempBasalAbsolute(absoluteValue, durationInMinutes, profile, enforceNew); + return setTempBasalAbsolute(absoluteValue, durationInMinutes, profile, enforceNew, tbrType); } } diff --git a/omnipod-dash/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/dash/OmnipodDashPumpPlugin.java b/omnipod-dash/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/dash/OmnipodDashPumpPlugin.java index e21daf9899..4591a4b69e 100644 --- a/omnipod-dash/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/dash/OmnipodDashPumpPlugin.java +++ b/omnipod-dash/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/dash/OmnipodDashPumpPlugin.java @@ -1,5 +1,6 @@ package info.nightscout.androidaps.plugins.pump.omnipod.dash; +import androidx.annotation.NonNull; import androidx.annotation.Nullable; import org.jetbrains.annotations.NotNull; @@ -21,6 +22,7 @@ import info.nightscout.androidaps.interfaces.PluginType; import info.nightscout.androidaps.interfaces.PumpDescription; import info.nightscout.androidaps.interfaces.PumpInterface; import info.nightscout.androidaps.interfaces.PumpPluginBase; +import info.nightscout.androidaps.interfaces.PumpSync; import info.nightscout.androidaps.logging.AAPSLogger; import info.nightscout.androidaps.logging.LTag; import info.nightscout.androidaps.plugins.common.ManufacturerType; @@ -131,11 +133,11 @@ public class OmnipodDashPumpPlugin extends PumpPluginBase implements PumpInterfa } - @NotNull @Override public PumpEnactResult setTempBasalAbsolute(double absoluteRate, int durationInMinutes, @NotNull Profile profile, boolean enforceNew) { + @NotNull @Override public PumpEnactResult setTempBasalAbsolute(double absoluteRate, int durationInMinutes, @NotNull Profile profile, boolean enforceNew, @NonNull PumpSync.TemporaryBasalType tbrType) { return null; } - @NotNull @Override public PumpEnactResult setTempBasalPercent(int percent, int durationInMinutes, @NotNull Profile profile, boolean enforceNew) { + @NotNull @Override public PumpEnactResult setTempBasalPercent(int percent, int durationInMinutes, @NotNull Profile profile, boolean enforceNew, @NonNull PumpSync.TemporaryBasalType tbrType) { return null; } diff --git a/omnipod-eros/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/eros/OmnipodErosPumpPlugin.java b/omnipod-eros/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/eros/OmnipodErosPumpPlugin.java index 76e8237589..91959c5a7b 100644 --- a/omnipod-eros/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/eros/OmnipodErosPumpPlugin.java +++ b/omnipod-eros/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/eros/OmnipodErosPumpPlugin.java @@ -660,7 +660,7 @@ public class OmnipodErosPumpPlugin extends PumpPluginBase implements PumpInterfa // if false and the same rate is requested enacted=false and success=true is returned and TBR is not changed @Override @NonNull - public PumpEnactResult setTempBasalAbsolute(double absoluteRate, int durationInMinutes, @NonNull Profile profile, boolean enforceNew) { + public PumpEnactResult setTempBasalAbsolute(double absoluteRate, int durationInMinutes, @NonNull Profile profile, boolean enforceNew, @NonNull PumpSync.TemporaryBasalType tbrType) { aapsLogger.info(LTag.PUMP, "setTempBasalAbsolute: rate: {}, duration={}", absoluteRate, durationInMinutes); if (durationInMinutes <= 0 || durationInMinutes % BASAL_STEP_DURATION.getStandardMinutes() != 0) { @@ -1026,14 +1026,14 @@ public class OmnipodErosPumpPlugin extends PumpPluginBase implements PumpInterfa aapsLogger.debug(LTag.PUMP, "stopConnecting [PumpPluginAbstract] - default (empty) implementation."); } - @NonNull @Override public PumpEnactResult setTempBasalPercent(int percent, int durationInMinutes, @NonNull Profile profile, boolean enforceNew) { + @NonNull @Override public PumpEnactResult setTempBasalPercent(int percent, int durationInMinutes, @NonNull Profile profile, boolean enforceNew, @NonNull PumpSync.TemporaryBasalType tbrType) { if (percent == 0) { - return setTempBasalAbsolute(0.0d, durationInMinutes, profile, enforceNew); + return setTempBasalAbsolute(0.0d, durationInMinutes, profile, enforceNew, tbrType); } else { double absoluteValue = profile.getBasal() * (percent / 100.0d); absoluteValue = pumpDescription.getPumpType().determineCorrectBasalSize(absoluteValue); aapsLogger.warn(LTag.PUMP, "setTempBasalPercent [OmnipodPumpPlugin] - You are trying to use setTempBasalPercent with percent other then 0% (" + percent + "). This will start setTempBasalAbsolute, with calculated value (" + absoluteValue + "). Result might not be 100% correct."); - return setTempBasalAbsolute(absoluteValue, durationInMinutes, profile, enforceNew); + return setTempBasalAbsolute(absoluteValue, durationInMinutes, profile, enforceNew, tbrType); } }