Merge pull request #39 from 0pen-dash/avereha/alarms

Avereha/alarms
This commit is contained in:
Andrei Vereha 2021-06-06 21:30:24 +02:00 committed by GitHub
commit 8b87919b05
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 207 additions and 108 deletions

View file

@ -4,6 +4,7 @@ import dagger.android.HasAndroidInjector
import info.nightscout.androidaps.data.DetailedBolusInfo import info.nightscout.androidaps.data.DetailedBolusInfo
import info.nightscout.androidaps.data.PumpEnactResult import info.nightscout.androidaps.data.PumpEnactResult
import info.nightscout.androidaps.events.EventProfileSwitchChanged import info.nightscout.androidaps.events.EventProfileSwitchChanged
import info.nightscout.androidaps.events.EventTempBasalChange
import info.nightscout.androidaps.interfaces.* import info.nightscout.androidaps.interfaces.*
import info.nightscout.androidaps.logging.AAPSLogger import info.nightscout.androidaps.logging.AAPSLogger
import info.nightscout.androidaps.logging.LTag import info.nightscout.androidaps.logging.LTag
@ -17,7 +18,6 @@ import info.nightscout.androidaps.plugins.pump.omnipod.common.queue.command.*
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.OmnipodDashManager import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.OmnipodDashManager
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.event.PodEvent import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.event.PodEvent
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.definition.ActivationProgress import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.definition.ActivationProgress
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.definition.BasalProgram
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.definition.BeepType import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.definition.BeepType
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.definition.DeliveryStatus import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.definition.DeliveryStatus
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.definition.PodConstants import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.definition.PodConstants
@ -39,12 +39,13 @@ import info.nightscout.androidaps.utils.sharedPreferences.SP
import io.reactivex.Completable import io.reactivex.Completable
import io.reactivex.Observable import io.reactivex.Observable
import io.reactivex.Single import io.reactivex.Single
import io.reactivex.rxkotlin.blockingSubscribeBy import io.reactivex.rxkotlin.plusAssign
import io.reactivex.rxkotlin.subscribeBy import io.reactivex.rxkotlin.subscribeBy
import org.json.JSONObject import org.json.JSONObject
import java.util.* import java.util.*
import javax.inject.Inject import javax.inject.Inject
import javax.inject.Singleton import javax.inject.Singleton
import kotlin.random.Random
@Singleton @Singleton
class OmnipodDashPumpPlugin @Inject constructor( class OmnipodDashPumpPlugin @Inject constructor(
@ -55,6 +56,8 @@ class OmnipodDashPumpPlugin @Inject constructor(
private val history: DashHistory, private val history: DashHistory,
private val pumpSync: PumpSync, private val pumpSync: PumpSync,
private val rxBus: RxBusWrapper, private val rxBus: RxBusWrapper,
// private val disposable: CompositeDisposable = CompositeDisposable(),
// private val aapsSchedulers: AapsSchedulers,
injector: HasAndroidInjector, injector: HasAndroidInjector,
aapsLogger: AAPSLogger, aapsLogger: AAPSLogger,
@ -121,38 +124,62 @@ class OmnipodDashPumpPlugin @Inject constructor(
} }
override fun getPumpStatus(reason: String) { override fun getPumpStatus(reason: String) {
val throwable = Completable.concat(listOf( val throwable = Completable.concat(
omnipodManager listOf(
.getStatus(ResponseType.StatusResponseType.DEFAULT_STATUS_RESPONSE) omnipodManager
.ignoreElements(), .getStatus(ResponseType.StatusResponseType.DEFAULT_STATUS_RESPONSE)
history.updateFromState(podStateManager), .ignoreElements(),
podStateManager.updateActiveCommand() history.updateFromState(podStateManager),
.map { handleCommandConfirmation(it) } podStateManager.updateActiveCommand()
.ignoreElement(), .map { handleCommandConfirmation(it) }
)).blockingGet() .ignoreElement(),
if (throwable != null){ checkPodKaput()
)
).blockingGet()
if (throwable != null) {
aapsLogger.error(LTag.PUMP, "Error in getPumpStatus", throwable) aapsLogger.error(LTag.PUMP, "Error in getPumpStatus", throwable)
} else { } else {
aapsLogger.info(LTag.PUMP, "getPumpStatus executed with success") aapsLogger.info(LTag.PUMP, "getPumpStatus executed with success")
} }
} }
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()
)
}
Completable.complete()
}
override fun setNewBasalProfile(profile: Profile): PumpEnactResult { override fun setNewBasalProfile(profile: Profile): PumpEnactResult {
val basalProgram = mapProfileToBasalProgram(profile) val basalProgram = mapProfileToBasalProgram(profile)
return executeSimpleProgrammingCommand( return executeProgrammingCommand(
pre = suspendDeliveryIfActive(), pre = suspendDeliveryIfActive(),
historyEntry = history.createRecord(commandType = OmnipodCommandType.SET_BASAL_PROFILE), historyEntry = history.createRecord(commandType = OmnipodCommandType.SET_BASAL_PROFILE),
activeCommandEntry = { historyId ->
podStateManager.createActiveCommand(historyId, basalProgram = basalProgram)
},
command = omnipodManager.setBasalProgram(basalProgram).ignoreElements(), command = omnipodManager.setBasalProgram(basalProgram).ignoreElements(),
basalProgram = basalProgram,
post = failWhenUnconfirmed(), post = failWhenUnconfirmed(),
).toPumpEnactResult() ).toPumpEnactResult()
} }
private fun failWhenUnconfirmed(): Completable = Completable.defer{ private fun failWhenUnconfirmed(): Completable = Completable.defer {
rxBus.send(EventTempBasalChange())
if (podStateManager.activeCommand != null) { if (podStateManager.activeCommand != null) {
Completable.error(java.lang.IllegalStateException("Command not confirmed")) Completable.error(java.lang.IllegalStateException("Command not confirmed"))
}else { } else {
Completable.complete() Completable.complete()
} }
} }
@ -161,9 +188,9 @@ class OmnipodDashPumpPlugin @Inject constructor(
if (podStateManager.deliveryStatus == DeliveryStatus.SUSPENDED) if (podStateManager.deliveryStatus == DeliveryStatus.SUSPENDED)
Completable.complete() Completable.complete()
else else
executeSimpleProgrammingCommand( executeProgrammingCommand(
history.createRecord(OmnipodCommandType.SUSPEND_DELIVERY), historyEntry = history.createRecord(OmnipodCommandType.SUSPEND_DELIVERY),
omnipodManager.suspendDelivery() command = omnipodManager.suspendDelivery()
.filter { podEvent -> podEvent is PodEvent.CommandSent } .filter { podEvent -> podEvent is PodEvent.CommandSent }
.map { .map {
pumpSyncTempBasal( pumpSyncTempBasal(
@ -172,11 +199,19 @@ class OmnipodDashPumpPlugin @Inject constructor(
PodConstants.MAX_POD_LIFETIME.standardMinutes, PodConstants.MAX_POD_LIFETIME.standardMinutes,
PumpSync.TemporaryBasalType.PUMP_SUSPEND PumpSync.TemporaryBasalType.PUMP_SUSPEND
) )
rxBus.send(EventTempBasalChange())
} }
.ignoreElements(), .ignoreElements()
) )
} }
/* override fun onStop() {
super.onStop()
disposable.clear()
}
*/
private fun observeDeliverySuspended(): Completable = Completable.defer { private fun observeDeliverySuspended(): Completable = Completable.defer {
if (podStateManager.deliveryStatus == DeliveryStatus.SUSPENDED) if (podStateManager.deliveryStatus == DeliveryStatus.SUSPENDED)
Completable.complete() Completable.complete()
@ -202,7 +237,15 @@ class OmnipodDashPumpPlugin @Inject constructor(
} }
override val baseBasalRate: Double override val baseBasalRate: Double
get() = podStateManager.basalProgram?.rateAt(Date()) ?: 0.0 get() {
val date = Date()
val ret = podStateManager.basalProgram?.rateAt(date) ?: 0.0
aapsLogger.info(LTag.PUMP, "baseBasalRate: %ret at $date}")
return if (podStateManager.alarmType != null) {
0.0
} else
ret
}
override val reservoirLevel: Double override val reservoirLevel: Double
get() { get() {
@ -274,10 +317,12 @@ class OmnipodDashPumpPlugin @Inject constructor(
override fun stopBolusDelivering() { override fun stopBolusDelivering() {
// TODO update Treatments (?) // TODO update Treatments (?)
executeSimpleProgrammingCommand( aapsLogger.info(LTag.PUMP, "stopBolusDelivering called")
history.createRecord(OmnipodCommandType.CANCEL_BOLUS), val ret = executeProgrammingCommand(
omnipodManager.stopBolus().ignoreElements() historyEntry = history.createRecord(OmnipodCommandType.CANCEL_BOLUS),
command = omnipodManager.stopBolus().ignoreElements()
).toPumpEnactResult() ).toPumpEnactResult()
aapsLogger.info(LTag.PUMP, "stopBolusDelivering finished with result: $ret")
} }
override fun setTempBasalAbsolute( override fun setTempBasalAbsolute(
@ -288,12 +333,26 @@ class OmnipodDashPumpPlugin @Inject constructor(
tbrType: PumpSync.TemporaryBasalType tbrType: PumpSync.TemporaryBasalType
): PumpEnactResult { ): PumpEnactResult {
val tempBasalBeeps = sp.getBoolean(R.string.key_omnipod_common_tbr_beeps_enabled, false) val tempBasalBeeps = sp.getBoolean(R.string.key_omnipod_common_tbr_beeps_enabled, false)
aapsLogger.info(
return executeSimpleProgrammingCommand( LTag.PUMP,
"setTempBasalAbsolute: $durationInMinutes min :: $absoluteRate U/h :: " +
"enforce: $enforceNew :: tbrType: $tbrType"
)
return executeProgrammingCommand(
historyEntry = history.createRecord( historyEntry = history.createRecord(
commandType = OmnipodCommandType.SET_TEMPORARY_BASAL, commandType = OmnipodCommandType.SET_TEMPORARY_BASAL,
tempBasalRecord = TempBasalRecord(duration = durationInMinutes, rate = absoluteRate) tempBasalRecord = TempBasalRecord(duration = durationInMinutes, rate = absoluteRate)
), ),
activeCommandEntry = { historyId ->
podStateManager.createActiveCommand(
historyId,
tempBasal = OmnipodDashPodStateManager.TempBasal(
startTime = System.currentTimeMillis(),
rate = absoluteRate,
durationInMinutes = durationInMinutes.toShort(),
)
)
},
command = omnipodManager.setTempBasal( command = omnipodManager.setTempBasal(
absoluteRate, absoluteRate,
durationInMinutes.toShort(), durationInMinutes.toShort(),
@ -302,7 +361,7 @@ class OmnipodDashPumpPlugin @Inject constructor(
.filter { podEvent -> podEvent is PodEvent.CommandSent } .filter { podEvent -> podEvent is PodEvent.CommandSent }
.map { pumpSyncTempBasal(it, absoluteRate, durationInMinutes.toLong(), tbrType) } .map { pumpSyncTempBasal(it, absoluteRate, durationInMinutes.toLong(), tbrType) }
.ignoreElements(), .ignoreElements(),
pre = observeNoActiveTempBasal() pre = observeNoActiveTempBasal(true),
).toPumpEnactResult() ).toPumpEnactResult()
} }
@ -338,17 +397,29 @@ class OmnipodDashPumpPlugin @Inject constructor(
private fun observeNoActiveTempBasal(): Completable { private fun observeNoActiveTempBasal(): Completable {
return Completable.defer { return Completable.defer {
val expectedState = pumpSync.expectedPumpState() when {
if (expectedState.temporaryBasal == null) { podStateManager.deliveryStatus !in
aapsLogger.info(LTag.PUMP, "No temporary basal to cancel") arrayOf(DeliveryStatus.TEMP_BASAL_ACTIVE, DeliveryStatus.BOLUS_AND_TEMP_BASAL_ACTIVE) -> {
Completable.complete() // TODO: what happens if we try to cancel inexistent temp basal?
} else { aapsLogger.info(LTag.PUMP, "No temporary basal to cancel")
// enforceNew == true Completable.complete()
aapsLogger.info(LTag.PUMP, "Canceling existing temp basal") }
executeSimpleProgrammingCommand(
history.createRecord(OmnipodCommandType.CANCEL_TEMPORARY_BASAL), !enforceNew ->
omnipodManager.stopTempBasal().ignoreElements() Completable.error(
) IllegalStateException(
"Temporary basal already active and enforeNew is not set."
)
)
else -> {
// enforceNew == true
aapsLogger.info(LTag.PUMP, "Canceling existing temp basal")
executeProgrammingCommand(
historyEntry = history.createRecord(OmnipodCommandType.CANCEL_TEMPORARY_BASAL),
command = omnipodManager.stopTempBasal().ignoreElements()
)
}
} }
} }
} }
@ -386,7 +457,7 @@ class OmnipodDashPumpPlugin @Inject constructor(
} }
override fun cancelTempBasal(enforceNew: Boolean): PumpEnactResult { override fun cancelTempBasal(enforceNew: Boolean): PumpEnactResult {
return executeSimpleProgrammingCommand( return executeProgrammingCommand(
historyEntry = history.createRecord(OmnipodCommandType.CANCEL_TEMPORARY_BASAL), historyEntry = history.createRecord(OmnipodCommandType.CANCEL_TEMPORARY_BASAL),
command = omnipodManager.stopTempBasal().ignoreElements(), command = omnipodManager.stopTempBasal().ignoreElements(),
pre = observeActiveTempBasal(), pre = observeActiveTempBasal(),
@ -395,6 +466,9 @@ class OmnipodDashPumpPlugin @Inject constructor(
fun Completable.toPumpEnactResult(): PumpEnactResult { fun Completable.toPumpEnactResult(): PumpEnactResult {
return this.toSingleDefault(PumpEnactResult(injector).success(true).enacted(true)) return this.toSingleDefault(PumpEnactResult(injector).success(true).enacted(true))
.doOnError { throwable ->
aapsLogger.error(LTag.PUMP, "toPumpEnactResult, error executing command: $throwable")
}
.onErrorReturnItem(PumpEnactResult(injector).success(false).enacted(false)) .onErrorReturnItem(PumpEnactResult(injector).success(false).enacted(false))
.blockingGet() .blockingGet()
} }
@ -483,38 +557,15 @@ class OmnipodDashPumpPlugin @Inject constructor(
private fun silenceAlerts(): PumpEnactResult { private fun silenceAlerts(): PumpEnactResult {
// TODO filter alert types // TODO filter alert types
return podStateManager.activeAlerts?.let { return podStateManager.activeAlerts?.let {
Single.create<PumpEnactResult> { source -> executeProgrammingCommand(
Observable.concat( historyEntry = history.createRecord(commandType = OmnipodCommandType.ACKNOWLEDGE_ALERTS),
// TODO: is this a programming command? if yes, save to history command = omnipodManager.silenceAlerts(it).ignoreElements(),
omnipodManager.silenceAlerts(it), ).toPumpEnactResult()
history.updateFromState(podStateManager).toObservable(),
podStateManager.updateActiveCommand().toObservable(),
).subscribeBy(
onNext = { podEvent ->
aapsLogger.debug(
LTag.PUMP,
"Received PodEvent in silenceAlerts: $podEvent"
)
},
onError = { throwable ->
aapsLogger.error(LTag.PUMP, "Error in silenceAlerts", throwable)
source.onSuccess(
PumpEnactResult(injector).success(false).comment(
throwable.toString()
)
)
},
onComplete = {
aapsLogger.debug("silenceAlerts completed")
source.onSuccess(PumpEnactResult(injector).success(true))
}
)
}.blockingGet()
} ?: PumpEnactResult(injector).success(false).enacted(false).comment("No active alerts") // TODO i18n } ?: PumpEnactResult(injector).success(false).enacted(false).comment("No active alerts") // TODO i18n
} }
private fun suspendDelivery(): PumpEnactResult { private fun suspendDelivery(): PumpEnactResult {
return executeSimpleProgrammingCommand( return executeProgrammingCommand(
historyEntry = history.createRecord(OmnipodCommandType.SUSPEND_DELIVERY), historyEntry = history.createRecord(OmnipodCommandType.SUSPEND_DELIVERY),
command = omnipodManager.suspendDelivery() command = omnipodManager.suspendDelivery()
.filter { podEvent -> podEvent is PodEvent.CommandSent } .filter { podEvent -> podEvent is PodEvent.CommandSent }
@ -540,18 +591,18 @@ class OmnipodDashPumpPlugin @Inject constructor(
private fun resumeDelivery(): PumpEnactResult { private fun resumeDelivery(): PumpEnactResult {
return profileFunction.getProfile()?.let { return profileFunction.getProfile()?.let {
executeSimpleProgrammingCommand( executeProgrammingCommand(
history.createRecord(OmnipodCommandType.RESUME_DELIVERY),
omnipodManager.setBasalProgram(mapProfileToBasalProgram(it)).ignoreElements(),
pre = observeDeliverySuspended(), pre = observeDeliverySuspended(),
historyEntry = history.createRecord(OmnipodCommandType.RESUME_DELIVERY),
command = omnipodManager.setBasalProgram(mapProfileToBasalProgram(it)).ignoreElements()
).toPumpEnactResult() ).toPumpEnactResult()
} ?: PumpEnactResult(injector).success(false).enacted(false).comment("No profile active") // TODO i18n } ?: PumpEnactResult(injector).success(false).enacted(false).comment("No profile active") // TODO i18n
} }
private fun deactivatePod(): PumpEnactResult { private fun deactivatePod(): PumpEnactResult {
return executeSimpleProgrammingCommand( return executeProgrammingCommand(
history.createRecord(OmnipodCommandType.DEACTIVATE_POD), historyEntry = history.createRecord(OmnipodCommandType.DEACTIVATE_POD),
omnipodManager.deactivatePod().ignoreElements() command = omnipodManager.deactivatePod().ignoreElements()
).toPumpEnactResult() ).toPumpEnactResult()
} }
@ -566,9 +617,9 @@ class OmnipodDashPumpPlugin @Inject constructor(
} }
private fun playTestBeep(): PumpEnactResult { private fun playTestBeep(): PumpEnactResult {
return executeSimpleProgrammingCommand( return executeProgrammingCommand(
history.createRecord(OmnipodCommandType.PLAY_TEST_BEEP), historyEntry = history.createRecord(OmnipodCommandType.PLAY_TEST_BEEP),
omnipodManager.playBeep(BeepType.LONG_SINGLE_BEEP).ignoreElements() command = omnipodManager.playBeep(BeepType.LONG_SINGLE_BEEP).ignoreElements()
).toPumpEnactResult() ).toPumpEnactResult()
} }
@ -593,11 +644,12 @@ class OmnipodDashPumpPlugin @Inject constructor(
commandQueue.customCommand(CommandHandleTimeChange(false), null) commandQueue.customCommand(CommandHandleTimeChange(false), null)
} }
private fun executeSimpleProgrammingCommand( private fun executeProgrammingCommand(
historyEntry: Single<String>,
command: Completable,
pre: Completable = Completable.complete(), pre: Completable = Completable.complete(),
basalProgram: BasalProgram? = null, historyEntry: Single<String>,
activeCommandEntry: (historyId: String) -> Single<OmnipodDashPodStateManager.ActiveCommand> =
{ historyId -> podStateManager.createActiveCommand(historyId) },
command: Completable,
post: Completable = Completable.complete(), post: Completable = Completable.complete(),
): Completable { ): Completable {
return Completable.concat( return Completable.concat(
@ -605,7 +657,7 @@ class OmnipodDashPumpPlugin @Inject constructor(
pre, pre,
podStateManager.observeNoActiveCommand().ignoreElements(), podStateManager.observeNoActiveCommand().ignoreElements(),
historyEntry historyEntry
.flatMap { podStateManager.createActiveCommand(it, basalProgram) } .flatMap { activeCommandEntry(it) }
.ignoreElement(), .ignoreElement(),
command.doOnError { command.doOnError {
podStateManager.activeCommand?.sendError = it podStateManager.activeCommand?.sendError = it
@ -636,6 +688,7 @@ class OmnipodDashPumpPlugin @Inject constructor(
PumpType.OMNIPOD_DASH, PumpType.OMNIPOD_DASH,
serialNumber() serialNumber()
) )
podStateManager.tempBasal = null
} }
OmnipodCommandType.SET_BASAL_PROFILE -> { OmnipodCommandType.SET_BASAL_PROFILE -> {
@ -662,12 +715,16 @@ class OmnipodDashPumpPlugin @Inject constructor(
aapsLogger.info(LTag.PUMPCOMM, "temporary basal denied. PumpId: ${historyEntry.pumpId()}") aapsLogger.info(LTag.PUMPCOMM, "temporary basal denied. PumpId: ${historyEntry.pumpId()}")
if (!confirmation.success) { if (!confirmation.success) {
pumpSync.invalidateTemporaryBasal(historyEntry.pumpId()) pumpSync.invalidateTemporaryBasal(historyEntry.pumpId())
} else {
podStateManager.tempBasal = command.tempBasal
} }
} }
OmnipodCommandType.SUSPEND_DELIVERY -> { OmnipodCommandType.SUSPEND_DELIVERY -> {
if (!confirmation.success) { if (!confirmation.success) {
pumpSync.invalidateTemporaryBasal(historyEntry.pumpId()) pumpSync.invalidateTemporaryBasal(historyEntry.pumpId())
} else {
podStateManager.tempBasal = null
} }
} }

View file

@ -6,15 +6,10 @@ import info.nightscout.androidaps.logging.LTag
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.Ids import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.Ids
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.endecrypt.EnDecrypt import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.endecrypt.EnDecrypt
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.exceptions.CouldNotParseResponseException import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.exceptions.CouldNotParseResponseException
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.exceptions.IllegalResponseException
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.exceptions.NakResponseException
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.exceptions.PodAlarmException
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.message.* import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.message.*
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.message.MessageType import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.message.MessageType
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.message.StringLengthPrefixEncoding.Companion.parseKeys import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.message.StringLengthPrefixEncoding.Companion.parseKeys
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.command.base.Command import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.command.base.Command
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.response.AlarmStatusResponse
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.response.NakResponse
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.response.Response import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.response.Response
import kotlin.reflect.KClass import kotlin.reflect.KClass
@ -88,7 +83,7 @@ class Session(
val response = parseResponse(decrypted) val response = parseResponse(decrypted)
if (!responseType.isInstance(response)) { /*if (!responseType.isInstance(response)) {
if (response is AlarmStatusResponse) { if (response is AlarmStatusResponse) {
throw PodAlarmException(response) throw PodAlarmException(response)
} }
@ -98,6 +93,8 @@ class Session(
throw IllegalResponseException(responseType, response) throw IllegalResponseException(responseType, response)
} }
*/
sessionKeys.msgSequenceNumber++ sessionKeys.msgSequenceNumber++
val ack = getAck(responseMsgPacket) val ack = getAck(responseMsgPacket)
aapsLogger.debug(LTag.PUMPBTCOMM, "Sending ACK: ${ack.payload.toHex()} in packet $ack") aapsLogger.debug(LTag.PUMPBTCOMM, "Sending ACK: ${ack.payload.toHex()} in packet $ack")

View file

@ -23,7 +23,7 @@ class BasalProgram(
val hourOfDay = instance[Calendar.HOUR_OF_DAY] val hourOfDay = instance[Calendar.HOUR_OF_DAY]
val minuteOfHour = instance[Calendar.MINUTE] val minuteOfHour = instance[Calendar.MINUTE]
val slotIndex = hourOfDay * 2 + minuteOfHour.div(30) val slotIndex = hourOfDay * 2 + minuteOfHour.div(30)
val slot = segments.find { it.startSlotIndex <= slotIndex && slotIndex< it.endSlotIndex } val slot = segments.find { it.startSlotIndex <= slotIndex && slotIndex < it.endSlotIndex }
return (slot?.basalRateInHundredthUnitsPerHour ?: 0).toDouble() / 100 return (slot?.basalRateInHundredthUnitsPerHour ?: 0).toDouble() / 100
} }

View file

@ -28,6 +28,7 @@ interface OmnipodDashPodStateManager {
val isActivationCompleted: Boolean val isActivationCompleted: Boolean
val isSuspended: Boolean val isSuspended: Boolean
val isPodRunning: Boolean val isPodRunning: Boolean
val isPodKaput: Boolean
var bluetoothConnectionState: BluetoothConnectionState var bluetoothConnectionState: BluetoothConnectionState
val lastUpdatedSystem: Long // System.currentTimeMillis() val lastUpdatedSystem: Long // System.currentTimeMillis()
@ -57,8 +58,9 @@ interface OmnipodDashPodStateManager {
val deliveryStatus: DeliveryStatus? val deliveryStatus: DeliveryStatus?
val minutesSinceActivation: Short? val minutesSinceActivation: Short?
val activeAlerts: EnumSet<AlertType>? val activeAlerts: EnumSet<AlertType>?
val alarmType: AlarmType?
val tempBasal: TempBasal? var tempBasal: TempBasal?
val tempBasalActive: Boolean val tempBasalActive: Boolean
var basalProgram: BasalProgram? var basalProgram: BasalProgram?
val activeCommand: ActiveCommand? val activeCommand: ActiveCommand?
@ -73,7 +75,8 @@ interface OmnipodDashPodStateManager {
fun updateFromPairing(uniqueId: Id, pairResult: PairResult) fun updateFromPairing(uniqueId: Id, pairResult: PairResult)
fun reset() fun reset()
fun createActiveCommand(historyId: String, basalProgram: BasalProgram? = null): Single<ActiveCommand> fun createActiveCommand(historyId: String, basalProgram: BasalProgram? = null, tempBasal: TempBasal? = null):
Single<ActiveCommand>
fun updateActiveCommand(): Maybe<CommandConfirmed> fun updateActiveCommand(): Maybe<CommandConfirmed>
fun observeNoActiveCommand(): Observable<PodEvent> fun observeNoActiveCommand(): Observable<PodEvent>
fun getCommandConfirmationFromState(): CommandConfirmationFromState fun getCommandConfirmationFromState(): CommandConfirmationFromState
@ -84,7 +87,8 @@ interface OmnipodDashPodStateManager {
var sentRealtime: Long = 0, var sentRealtime: Long = 0,
val historyId: String, val historyId: String,
var sendError: Throwable?, var sendError: Throwable?,
var basalProgram: BasalProgram? var basalProgram: BasalProgram?,
val tempBasal: TempBasal?
) )
// TODO: set created to "now" on boot // TODO: set created to "now" on boot
data class TempBasal(val startTime: Long, val rate: Double, val durationInMinutes: Short) : Serializable data class TempBasal(val startTime: Long, val rate: Double, val durationInMinutes: Short) : Serializable

View file

@ -55,6 +55,9 @@ class OmnipodDashPodStateManagerImpl @Inject constructor(
get() = podState.deliveryStatus?.equals(DeliveryStatus.SUSPENDED) get() = podState.deliveryStatus?.equals(DeliveryStatus.SUSPENDED)
?: false ?: false
override val isPodKaput: Boolean
get() = podState.podStatus in arrayOf(PodStatus.ALARM, PodStatus.DEACTIVATED)
override val isPodRunning: Boolean override val isPodRunning: Boolean
get() = podState.podStatus?.isRunning() ?: false get() = podState.podStatus?.isRunning() ?: false
@ -137,20 +140,27 @@ class OmnipodDashPodStateManagerImpl @Inject constructor(
override val activeAlerts: EnumSet<AlertType>? override val activeAlerts: EnumSet<AlertType>?
get() = podState.activeAlerts get() = podState.activeAlerts
override val tempBasal: OmnipodDashPodStateManager.TempBasal? override val alarmType: AlarmType?
get() = podState.alarmType
override var tempBasal: OmnipodDashPodStateManager.TempBasal?
get() = podState.tempBasal get() = podState.tempBasal
set(tempBasal) {
podState.tempBasal = tempBasal
rxBus.send(EventOmnipodDashPumpValuesChanged())
store()
}
override val tempBasalActive: Boolean override val tempBasalActive: Boolean
get() = podState.deliveryStatus in get() = !isSuspended && tempBasal?.let {
arrayOf( it.startTime + it.durationInMinutes * 60 * 1000 > System.currentTimeMillis()
DeliveryStatus.TEMP_BASAL_ACTIVE, } ?: false
DeliveryStatus.BOLUS_AND_TEMP_BASAL_ACTIVE
)
override var basalProgram: BasalProgram? override var basalProgram: BasalProgram?
get() = podState.basalProgram get() = podState.basalProgram
set(basalProgram) { set(basalProgram) {
podState.basalProgram = basalProgram podState.basalProgram = basalProgram
rxBus.send(EventOmnipodDashPumpValuesChanged())
store() store()
} }
@ -188,7 +198,11 @@ class OmnipodDashPodStateManagerImpl @Inject constructor(
get() = podState.activeCommand get() = podState.activeCommand
@Synchronized @Synchronized
override fun createActiveCommand(historyId: String, basalProgram: BasalProgram?): override fun createActiveCommand(
historyId: String,
basalProgram: BasalProgram?,
tempBasal: OmnipodDashPodStateManager.TempBasal?
):
Single<OmnipodDashPodStateManager.ActiveCommand> { Single<OmnipodDashPodStateManager.ActiveCommand> {
return Single.create { source -> return Single.create { source ->
if (activeCommand == null) { if (activeCommand == null) {
@ -198,6 +212,7 @@ class OmnipodDashPodStateManagerImpl @Inject constructor(
historyId = historyId, historyId = historyId,
sendError = null, sendError = null,
basalProgram = basalProgram, basalProgram = basalProgram,
tempBasal = tempBasal,
) )
podState.activeCommand = command podState.activeCommand = command
source.onSuccess(command) source.onSuccess(command)
@ -308,6 +323,7 @@ class OmnipodDashPodStateManagerImpl @Inject constructor(
} }
override fun updateFromDefaultStatusResponse(response: DefaultStatusResponse) { override fun updateFromDefaultStatusResponse(response: DefaultStatusResponse) {
logger.debug(LTag.PUMPBTCOMM, "Default status reponse :$response")
podState.deliveryStatus = response.deliveryStatus podState.deliveryStatus = response.deliveryStatus
podState.podStatus = response.podStatus podState.podStatus = response.podStatus
podState.pulsesDelivered = response.totalPulsesDelivered podState.pulsesDelivered = response.totalPulsesDelivered
@ -374,11 +390,23 @@ class OmnipodDashPodStateManagerImpl @Inject constructor(
} }
override fun updateFromAlarmStatusResponse(response: AlarmStatusResponse) { override fun updateFromAlarmStatusResponse(response: AlarmStatusResponse) {
// TODO logger.info(
logger.error(
LTag.PUMP, LTag.PUMP,
"Not implemented: OmnipodDashPodStateManagerImpl.updateFromAlarmStatusResponse(AlarmStatusResponse)" "Received AlarmStatusReponse: $response"
) )
podState.deliveryStatus = response.deliveryStatus
podState.podStatus = response.podStatus
podState.pulsesDelivered = response.totalPulsesDelivered
if (response.reservoirPulsesRemaining < 1023) {
podState.pulsesRemaining = response.reservoirPulsesRemaining
}
podState.sequenceNumberOfLastProgrammingCommand = response.sequenceNumberOfLastProgrammingCommand
podState.minutesSinceActivation = response.minutesSinceActivation
podState.activeAlerts = response.activeAlerts
podState.alarmType = response.alarmType
podState.lastUpdatedSystem = System.currentTimeMillis()
podState.lastStatusResponseReceived = SystemClock.elapsedRealtime()
store() store()
rxBus.send(EventOmnipodDashPumpValuesChanged()) rxBus.send(EventOmnipodDashPumpValuesChanged())
@ -451,6 +479,7 @@ class OmnipodDashPodStateManagerImpl @Inject constructor(
var deliveryStatus: DeliveryStatus? = null var deliveryStatus: DeliveryStatus? = null
var minutesSinceActivation: Short? = null var minutesSinceActivation: Short? = null
var activeAlerts: EnumSet<AlertType>? = null var activeAlerts: EnumSet<AlertType>? = null
var alarmType: AlarmType? = null
var basalProgram: BasalProgram? = null var basalProgram: BasalProgram? = null
var tempBasal: OmnipodDashPodStateManager.TempBasal? = null var tempBasal: OmnipodDashPodStateManager.TempBasal? = null

View file

@ -8,9 +8,10 @@ object AlertUtil {
fun decodeAlertSet(encoded: Byte): EnumSet<AlertType> { fun decodeAlertSet(encoded: Byte): EnumSet<AlertType> {
val encodedInt = encoded.toInt() and 0xff val encodedInt = encoded.toInt() and 0xff
val alertList = AlertType.values().filter { val alertList = AlertType.values()
(it.value.toInt() and 0xff) and encodedInt != 0 .filter { it != AlertType.UNKNOWN } // 0xff && <something> will always be true
}.toList() .filter { (it.value.toInt() and 0xff) and encodedInt != 0 }
.toList()
return if (alertList.isEmpty()) { return if (alertList.isEmpty()) {
EnumSet.noneOf(AlertType::class.java) EnumSet.noneOf(AlertType::class.java)

View file

@ -212,7 +212,6 @@ class OmnipodDashOverviewFragment : DaggerFragment() {
} }
private fun updateUi() { private fun updateUi() {
// TODO update bluetooth status
updateBluetoothStatus() updateBluetoothStatus()
updateOmnipodStatus() updateOmnipodStatus()
updatePodActionButtons() updatePodActionButtons()
@ -296,6 +295,15 @@ class OmnipodDashOverviewFragment : DaggerFragment() {
errors.add(resourceHelper.gs(R.string.omnipod_common_pod_status_pod_fault_description, faultEventCode.value, faultEventCode.name)) errors.add(resourceHelper.gs(R.string.omnipod_common_pod_status_pod_fault_description, faultEventCode.value, faultEventCode.name))
} }
*/ */
podStateManager.alarmType?.let {
errors.add(
resourceHelper.gs(
R.string.omnipod_common_pod_status_pod_fault_description,
it.value,
it.toString()
)
)
}
// base basal rate // base basal rate
podInfoBinding.baseBasalRate.text = if (podStateManager.basalProgram != null && !podStateManager.isSuspended) { podInfoBinding.baseBasalRate.text = if (podStateManager.basalProgram != null && !podStateManager.isSuspended) {
@ -343,7 +351,9 @@ class OmnipodDashOverviewFragment : DaggerFragment() {
) )
} }
podInfoBinding.podActiveAlerts.text = PLACEHOLDER podInfoBinding.podActiveAlerts.text = podStateManager.activeAlerts?.let {
it.map { it.toString() }.joinToString(",")
} ?: PLACEHOLDER
} }
if (errors.size == 0) { if (errors.size == 0) {
@ -503,7 +513,8 @@ class OmnipodDashOverviewFragment : DaggerFragment() {
} }
private fun updateSilenceAlertsButton() { private fun updateSilenceAlertsButton() {
if (isAutomaticallySilenceAlertsEnabled() && podStateManager.isPodRunning && if (!isAutomaticallySilenceAlertsEnabled() &&
podStateManager.isPodRunning &&
( (
podStateManager.activeAlerts!!.size > 0 || podStateManager.activeAlerts!!.size > 0 ||
commandQueue.isCustomCommandInQueue(CommandSilenceAlerts::class.java) commandQueue.isCustomCommandInQueue(CommandSilenceAlerts::class.java)