update overview fragment. set basal if pod is not activated
This commit is contained in:
parent
fa846bdd65
commit
1910ac7b60
6 changed files with 360 additions and 127 deletions
|
@ -39,11 +39,9 @@ import info.nightscout.androidaps.utils.resources.ResourceHelper
|
|||
import info.nightscout.androidaps.utils.rx.AapsSchedulers
|
||||
import info.nightscout.androidaps.utils.sharedPreferences.SP
|
||||
import io.reactivex.Completable
|
||||
import io.reactivex.Observable
|
||||
import io.reactivex.Single
|
||||
import org.json.JSONObject
|
||||
import java.util.*
|
||||
import java.util.concurrent.TimeUnit
|
||||
import javax.inject.Inject
|
||||
import javax.inject.Singleton
|
||||
import kotlin.math.ceil
|
||||
|
@ -68,6 +66,7 @@ class OmnipodDashPumpPlugin @Inject constructor(
|
|||
resourceHelper: ResourceHelper,
|
||||
commandQueue: CommandQueueProvider
|
||||
) : PumpPluginBase(pluginDescription, injector, aapsLogger, resourceHelper, commandQueue), Pump {
|
||||
@Volatile var bolusCanceled = false
|
||||
|
||||
companion object {
|
||||
|
||||
|
@ -128,18 +127,7 @@ class OmnipodDashPumpPlugin @Inject constructor(
|
|||
}
|
||||
|
||||
override fun getPumpStatus(reason: String) {
|
||||
val throwable = Completable.concat(
|
||||
listOf(
|
||||
omnipodManager
|
||||
.getStatus(ResponseType.StatusResponseType.DEFAULT_STATUS_RESPONSE)
|
||||
.ignoreElements(),
|
||||
history.updateFromState(podStateManager),
|
||||
podStateManager.updateActiveCommand()
|
||||
.map { handleCommandConfirmation(it) }
|
||||
.ignoreElement(),
|
||||
checkPodKaput()
|
||||
)
|
||||
).blockingGet()
|
||||
val throwable = getPodStatus().blockingGet()
|
||||
if (throwable != null) {
|
||||
aapsLogger.error(LTag.PUMP, "Error in getPumpStatus", throwable)
|
||||
} else {
|
||||
|
@ -147,26 +135,58 @@ class OmnipodDashPumpPlugin @Inject constructor(
|
|||
}
|
||||
}
|
||||
|
||||
private fun getPodStatus(): Completable = Completable.concat(
|
||||
listOf(
|
||||
omnipodManager
|
||||
.getStatus(ResponseType.StatusResponseType.DEFAULT_STATUS_RESPONSE)
|
||||
.ignoreElements(),
|
||||
history.updateFromState(podStateManager),
|
||||
podStateManager.updateActiveCommand()
|
||||
.map { handleCommandConfirmation(it) }
|
||||
.ignoreElement(),
|
||||
checkPodKaput(),
|
||||
)
|
||||
)
|
||||
|
||||
private fun checkPodKaput(): Completable = Completable.defer {
|
||||
val tbr = pumpSync.expectedPumpState().temporaryBasal
|
||||
if (podStateManager.isPodKaput &&
|
||||
(tbr == null || tbr.rate != 0.0)
|
||||
) {
|
||||
pumpSync.syncTemporaryBasalWithPumpId(
|
||||
timestamp = System.currentTimeMillis(),
|
||||
rate = 0.0,
|
||||
duration = T.mins(PodConstants.MAX_POD_LIFETIME.standardMinutes).msecs(),
|
||||
isAbsolute = true,
|
||||
type = PumpSync.TemporaryBasalType.PUMP_SUSPEND,
|
||||
pumpId = Random.Default.nextLong(), // we don't use this, just make sure it's unique
|
||||
pumpType = PumpType.OMNIPOD_DASH,
|
||||
pumpSerial = serialNumber()
|
||||
)
|
||||
if (podStateManager.isPodKaput) {
|
||||
if (tbr == null || tbr.rate != 0.0) {
|
||||
pumpSync.syncTemporaryBasalWithPumpId(
|
||||
timestamp = System.currentTimeMillis(),
|
||||
rate = 0.0,
|
||||
duration = T.mins(PodConstants.MAX_POD_LIFETIME.standardMinutes).msecs(),
|
||||
isAbsolute = true,
|
||||
type = PumpSync.TemporaryBasalType.PUMP_SUSPEND,
|
||||
pumpId = Random.Default.nextLong(), // we don't use this, just make sure it's unique
|
||||
pumpType = PumpType.OMNIPOD_DASH,
|
||||
pumpSerial = serialNumber()
|
||||
)
|
||||
}
|
||||
podStateManager.lastBolus?.run {
|
||||
if (!complete) {
|
||||
val deliveredUnits = markComplete()
|
||||
complete = true
|
||||
val bolusHistoryEntry = history.getById(historyId)
|
||||
val sync = pumpSync.syncBolusWithPumpId(
|
||||
timestamp = bolusHistoryEntry.createdAt,
|
||||
amount = deliveredUnits,
|
||||
pumpId = bolusHistoryEntry.pumpId(),
|
||||
pumpType = PumpType.OMNIPOD_DASH,
|
||||
pumpSerial = serialNumber(),
|
||||
type = bolusType
|
||||
)
|
||||
aapsLogger.info(LTag.PUMP, "syncBolusWithPumpId on CANCEL_BOLUS returned: $sync")
|
||||
}
|
||||
}
|
||||
}
|
||||
Completable.complete()
|
||||
}
|
||||
|
||||
override fun setNewBasalProfile(profile: Profile): PumpEnactResult {
|
||||
if (!podStateManager.isActivationCompleted) {
|
||||
return PumpEnactResult().success(true).enacted(false)
|
||||
}
|
||||
val basalProgram = mapProfileToBasalProgram(profile)
|
||||
return executeProgrammingCommand(
|
||||
pre = suspendDeliveryIfActive(),
|
||||
|
@ -244,7 +264,7 @@ class OmnipodDashPumpPlugin @Inject constructor(
|
|||
get() {
|
||||
val date = Date()
|
||||
val ret = podStateManager.basalProgram?.rateAt(date) ?: 0.0
|
||||
aapsLogger.info(LTag.PUMP, "baseBasalRate: %ret at $date}")
|
||||
aapsLogger.info(LTag.PUMP, "baseBasalRate: $ret at $date}")
|
||||
return if (podStateManager.alarmType != null) {
|
||||
0.0
|
||||
} else
|
||||
|
@ -268,74 +288,136 @@ class OmnipodDashPumpPlugin @Inject constructor(
|
|||
get() = 0
|
||||
|
||||
override fun deliverTreatment(detailedBolusInfo: DetailedBolusInfo): PumpEnactResult {
|
||||
// TODO report actual delivered amount after Pod Alarm and bolus cancellation
|
||||
val bolusBeeps = sp.getBoolean(R.string.key_omnipod_common_bolus_beeps_enabled, false)
|
||||
try {
|
||||
aapsLogger.info(LTag.PUMP, "Delivering treatment: $detailedBolusInfo")
|
||||
val bolusBeeps = sp.getBoolean(R.string.key_omnipod_common_bolus_beeps_enabled, false)
|
||||
|
||||
if (detailedBolusInfo.carbs > 0 || detailedBolusInfo.insulin == 0.0) {
|
||||
return PumpEnactResult(injector)
|
||||
.success(false)
|
||||
.enacted(false)
|
||||
.bolusDelivered(0.0)
|
||||
.carbsDelivered(0.0)
|
||||
.comment("Invalid input")
|
||||
if (detailedBolusInfo.carbs > 0 ||
|
||||
detailedBolusInfo.insulin == 0.0
|
||||
) {
|
||||
return PumpEnactResult(injector)
|
||||
.success(false)
|
||||
.enacted(false)
|
||||
.bolusDelivered(0.0)
|
||||
.carbsDelivered(0.0)
|
||||
.comment("Invalid input")
|
||||
}
|
||||
val requestedBolusAmount = detailedBolusInfo.insulin
|
||||
if (requestedBolusAmount > reservoirLevel) {
|
||||
return PumpEnactResult(injector)
|
||||
.success(false)
|
||||
.enacted(false)
|
||||
.bolusDelivered(0.0)
|
||||
.carbsDelivered(0.0)
|
||||
.comment("Not enough insulin in the reservoir")
|
||||
}
|
||||
var deliveredBolusAmount = 0.0
|
||||
|
||||
aapsLogger.info(
|
||||
LTag.PUMP,
|
||||
"deliverTreatment: requestedBolusAmount=$requestedBolusAmount"
|
||||
)
|
||||
val ret = executeProgrammingCommand(
|
||||
pre = observeDeliveryNotCanceled(),
|
||||
historyEntry = history.createRecord(
|
||||
commandType = OmnipodCommandType.SET_BOLUS,
|
||||
bolusRecord = BolusRecord(
|
||||
requestedBolusAmount,
|
||||
BolusType.fromBolusInfoBolusType(detailedBolusInfo.bolusType)
|
||||
)
|
||||
),
|
||||
activeCommandEntry = { historyId ->
|
||||
podStateManager.createActiveCommand(
|
||||
historyId,
|
||||
requestedBolus = requestedBolusAmount
|
||||
)
|
||||
},
|
||||
command = omnipodManager.bolus(
|
||||
detailedBolusInfo.insulin,
|
||||
bolusBeeps,
|
||||
bolusBeeps
|
||||
).filter { podEvent -> podEvent is PodEvent.CommandSent }
|
||||
.map { pumpSyncBolusStart(it, requestedBolusAmount, detailedBolusInfo.bolusType) }
|
||||
.ignoreElements(),
|
||||
post = waitForBolusDeliveryToComplete(5, requestedBolusAmount, detailedBolusInfo.bolusType)
|
||||
.map {
|
||||
deliveredBolusAmount = it
|
||||
aapsLogger.info(LTag.PUMP, "deliverTreatment: deliveredBolusAmount=$deliveredBolusAmount")
|
||||
}
|
||||
.ignoreElement()
|
||||
).toSingleDefault(
|
||||
PumpEnactResult(injector).success(true).enacted(true).bolusDelivered(deliveredBolusAmount)
|
||||
)
|
||||
.onErrorReturnItem(PumpEnactResult(injector).success(false).enacted(false))
|
||||
.blockingGet()
|
||||
aapsLogger.info(LTag.PUMP, "deliverTreatment result: $ret")
|
||||
return ret
|
||||
} finally {
|
||||
bolusCanceled = false
|
||||
}
|
||||
val requestedBolusAmount = detailedBolusInfo.insulin
|
||||
var delieveredBolusAmount = 0.0
|
||||
}
|
||||
|
||||
aapsLogger.info(
|
||||
LTag.PUMP,
|
||||
"deliverTreatment: units: $requestedBolusAmount"
|
||||
)
|
||||
return executeProgrammingCommand(
|
||||
pre = observeNoActiveTempBasal(true),
|
||||
historyEntry = history.createRecord(
|
||||
commandType = OmnipodCommandType.SET_BOLUS,
|
||||
bolusRecord = BolusRecord(
|
||||
requestedBolusAmount,
|
||||
BolusType.fromBolusInfoBolusType(detailedBolusInfo.bolusType)
|
||||
)
|
||||
),
|
||||
command = omnipodManager.bolus(
|
||||
detailedBolusInfo.insulin,
|
||||
bolusBeeps,
|
||||
bolusBeeps
|
||||
).filter { podEvent -> podEvent is PodEvent.CommandSent }
|
||||
.map { pumpSyncBolusStart(it, requestedBolusAmount, detailedBolusInfo.bolusType) }
|
||||
.ignoreElements(),
|
||||
post = waitForBolusDeliveryToComplete(5, requestedBolusAmount, detailedBolusInfo.bolusType)
|
||||
).toSingleDefault(PumpEnactResult(injector).success(true).enacted(true).bolusDelivered(delieveredBolusAmount))
|
||||
.onErrorReturnItem(PumpEnactResult(injector).success(false).enacted(false))
|
||||
.blockingGet()
|
||||
private fun observeDeliveryNotCanceled(): Completable = Completable.defer {
|
||||
if (bolusCanceled) {
|
||||
Completable.error(java.lang.IllegalStateException("Bolus canceled"))
|
||||
} else {
|
||||
Completable.complete()
|
||||
}
|
||||
}
|
||||
|
||||
private fun waitForBolusDeliveryToComplete(
|
||||
maxRetriesAtTheEnd: Int,
|
||||
maxRetries: Int,
|
||||
requestedBolusAmount: Double,
|
||||
bolusType: DetailedBolusInfo.BolusType
|
||||
): Completable {
|
||||
// TODO: wait for bolus delivery to start!
|
||||
// For now, we assume it started with success
|
||||
|
||||
val estimatedDeliveryTimeSeconds = ceil(requestedBolusAmount / 0.05).toLong() * 2
|
||||
): Single<Double> = Single.defer {
|
||||
// wait for bolus delivery confirmation, if possible
|
||||
val estimatedDeliveryTimeSeconds = estimateBolusDeliverySeconds(requestedBolusAmount)
|
||||
aapsLogger.info(LTag.PUMP, "estimatedDeliveryTimeSeconds: $estimatedDeliveryTimeSeconds")
|
||||
return Completable.concat(
|
||||
listOf(
|
||||
Observable.interval(1, TimeUnit.SECONDS)
|
||||
.take(estimatedDeliveryTimeSeconds)
|
||||
.doOnNext {
|
||||
if (bolusType == DetailedBolusInfo.BolusType.SMB) {
|
||||
return@doOnNext
|
||||
}
|
||||
val progressUpdateEvent = EventOverviewBolusProgress
|
||||
val percent = (100 * it) / estimatedDeliveryTimeSeconds
|
||||
progressUpdateEvent.status = resourceHelper.gs(R.string.bolusdelivering, requestedBolusAmount)
|
||||
progressUpdateEvent.percent = percent.toInt()
|
||||
rxBus.send(progressUpdateEvent)
|
||||
}.ignoreElements(),
|
||||
Observable.interval(5, TimeUnit.SECONDS).take(1).ignoreElements()
|
||||
// TODO check delivery status. for now, we are just sleeping for 5 sec
|
||||
)
|
||||
)
|
||||
var waited = 0
|
||||
while (waited < estimatedDeliveryTimeSeconds && !bolusCanceled) {
|
||||
waited += 1
|
||||
Thread.sleep(1000)
|
||||
if (bolusType == DetailedBolusInfo.BolusType.SMB) {
|
||||
continue
|
||||
}
|
||||
val progressUpdateEvent = EventOverviewBolusProgress
|
||||
val percent = (100 * waited) / estimatedDeliveryTimeSeconds
|
||||
progressUpdateEvent.status = resourceHelper.gs(R.string.bolusdelivering, requestedBolusAmount)
|
||||
progressUpdateEvent.percent = percent.toInt()
|
||||
rxBus.send(progressUpdateEvent)
|
||||
}
|
||||
|
||||
for (tries in 1..maxRetries) {
|
||||
val errorGettingStatus = getPodStatus().blockingGet()
|
||||
if (errorGettingStatus != null) {
|
||||
Thread.sleep(3000) // retry every 3 sec
|
||||
continue
|
||||
}
|
||||
if (podStateManager.deliveryStatus in
|
||||
arrayOf(
|
||||
DeliveryStatus.BOLUS_AND_TEMP_BASAL_ACTIVE,
|
||||
DeliveryStatus.BOLUS_AND_BASAL_ACTIVE
|
||||
) &&
|
||||
!bolusCanceled
|
||||
) {
|
||||
// delivery not complete yet
|
||||
val remainingUnits = podStateManager.lastBolus!!.bolusUnitsRemaining
|
||||
val progressUpdateEvent = EventOverviewBolusProgress
|
||||
val percent = ((requestedBolusAmount - remainingUnits) / requestedBolusAmount) * 100
|
||||
progressUpdateEvent.status = "Remaining: $remainingUnits units"
|
||||
progressUpdateEvent.percent = percent.toInt()
|
||||
Thread.sleep(estimateBolusDeliverySeconds(remainingUnits) * 1000.toLong())
|
||||
} else {
|
||||
// delivery is complete. If pod is Kaput, we are handling this in getPodStatus
|
||||
// TODO: race with cancel!!
|
||||
return@defer Single.just(podStateManager.lastBolus!!.deliveredUnits()!!)
|
||||
}
|
||||
}
|
||||
Single.just(requestedBolusAmount) // will be updated later!
|
||||
}
|
||||
|
||||
private fun estimateBolusDeliverySeconds(requestedBolusAmount: Double): Long {
|
||||
return ceil(requestedBolusAmount / 0.05).toLong() * 2 + 3
|
||||
}
|
||||
|
||||
private fun pumpSyncBolusStart(
|
||||
|
@ -365,14 +447,20 @@ class OmnipodDashPumpPlugin @Inject constructor(
|
|||
}
|
||||
|
||||
override fun stopBolusDelivering() {
|
||||
// TODO update Treatments (?)
|
||||
aapsLogger.info(LTag.PUMP, "stopBolusDelivering called")
|
||||
val ret = executeProgrammingCommand(
|
||||
historyEntry = history.createRecord(OmnipodCommandType.CANCEL_BOLUS),
|
||||
command = omnipodManager.stopBolus().ignoreElements()
|
||||
).subscribeOn(aapsSchedulers.io) // stopBolusDelivering is executed on the main thread
|
||||
.toPumpEnactResult()
|
||||
aapsLogger.info(LTag.PUMP, "stopBolusDelivering finished with result: $ret")
|
||||
bolusCanceled = true
|
||||
for (tries in 1..10) {
|
||||
val ret = executeProgrammingCommand(
|
||||
historyEntry = history.createRecord(OmnipodCommandType.CANCEL_BOLUS),
|
||||
command = omnipodManager.stopBolus().ignoreElements()
|
||||
).subscribeOn(aapsSchedulers.io) // stopBolusDelivering is executed on the main thread
|
||||
.toPumpEnactResult()
|
||||
aapsLogger.info(LTag.PUMP, "stopBolusDelivering finished with result: $ret")
|
||||
if (ret.success) {
|
||||
return
|
||||
}
|
||||
Thread.sleep(500)
|
||||
}
|
||||
}
|
||||
|
||||
override fun setTempBasalAbsolute(
|
||||
|
@ -385,11 +473,11 @@ class OmnipodDashPumpPlugin @Inject constructor(
|
|||
val tempBasalBeeps = sp.getBoolean(R.string.key_omnipod_common_tbr_beeps_enabled, false)
|
||||
aapsLogger.info(
|
||||
LTag.PUMP,
|
||||
"setTempBasalAbsolute: $durationInMinutes min :: $absoluteRate U/h :: " +
|
||||
"enforce: $enforceNew :: tbrType: $tbrType"
|
||||
"setTempBasalAbsolute: duration=$durationInMinutes min, rate=$absoluteRate U/h :: " +
|
||||
"enforce=$enforceNew, tbrType=$tbrType"
|
||||
)
|
||||
|
||||
return executeProgrammingCommand(
|
||||
val ret = executeProgrammingCommand(
|
||||
pre = observeNoActiveTempBasal(true),
|
||||
historyEntry = history.createRecord(
|
||||
commandType = OmnipodCommandType.SET_TEMPORARY_BASAL,
|
||||
|
@ -414,6 +502,8 @@ class OmnipodDashPumpPlugin @Inject constructor(
|
|||
.map { pumpSyncTempBasal(it, absoluteRate, durationInMinutes.toLong(), tbrType) }
|
||||
.ignoreElements(),
|
||||
).toPumpEnactResult()
|
||||
aapsLogger.info(LTag.PUMP, "setTempBasalAbsolute: result=$ret")
|
||||
return ret
|
||||
}
|
||||
|
||||
private fun pumpSyncTempBasal(
|
||||
|
@ -718,6 +808,7 @@ class OmnipodDashPumpPlugin @Inject constructor(
|
|||
podStateManager.updateActiveCommand()
|
||||
.map { handleCommandConfirmation(it) }
|
||||
.ignoreElement(),
|
||||
checkPodKaput(),
|
||||
post,
|
||||
)
|
||||
)
|
||||
|
@ -763,8 +854,8 @@ class OmnipodDashPumpPlugin @Inject constructor(
|
|||
|
||||
OmnipodCommandType.SET_TEMPORARY_BASAL -> {
|
||||
// This treatment was synced before sending the command
|
||||
aapsLogger.info(LTag.PUMPCOMM, "temporary basal denied. PumpId: ${historyEntry.pumpId()}")
|
||||
if (!confirmation.success) {
|
||||
aapsLogger.info(LTag.PUMPCOMM, "temporary basal denied. PumpId: ${historyEntry.pumpId()}")
|
||||
pumpSync.invalidateTemporaryBasal(historyEntry.pumpId())
|
||||
} else {
|
||||
podStateManager.tempBasal = command.tempBasal
|
||||
|
@ -780,7 +871,26 @@ class OmnipodDashPumpPlugin @Inject constructor(
|
|||
}
|
||||
|
||||
OmnipodCommandType.SET_BOLUS -> {
|
||||
if (!confirmation.success) {
|
||||
if (confirmation.success) {
|
||||
if (command.requestedBolus == null) {
|
||||
aapsLogger.error(LTag.PUMP, "Requested bolus not found: $command")
|
||||
}
|
||||
val record = historyEntry.record
|
||||
if (record !is BolusRecord) {
|
||||
aapsLogger.error(
|
||||
LTag.PUMP,
|
||||
"Expected SET_BOLUS history record to be a BolusRecord, found " +
|
||||
"$record"
|
||||
)
|
||||
}
|
||||
record as BolusRecord
|
||||
|
||||
podStateManager.createLastBolus(
|
||||
record.amout,
|
||||
command.historyId,
|
||||
record.bolusType.toBolusInfoBolusType()
|
||||
)
|
||||
} else {
|
||||
pumpSync.syncBolusWithPumpId(
|
||||
timestamp = historyEntry.createdAt,
|
||||
amount = 0.0,
|
||||
|
@ -792,6 +902,25 @@ class OmnipodDashPumpPlugin @Inject constructor(
|
|||
}
|
||||
}
|
||||
|
||||
OmnipodCommandType.CANCEL_BOLUS -> {
|
||||
if (confirmation.success) {
|
||||
|
||||
podStateManager.lastBolus?.run {
|
||||
val deliveredUnits = markComplete()
|
||||
val bolusHistoryEntry = history.getById(historyId)
|
||||
val sync = pumpSync.syncBolusWithPumpId(
|
||||
timestamp = bolusHistoryEntry.createdAt,
|
||||
amount = deliveredUnits, // we just marked this bolus as complete
|
||||
pumpId = bolusHistoryEntry.pumpId(),
|
||||
pumpType = PumpType.OMNIPOD_DASH,
|
||||
pumpSerial = serialNumber(),
|
||||
type = bolusType
|
||||
)
|
||||
aapsLogger.info(LTag.PUMP, "syncBolusWithPumpId on CANCEL_BOLUS returned: $sync")
|
||||
} ?: aapsLogger.error(LTag.PUMP, "Cancelled bolus that does not exist")
|
||||
}
|
||||
}
|
||||
|
||||
else ->
|
||||
aapsLogger.warn(
|
||||
LTag.PUMP,
|
||||
|
|
|
@ -218,7 +218,7 @@ class OmnipodDashManagerImpl @Inject constructor(
|
|||
return Observable.concat(
|
||||
observePodReadyForActivationPart1,
|
||||
observePairNewPod,
|
||||
observeConnectToPod, // FIXME needed after disconnect; observePairNewPod does not connect in that case.
|
||||
observeConnectToPod,
|
||||
observeActivationPart1Commands(lowReservoirAlertTrigger)
|
||||
).doOnComplete(ActivationProgressUpdater(ActivationProgress.PHASE_1_COMPLETED))
|
||||
// TODO these would be common for any observable returned in a public function in this class
|
||||
|
@ -246,6 +246,7 @@ class OmnipodDashManagerImpl @Inject constructor(
|
|||
)
|
||||
}
|
||||
if (podStateManager.activationProgress.isBefore(ActivationProgress.PRIMING)) {
|
||||
observables.add(observeConnectToPod) // connection can time out while waiting
|
||||
observables.add(
|
||||
Observable.defer {
|
||||
Observable.timer(podStateManager.firstPrimeBolusVolume!!.toLong(), TimeUnit.SECONDS)
|
||||
|
@ -644,6 +645,7 @@ class OmnipodDashManagerImpl @Inject constructor(
|
|||
}
|
||||
|
||||
is PodEvent.CommandSent -> {
|
||||
logger.debug(LTag.PUMP, "Command sent: ${event.command.commandType}")
|
||||
podStateManager.activeCommand?.let {
|
||||
if (it.sequence == event.command.sequenceNumber) {
|
||||
it.sentRealtime = SystemClock.elapsedRealtime()
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
package info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.state
|
||||
|
||||
import info.nightscout.androidaps.data.DetailedBolusInfo
|
||||
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.Id
|
||||
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.pair.PairResult
|
||||
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.event.PodEvent
|
||||
|
@ -64,6 +65,7 @@ interface OmnipodDashPodStateManager {
|
|||
val tempBasalActive: Boolean
|
||||
var basalProgram: BasalProgram?
|
||||
val activeCommand: ActiveCommand?
|
||||
val lastBolus: LastBolus?
|
||||
|
||||
fun increaseMessageSequenceNumber()
|
||||
fun increaseEapAkaSequenceNumber(): ByteArray
|
||||
|
@ -75,12 +77,19 @@ interface OmnipodDashPodStateManager {
|
|||
fun updateFromPairing(uniqueId: Id, pairResult: PairResult)
|
||||
fun reset()
|
||||
|
||||
fun createActiveCommand(historyId: String, basalProgram: BasalProgram? = null, tempBasal: TempBasal? = null):
|
||||
Single<ActiveCommand>
|
||||
fun createActiveCommand(
|
||||
historyId: String,
|
||||
basalProgram: BasalProgram? = null,
|
||||
tempBasal: TempBasal? = null,
|
||||
requestedBolus: Double? = null
|
||||
): Single<ActiveCommand>
|
||||
fun updateActiveCommand(): Maybe<CommandConfirmed>
|
||||
fun observeNoActiveCommand(): Observable<PodEvent>
|
||||
fun getCommandConfirmationFromState(): CommandConfirmationFromState
|
||||
|
||||
fun createLastBolus(requestedUnits: Double, historyId: String, bolusType: DetailedBolusInfo.BolusType)
|
||||
fun markLastBolusComplete(): LastBolus?
|
||||
|
||||
data class ActiveCommand(
|
||||
val sequence: Short,
|
||||
val createdRealtime: Long,
|
||||
|
@ -88,11 +97,36 @@ interface OmnipodDashPodStateManager {
|
|||
val historyId: String,
|
||||
var sendError: Throwable?,
|
||||
var basalProgram: BasalProgram?,
|
||||
val tempBasal: TempBasal?
|
||||
val tempBasal: TempBasal?,
|
||||
val requestedBolus: Double?
|
||||
)
|
||||
|
||||
// TODO: set created to "now" on boot
|
||||
data class TempBasal(val startTime: Long, val rate: Double, val durationInMinutes: Short) : Serializable
|
||||
|
||||
data class LastBolus(
|
||||
val startTime: Long,
|
||||
val requestedUnits: Double,
|
||||
var bolusUnitsRemaining: Double,
|
||||
var complete: Boolean,
|
||||
val historyId: String,
|
||||
val bolusType: DetailedBolusInfo.BolusType
|
||||
) {
|
||||
|
||||
fun deliveredUnits(): Double? {
|
||||
return if (complete) {
|
||||
requestedUnits - bolusUnitsRemaining
|
||||
} else {
|
||||
null
|
||||
}
|
||||
}
|
||||
|
||||
fun markComplete(): Double {
|
||||
this.complete = true
|
||||
return requestedUnits - bolusUnitsRemaining
|
||||
}
|
||||
}
|
||||
|
||||
enum class BluetoothConnectionState {
|
||||
CONNECTING, CONNECTED, DISCONNECTED
|
||||
}
|
||||
|
|
|
@ -2,6 +2,7 @@ package info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.state
|
|||
|
||||
import android.os.SystemClock
|
||||
import com.google.gson.Gson
|
||||
import info.nightscout.androidaps.data.DetailedBolusInfo
|
||||
import info.nightscout.androidaps.logging.AAPSLogger
|
||||
import info.nightscout.androidaps.logging.LTag
|
||||
import info.nightscout.androidaps.plugins.bus.RxBusWrapper
|
||||
|
@ -151,6 +152,10 @@ class OmnipodDashPodStateManagerImpl @Inject constructor(
|
|||
store()
|
||||
}
|
||||
|
||||
override val lastBolus: OmnipodDashPodStateManager.LastBolus?
|
||||
@Synchronized
|
||||
get() = podState.lastBolus
|
||||
|
||||
override val tempBasalActive: Boolean
|
||||
get() = !isSuspended && tempBasal?.let {
|
||||
it.startTime + it.durationInMinutes * 60 * 1000 > System.currentTimeMillis()
|
||||
|
@ -197,11 +202,46 @@ class OmnipodDashPodStateManagerImpl @Inject constructor(
|
|||
override val activeCommand: OmnipodDashPodStateManager.ActiveCommand?
|
||||
get() = podState.activeCommand
|
||||
|
||||
@Synchronized
|
||||
override fun createLastBolus(requestedUnits: Double, historyId: String, bolusType: DetailedBolusInfo.BolusType) {
|
||||
podState.lastBolus = OmnipodDashPodStateManager.LastBolus(
|
||||
startTime = System.currentTimeMillis(),
|
||||
requestedUnits = requestedUnits,
|
||||
bolusUnitsRemaining = requestedUnits,
|
||||
complete = false, // cancelled, delivered 100% or pod failure
|
||||
historyId = historyId,
|
||||
bolusType = bolusType
|
||||
)
|
||||
}
|
||||
|
||||
@Synchronized
|
||||
override fun markLastBolusComplete(): OmnipodDashPodStateManager.LastBolus? {
|
||||
val lastBolus = podState.lastBolus
|
||||
|
||||
lastBolus?.run {
|
||||
this.complete = true
|
||||
}
|
||||
?: logger.error(LTag.PUMP, "Trying to mark null bolus as complete")
|
||||
|
||||
return lastBolus
|
||||
}
|
||||
|
||||
private fun updateLastBolusFromResponse(bolusPulsesRemaining: Short) {
|
||||
podState.lastBolus?.run {
|
||||
val remainingUnits = bolusPulsesRemaining.toDouble() * 0.05
|
||||
this.bolusUnitsRemaining = remainingUnits
|
||||
if (remainingUnits == 0.0) {
|
||||
this.complete = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Synchronized
|
||||
override fun createActiveCommand(
|
||||
historyId: String,
|
||||
basalProgram: BasalProgram?,
|
||||
tempBasal: OmnipodDashPodStateManager.TempBasal?
|
||||
tempBasal: OmnipodDashPodStateManager.TempBasal?,
|
||||
requestedBolus: Double?
|
||||
):
|
||||
Single<OmnipodDashPodStateManager.ActiveCommand> {
|
||||
return Single.create { source ->
|
||||
|
@ -213,6 +253,7 @@ class OmnipodDashPodStateManagerImpl @Inject constructor(
|
|||
sendError = null,
|
||||
basalProgram = basalProgram,
|
||||
tempBasal = tempBasal,
|
||||
requestedBolus = requestedBolus
|
||||
)
|
||||
podState.activeCommand = command
|
||||
source.onSuccess(command)
|
||||
|
@ -233,6 +274,7 @@ class OmnipodDashPodStateManagerImpl @Inject constructor(
|
|||
if (activeCommand == null) {
|
||||
Observable.empty()
|
||||
} else {
|
||||
logger.warn(LTag.PUMP, "Active command already existing: $activeCommand")
|
||||
Observable.error(
|
||||
java.lang.IllegalStateException(
|
||||
"Trying to send a command " +
|
||||
|
@ -257,7 +299,7 @@ class OmnipodDashPodStateManagerImpl @Inject constructor(
|
|||
CommandSendingFailure -> {
|
||||
podState.activeCommand = null
|
||||
source.onError(
|
||||
activeCommand?.sendError
|
||||
activeCommand.sendError
|
||||
?: java.lang.IllegalStateException(
|
||||
"Could not send command and sendError is " +
|
||||
"missing"
|
||||
|
@ -323,11 +365,10 @@ class OmnipodDashPodStateManagerImpl @Inject constructor(
|
|||
}
|
||||
|
||||
override fun updateFromDefaultStatusResponse(response: DefaultStatusResponse) {
|
||||
logger.debug(LTag.PUMPBTCOMM, "Default status response :$response")
|
||||
logger.debug(LTag.PUMPCOMM, "Default status response :$response")
|
||||
podState.deliveryStatus = response.deliveryStatus
|
||||
podState.podStatus = response.podStatus
|
||||
podState.pulsesDelivered = response.totalPulsesDelivered
|
||||
podState.bolusPulsesRemaining = response.bolusPulsesRemaining
|
||||
if (response.reservoirPulsesRemaining < 1023) {
|
||||
podState.pulsesRemaining = response.reservoirPulsesRemaining
|
||||
}
|
||||
|
@ -337,6 +378,7 @@ class OmnipodDashPodStateManagerImpl @Inject constructor(
|
|||
|
||||
podState.lastUpdatedSystem = System.currentTimeMillis()
|
||||
podState.lastStatusResponseReceived = SystemClock.elapsedRealtime()
|
||||
updateLastBolusFromResponse(response.bolusPulsesRemaining)
|
||||
|
||||
store()
|
||||
rxBus.send(EventOmnipodDashPumpValuesChanged())
|
||||
|
@ -398,7 +440,6 @@ class OmnipodDashPodStateManagerImpl @Inject constructor(
|
|||
podState.deliveryStatus = response.deliveryStatus
|
||||
podState.podStatus = response.podStatus
|
||||
podState.pulsesDelivered = response.totalPulsesDelivered
|
||||
podState.bolusPulsesRemaining = response.bolusPulsesRemaining
|
||||
|
||||
if (response.reservoirPulsesRemaining < 1023) {
|
||||
podState.pulsesRemaining = response.reservoirPulsesRemaining
|
||||
|
@ -410,6 +451,7 @@ class OmnipodDashPodStateManagerImpl @Inject constructor(
|
|||
|
||||
podState.lastUpdatedSystem = System.currentTimeMillis()
|
||||
podState.lastStatusResponseReceived = SystemClock.elapsedRealtime()
|
||||
updateLastBolusFromResponse(response.bolusPulsesRemaining)
|
||||
|
||||
store()
|
||||
rxBus.send(EventOmnipodDashPumpValuesChanged())
|
||||
|
@ -488,5 +530,6 @@ class OmnipodDashPodStateManagerImpl @Inject constructor(
|
|||
var basalProgram: BasalProgram? = null
|
||||
var tempBasal: OmnipodDashPodStateManager.TempBasal? = null
|
||||
var activeCommand: OmnipodDashPodStateManager.ActiveCommand? = null
|
||||
var lastBolus: OmnipodDashPodStateManager.LastBolus? = null
|
||||
}
|
||||
}
|
||||
|
|
|
@ -11,6 +11,13 @@ data class TempBasalRecord(val duration: Int, val rate: Double) : Record()
|
|||
enum class BolusType {
|
||||
DEFAULT, SMB;
|
||||
|
||||
fun toBolusInfoBolusType(): DetailedBolusInfo.BolusType {
|
||||
return when (this) {
|
||||
DEFAULT -> DetailedBolusInfo.BolusType.NORMAL
|
||||
SMB -> DetailedBolusInfo.BolusType.SMB
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
fun fromBolusInfoBolusType(type: DetailedBolusInfo.BolusType): BolusType {
|
||||
return when (type) {
|
||||
|
|
|
@ -5,6 +5,7 @@ import android.graphics.Color
|
|||
import android.os.Bundle
|
||||
import android.os.Handler
|
||||
import android.os.Looper
|
||||
import android.os.SystemClock
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
|
@ -367,7 +368,8 @@ class OmnipodDashOverviewFragment : DaggerFragment() {
|
|||
|
||||
private fun updateLastConnection() {
|
||||
if (podStateManager.isUniqueIdSet) {
|
||||
podInfoBinding.lastConnection.text = readableDuration(podStateManager.lastUpdatedSystem)
|
||||
podInfoBinding.lastConnection.text = readableDuration(Duration(podStateManager.lastUpdatedSystem, System
|
||||
.currentTimeMillis()))
|
||||
val lastConnectionColor =
|
||||
if (omnipodDashPumpPlugin.isUnreachableAlertTimeoutExceeded(getPumpUnreachableTimeout().millis)) {
|
||||
Color.RED
|
||||
|
@ -377,7 +379,8 @@ class OmnipodDashOverviewFragment : DaggerFragment() {
|
|||
podInfoBinding.lastConnection.setTextColor(lastConnectionColor)
|
||||
} else {
|
||||
podInfoBinding.lastConnection.setTextColor(Color.WHITE)
|
||||
podInfoBinding.lastConnection.text = readableDuration(podStateManager.lastUpdatedSystem)
|
||||
podInfoBinding.lastConnection.text = readableDuration(
|
||||
Duration(podStateManager.lastUpdatedSystem, System.currentTimeMillis()))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -416,7 +419,7 @@ class OmnipodDashOverviewFragment : DaggerFragment() {
|
|||
}
|
||||
|
||||
val podStatusColor =
|
||||
if (!podStateManager.isActivationCompleted || /* TODO podStateManager.isPodDead || */ podStateManager.isSuspended) {
|
||||
if (!podStateManager.isActivationCompleted || podStateManager.isPodKaput || podStateManager.isSuspended) {
|
||||
Color.RED
|
||||
} else {
|
||||
Color.WHITE
|
||||
|
@ -425,27 +428,43 @@ class OmnipodDashOverviewFragment : DaggerFragment() {
|
|||
}
|
||||
|
||||
private fun updateLastBolus() {
|
||||
// TODO
|
||||
/*
|
||||
if (podStateManager.isActivationCompleted && podStateManager.hasLastBolus()) {
|
||||
var text = resourceHelper.gs(R.string.omnipod_common_overview_last_bolus_value, omnipodDashPumpPlugin.model().determineCorrectBolusSize(podStateManager.lastBolusAmount), resourceHelper.gs(R.string.insulin_unit_shortname), readableDuration(podStateManager.lastBolusStartTime))
|
||||
val textColor: Int
|
||||
|
||||
if (podStateManager.isLastBolusCertain) {
|
||||
textColor = Color.WHITE
|
||||
} else {
|
||||
var textColor = Color.WHITE
|
||||
podStateManager.activeCommand?.let {
|
||||
val requestedBolus = it.requestedBolus
|
||||
if (requestedBolus != null) {
|
||||
var text = resourceHelper.gs(
|
||||
R.string.omnipod_common_overview_last_bolus_value,
|
||||
omnipodDashPumpPlugin.model().determineCorrectBolusSize(requestedBolus),
|
||||
resourceHelper.gs(R.string.insulin_unit_shortname),
|
||||
readableDuration(Duration(it.createdRealtime, SystemClock.elapsedRealtime()))
|
||||
)
|
||||
text += " (uncertain) "
|
||||
textColor = Color.RED
|
||||
text += " (" + resourceHelper.gs(R.string.omnipod_eros_uncertain) + ")"
|
||||
podInfoBinding.lastBolus.text = text
|
||||
podInfoBinding.lastBolus.setTextColor(textColor)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
podInfoBinding.lastBolus.setTextColor(textColor)
|
||||
podStateManager.lastBolus?.let {
|
||||
// display requested units if delivery is in progress
|
||||
var bolusSize = it.deliveredUnits()
|
||||
?: it.requestedUnits
|
||||
|
||||
var text = resourceHelper.gs(
|
||||
R.string.omnipod_common_overview_last_bolus_value,
|
||||
omnipodDashPumpPlugin.model().determineCorrectBolusSize(bolusSize),
|
||||
resourceHelper.gs(R.string.insulin_unit_shortname),
|
||||
readableDuration(Duration(it.startTime, System.currentTimeMillis()))
|
||||
)
|
||||
if (!it.complete) {
|
||||
textColor = Color.YELLOW
|
||||
}
|
||||
podInfoBinding.lastBolus.text = text
|
||||
podInfoBinding.lastBolus.setTextColor(textColor)
|
||||
|
||||
} else {
|
||||
podInfoBinding.lastBolus.text = PLACEHOLDER
|
||||
podInfoBinding.lastBolus.setTextColor(Color.WHITE)
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
private fun updateTempBasal() {
|
||||
|
@ -592,8 +611,7 @@ class OmnipodDashOverviewFragment : DaggerFragment() {
|
|||
}
|
||||
*/
|
||||
|
||||
private fun readableDuration(dateTime: Long): String {
|
||||
val duration = Duration(dateTime, System.currentTimeMillis())
|
||||
private fun readableDuration(duration: Duration): String {
|
||||
val hours = duration.standardHours.toInt()
|
||||
val minutes = duration.standardMinutes.toInt()
|
||||
val seconds = duration.standardSeconds.toInt()
|
||||
|
|
Loading…
Reference in a new issue