From 842f196ae8d1a9f2fa25fcb17876ea945b9c773b Mon Sep 17 00:00:00 2001 From: Andrei Vereha Date: Sun, 18 Apr 2021 21:39:20 +0200 Subject: [PATCH] confirm/deny commands --- .../omnipod/dash/OmnipodDashPumpPlugin.kt | 65 ++++++++++------ .../dash/driver/OmnipodDashManagerImpl.kt | 6 ++ .../pump/omnipod/dash/driver/comm/Id.kt | 1 - .../pump/omnipod/dash/driver/comm/Ids.kt | 2 +- .../driver/comm/OmnipodDashBleManagerImpl.kt | 2 +- .../dash/driver/comm/packet/PayloadJoiner.kt | 2 +- .../dash/driver/comm/pair/LTKExchanger.kt | 22 +++--- .../dash/driver/comm/session/Session.kt | 1 - .../driver/comm/session/SessionEstablisher.kt | 1 - .../omnipod/dash/driver/event/PodEvent.kt | 4 + .../driver/pod/command/ProgramBasalCommand.kt | 2 +- .../dash/driver/pod/command/base/Command.kt | 1 + .../pod/command/base/HeaderEnabledCommand.kt | 2 +- .../pod/state/OmnipodDashPodStateManager.kt | 19 ++++- .../state/OmnipodDashPodStateManagerImpl.kt | 60 +++++++++++++-- .../pump/omnipod/dash/history/DashHistory.kt | 34 ++++++++- .../omnipod/dash/history/data/ResultStates.kt | 2 +- .../dash/history/database/HistoryRecordDao.kt | 4 + .../pod/response/DefaultStatusResponseTest.kt | 76 +++++++++---------- 19 files changed, 212 insertions(+), 94 deletions(-) diff --git a/omnipod-dash/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/dash/OmnipodDashPumpPlugin.kt b/omnipod-dash/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/dash/OmnipodDashPumpPlugin.kt index ca291751d2..e535549a0e 100644 --- a/omnipod-dash/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/dash/OmnipodDashPumpPlugin.kt +++ b/omnipod-dash/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/dash/OmnipodDashPumpPlugin.kt @@ -11,18 +11,23 @@ import info.nightscout.androidaps.plugins.common.ManufacturerType import info.nightscout.androidaps.plugins.general.actions.defs.CustomAction import info.nightscout.androidaps.plugins.general.actions.defs.CustomActionType import info.nightscout.androidaps.plugins.pump.common.defs.PumpType +import info.nightscout.androidaps.plugins.pump.omnipod.common.definition.OmnipodCommandType 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.pod.definition.ActivationProgress import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.definition.BeepType import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.response.ResponseType import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.state.OmnipodDashPodStateManager +import info.nightscout.androidaps.plugins.pump.omnipod.dash.history.DashHistory +import info.nightscout.androidaps.plugins.pump.omnipod.dash.history.data.BolusRecord +import info.nightscout.androidaps.plugins.pump.omnipod.dash.history.data.BolusType import info.nightscout.androidaps.plugins.pump.omnipod.dash.ui.OmnipodDashOverviewFragment import info.nightscout.androidaps.plugins.pump.omnipod.dash.util.mapProfileToBasalProgram import info.nightscout.androidaps.queue.commands.CustomCommand import info.nightscout.androidaps.utils.TimeChangeType import info.nightscout.androidaps.utils.resources.ResourceHelper import info.nightscout.androidaps.utils.sharedPreferences.SP +import io.reactivex.Observable import io.reactivex.Single import io.reactivex.rxkotlin.blockingSubscribeBy import io.reactivex.rxkotlin.subscribeBy @@ -37,6 +42,7 @@ class OmnipodDashPumpPlugin @Inject constructor( private val podStateManager: OmnipodDashPodStateManager, private val sp: SP, private val profileFunction: ProfileFunction, + private val history: DashHistory, injector: HasAndroidInjector, aapsLogger: AAPSLogger, resourceHelper: ResourceHelper, @@ -147,7 +153,6 @@ class OmnipodDashPumpPlugin @Inject constructor( it == mapProfileToBasalProgram(profile) } ?: true - override fun lastDataTime(): Long { return podStateManager.lastConnection } @@ -172,7 +177,6 @@ class OmnipodDashPumpPlugin @Inject constructor( get() = 0 override fun deliverTreatment(detailedBolusInfo: DetailedBolusInfo): PumpEnactResult { - // TODO history // TODO update Treatments (?) // TODO bolus progress // TODO report actual delivered amount after Pod Alarm and bolus cancellation @@ -180,29 +184,44 @@ class OmnipodDashPumpPlugin @Inject constructor( return Single.create { source -> val bolusBeeps = sp.getBoolean(R.string.key_omnipod_common_bolus_beeps_enabled, false) - omnipodManager.bolus( - detailedBolusInfo.insulin, - bolusBeeps, - bolusBeeps - ).subscribeBy( - onNext = { podEvent -> - aapsLogger.debug( - LTag.PUMP, - "Received PodEvent in deliverTreatment: $podEvent" - ) + Observable.concat( + history.createRecord( + commandType = OmnipodCommandType.SET_BOLUS, + bolusRecord = BolusRecord( + detailedBolusInfo.insulin, + if (detailedBolusInfo.isSMB) BolusType.SMB else BolusType.DEFAULT + ), + ).flatMapObservable { + recordId -> + podStateManager.createActiveCommand(recordId).toObservable() }, - onError = { throwable -> - aapsLogger.error(LTag.PUMP, "Error in deliverTreatment", throwable) - source.onSuccess(PumpEnactResult(injector).success(false).enacted(false).comment(throwable.message)) - }, - onComplete = { - aapsLogger.debug("deliverTreatment completed") - source.onSuccess( - PumpEnactResult(injector).success(true).enacted(true).bolusDelivered(detailedBolusInfo.insulin) - .carbsDelivered(detailedBolusInfo.carbs) - ) - } + omnipodManager.bolus( + detailedBolusInfo.insulin, + bolusBeeps, + bolusBeeps + ), + history.updateFromState(podStateManager).toObservable(), + podStateManager.updateActiveCommand().toObservable(), ) + .subscribeBy( + onNext = { podEvent -> + aapsLogger.debug( + LTag.PUMP, + "Received PodEvent in deliverTreatment: $podEvent" + ) + }, + onError = { throwable -> + aapsLogger.error(LTag.PUMP, "Error in deliverTreatment", throwable) + source.onSuccess(PumpEnactResult(injector).success(false).enacted(false).comment(throwable.message)) + }, + onComplete = { + aapsLogger.debug("deliverTreatment completed") + source.onSuccess( + PumpEnactResult(injector).success(true).enacted(true).bolusDelivered(detailedBolusInfo.insulin) + .carbsDelivered(detailedBolusInfo.carbs) + ) + } + ) }.blockingGet() } diff --git a/omnipod-dash/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/dash/driver/OmnipodDashManagerImpl.kt b/omnipod-dash/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/dash/driver/OmnipodDashManagerImpl.kt index 681ff8d43c..8fbb6ca64b 100644 --- a/omnipod-dash/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/dash/driver/OmnipodDashManagerImpl.kt +++ b/omnipod-dash/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/dash/driver/OmnipodDashManagerImpl.kt @@ -1,5 +1,6 @@ package info.nightscout.androidaps.plugins.pump.omnipod.dash.driver +import android.os.SystemClock import info.nightscout.androidaps.logging.AAPSLogger import info.nightscout.androidaps.logging.LTag import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.OmnipodDashBleManager @@ -643,6 +644,11 @@ class OmnipodDashManagerImpl @Inject constructor( } is PodEvent.CommandSent -> { + podStateManager.activeCommand?.let { + if (it.sequence == event.command.sequenceNumber) { + it.sentRealtime = SystemClock.elapsedRealtime() + } + } podStateManager.increaseMessageSequenceNumber() } diff --git a/omnipod-dash/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/dash/driver/comm/Id.kt b/omnipod-dash/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/dash/driver/comm/Id.kt index cccb42a989..e330434faf 100644 --- a/omnipod-dash/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/dash/driver/comm/Id.kt +++ b/omnipod-dash/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/dash/driver/comm/Id.kt @@ -3,7 +3,6 @@ package info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm import info.nightscout.androidaps.utils.extensions.toHex import java.nio.ByteBuffer - data class Id(val address: ByteArray) { init { require(address.size == 4) diff --git a/omnipod-dash/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/dash/driver/comm/Ids.kt b/omnipod-dash/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/dash/driver/comm/Ids.kt index ac3e2e8406..e22ed866db 100644 --- a/omnipod-dash/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/dash/driver/comm/Ids.kt +++ b/omnipod-dash/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/dash/driver/comm/Ids.kt @@ -17,4 +17,4 @@ class Ids(podState: OmnipodDashPodStateManager) { ) } } -} \ No newline at end of file +} diff --git a/omnipod-dash/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/dash/driver/comm/OmnipodDashBleManagerImpl.kt b/omnipod-dash/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/dash/driver/comm/OmnipodDashBleManagerImpl.kt index 55bddace08..76cf563d81 100644 --- a/omnipod-dash/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/dash/driver/comm/OmnipodDashBleManagerImpl.kt +++ b/omnipod-dash/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/dash/driver/comm/OmnipodDashBleManagerImpl.kt @@ -83,7 +83,7 @@ class OmnipodDashBleManagerImpl @Inject constructor( val conn = assertConnected() return conn.session ?: throw NotConnectedException("Missing session") - } + } override fun getStatus(): ConnectionStatus { // TODO is this used? diff --git a/omnipod-dash/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/dash/driver/comm/packet/PayloadJoiner.kt b/omnipod-dash/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/dash/driver/comm/packet/PayloadJoiner.kt index 17ef1a6000..766a19cf5e 100644 --- a/omnipod-dash/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/dash/driver/comm/packet/PayloadJoiner.kt +++ b/omnipod-dash/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/dash/driver/comm/packet/PayloadJoiner.kt @@ -42,7 +42,7 @@ class PayloadJoiner(private val firstPacket: ByteArray) { oneExtraPacket = lastPacket.oneExtraPacket } - idx == fullFragments+1 && oneExtraPacket -> { + idx == fullFragments + 1 && oneExtraPacket -> { fragments.add(LastOptionalPlusOneBlePacket.parse(packet)) } diff --git a/omnipod-dash/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/dash/driver/comm/pair/LTKExchanger.kt b/omnipod-dash/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/dash/driver/comm/pair/LTKExchanger.kt index ef64c71ea7..549d8f034d 100644 --- a/omnipod-dash/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/dash/driver/comm/pair/LTKExchanger.kt +++ b/omnipod-dash/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/dash/driver/comm/pair/LTKExchanger.kt @@ -2,15 +2,12 @@ package info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.pair import info.nightscout.androidaps.logging.AAPSLogger import info.nightscout.androidaps.logging.LTag -import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.Id import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.Ids import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.exceptions.MessageIOException import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.exceptions.PairingException import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.message.MessageIO import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.message.MessagePacket -import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.message.MessageSendErrorSending import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.message.MessageSendSuccess -import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.message.StringLengthPrefixEncoding import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.message.StringLengthPrefixEncoding.Companion.parseKeys import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.util.RandomByteGenerator import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.util.X25519KeyGenerator @@ -35,7 +32,7 @@ internal class LTKExchanger( keys = arrayOf(SP1, SP2), payloads = arrayOf(ids.podId.address, sp2()) ) - throwOnSendError(sp1sp2.messagePacket, SP1+SP2) + throwOnSendError(sp1sp2.messagePacket, SP1 + SP2) seq++ val sps1 = PairMessage( @@ -67,16 +64,16 @@ internal class LTKExchanger( seq++ // send SP0GP0 - val sp0gp0 = PairMessage ( - sequenceNumber = seq, - source = ids.myId, - destination = podAddress, - keys = arrayOf(SP0GP0), - payloads = arrayOf(ByteArray(0)) - ) + val sp0gp0 = PairMessage( + sequenceNumber = seq, + source = ids.myId, + destination = podAddress, + keys = arrayOf(SP0GP0), + payloads = arrayOf(ByteArray(0)) + ) val result = msgIO.sendMessage(sp0gp0.messagePacket) if (result !is MessageSendSuccess) { - aapsLogger.warn(LTag.PUMPBTCOMM,"Error sending SP0GP0: $result") + aapsLogger.warn(LTag.PUMPBTCOMM, "Error sending SP0GP0: $result") } msgIO.receiveMessage() @@ -97,7 +94,6 @@ internal class LTKExchanger( } } - private fun processSps1FromPod(msg: MessagePacket) { aapsLogger.debug(LTag.PUMPBTCOMM, "Received SPS1 from pod: ${msg.payload.toHex()}") diff --git a/omnipod-dash/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/dash/driver/comm/session/Session.kt b/omnipod-dash/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/dash/driver/comm/session/Session.kt index abf138e673..a549b2fe3d 100644 --- a/omnipod-dash/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/dash/driver/comm/session/Session.kt +++ b/omnipod-dash/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/dash/driver/comm/session/Session.kt @@ -2,7 +2,6 @@ package info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.session import info.nightscout.androidaps.logging.AAPSLogger import info.nightscout.androidaps.logging.LTag -import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.Id 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.exceptions.CouldNotParseResponseException diff --git a/omnipod-dash/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/dash/driver/comm/session/SessionEstablisher.kt b/omnipod-dash/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/dash/driver/comm/session/SessionEstablisher.kt index 355cab8daf..deb430d65d 100644 --- a/omnipod-dash/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/dash/driver/comm/session/SessionEstablisher.kt +++ b/omnipod-dash/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/dash/driver/comm/session/SessionEstablisher.kt @@ -2,7 +2,6 @@ package info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.session import info.nightscout.androidaps.logging.AAPSLogger import info.nightscout.androidaps.logging.LTag -import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.Id import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.Ids import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.endecrypt.Nonce import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.exceptions.SessionEstablishmentException diff --git a/omnipod-dash/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/dash/driver/event/PodEvent.kt b/omnipod-dash/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/dash/driver/event/PodEvent.kt index 08d33a43fe..dfee0dfcb5 100644 --- a/omnipod-dash/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/dash/driver/event/PodEvent.kt +++ b/omnipod-dash/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/dash/driver/event/PodEvent.kt @@ -65,4 +65,8 @@ sealed class PodEvent { return "ResponseReceived(command=$command, response=$response)" } } + + data class CommandConfirmed(val historyId: String) : PodEvent() + + data class CommandDenied(val historyId: String) : PodEvent() } diff --git a/omnipod-dash/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/dash/driver/pod/command/ProgramBasalCommand.kt b/omnipod-dash/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/dash/driver/pod/command/ProgramBasalCommand.kt index f717659dbb..f8a15f7178 100644 --- a/omnipod-dash/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/dash/driver/pod/command/ProgramBasalCommand.kt +++ b/omnipod-dash/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/dash/driver/pod/command/ProgramBasalCommand.kt @@ -31,7 +31,7 @@ class ProgramBasalCommand private constructor( private val delayUntilNextTenthPulseInUsec: Int val length: Short get() = (insulinProgramElements.size * 6 + 10).toShort() - val bodyLength: Byte + private val bodyLength: Byte get() = (insulinProgramElements.size * 6 + 8).toByte() override val encoded: ByteArray diff --git a/omnipod-dash/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/dash/driver/pod/command/base/Command.kt b/omnipod-dash/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/dash/driver/pod/command/base/Command.kt index 0b509a8ae1..11f0836ab8 100644 --- a/omnipod-dash/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/dash/driver/pod/command/base/Command.kt +++ b/omnipod-dash/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/dash/driver/pod/command/base/Command.kt @@ -6,4 +6,5 @@ import java.io.Serializable interface Command : Encodable, Serializable { val commandType: CommandType + val sequenceNumber: Short } diff --git a/omnipod-dash/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/dash/driver/pod/command/base/HeaderEnabledCommand.kt b/omnipod-dash/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/dash/driver/pod/command/base/HeaderEnabledCommand.kt index a4507b9d1a..c921424883 100644 --- a/omnipod-dash/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/dash/driver/pod/command/base/HeaderEnabledCommand.kt +++ b/omnipod-dash/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/dash/driver/pod/command/base/HeaderEnabledCommand.kt @@ -6,7 +6,7 @@ import java.nio.ByteBuffer abstract class HeaderEnabledCommand protected constructor( override val commandType: CommandType, protected val uniqueId: Int, - protected val sequenceNumber: Short, + override val sequenceNumber: Short, protected val multiCommandFlag: Boolean ) : Command { diff --git a/omnipod-dash/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/dash/driver/pod/state/OmnipodDashPodStateManager.kt b/omnipod-dash/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/dash/driver/pod/state/OmnipodDashPodStateManager.kt index 6f2a66300d..67bcac8209 100644 --- a/omnipod-dash/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/dash/driver/pod/state/OmnipodDashPodStateManager.kt +++ b/omnipod-dash/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/dash/driver/pod/state/OmnipodDashPodStateManager.kt @@ -2,11 +2,14 @@ package info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.state 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 import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.definition.* import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.response.AlarmStatusResponse import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.response.DefaultStatusResponse import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.response.SetUniqueIdResponse import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.response.VersionResponse +import io.reactivex.Completable +import io.reactivex.Maybe import java.io.Serializable import java.util.* @@ -18,7 +21,10 @@ interface OmnipodDashPodStateManager { val isSuspended: Boolean val isPodRunning: Boolean var lastConnection: Long - val lastUpdated: Long + + val lastUpdatedSystem: Long // System.currentTimeMillis() + // TODO: set lastUpdatedRealtime to 0 on boot + val lastStatusResponseReceived: Long val messageSequenceNumber: Short val sequenceNumberOfLastProgrammingCommand: Short? @@ -48,6 +54,7 @@ interface OmnipodDashPodStateManager { val tempBasal: TempBasal? val tempBasalActive: Boolean var basalProgram: BasalProgram? + val activeCommand: ActiveCommand? fun increaseMessageSequenceNumber() fun increaseEapAkaSequenceNumber(): ByteArray @@ -59,5 +66,15 @@ interface OmnipodDashPodStateManager { fun updateFromPairing(uniqueId: Id, pairResult: PairResult) fun reset() + fun createActiveCommand(historyId: String): Completable + fun updateActiveCommand(): Maybe + + data class ActiveCommand( + val sequence: Short, + val createdRealtime: Long, + var sentRealtime: Long = 0, + val historyId: String + ) + // TODO: set created to "now" on boot data class TempBasal(val startTime: Long, val rate: Double, val durationInMinutes: Short) : Serializable } diff --git a/omnipod-dash/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/dash/driver/pod/state/OmnipodDashPodStateManagerImpl.kt b/omnipod-dash/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/dash/driver/pod/state/OmnipodDashPodStateManagerImpl.kt index 8fcd23b213..bc9c433f83 100644 --- a/omnipod-dash/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/dash/driver/pod/state/OmnipodDashPodStateManagerImpl.kt +++ b/omnipod-dash/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/dash/driver/pod/state/OmnipodDashPodStateManagerImpl.kt @@ -1,5 +1,6 @@ package info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.state +import android.os.SystemClock import com.google.gson.Gson import info.nightscout.androidaps.logging.AAPSLogger import info.nightscout.androidaps.logging.LTag @@ -9,12 +10,15 @@ import info.nightscout.androidaps.plugins.pump.omnipod.dash.R 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.comm.session.EapSqn +import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.event.PodEvent import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.definition.* import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.response.AlarmStatusResponse import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.response.DefaultStatusResponse import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.response.SetUniqueIdResponse import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.response.VersionResponse import info.nightscout.androidaps.utils.sharedPreferences.SP +import io.reactivex.Completable +import io.reactivex.Maybe import java.io.Serializable import java.util.* import javax.inject.Inject @@ -60,8 +64,8 @@ class OmnipodDashPodStateManagerImpl @Inject constructor( store() } - override val lastUpdated: Long - get() = podState.lastUpdated + override val lastUpdatedSystem: Long + get() = podState.lastUpdatedSystem override val messageSequenceNumber: Short get() = podState.messageSequenceNumber @@ -152,6 +156,9 @@ class OmnipodDashPodStateManagerImpl @Inject constructor( store() } + override val lastStatusResponseReceived: Long + get() = podState.lastStatusResponseReceived + override fun increaseMessageSequenceNumber() { podState.messageSequenceNumber = ((podState.messageSequenceNumber.toInt() + 1) and 0x0f).toShort() store() @@ -171,6 +178,41 @@ class OmnipodDashPodStateManagerImpl @Inject constructor( store() } + override val activeCommand: OmnipodDashPodStateManager.ActiveCommand? + get() = podState.activeCommand + + @Synchronized + override fun createActiveCommand(historyId: String): Completable { + return if (activeCommand == null) { + podState.activeCommand = OmnipodDashPodStateManager.ActiveCommand( + podState.messageSequenceNumber, + createdRealtime = SystemClock.elapsedRealtime(), + historyId = historyId + ) + Completable.complete() + } else { + Completable.error( + java.lang.IllegalStateException( + "Trying to send a command " + + "and the last command was not confirmed" + ) + ) + } + } + + @Synchronized + override fun updateActiveCommand(): Maybe { + return podState.activeCommand?.run { + if (createdRealtime >= lastStatusResponseReceived) + Maybe.empty() + else if (sequenceNumberOfLastProgrammingCommand == sequence) + Maybe.just(PodEvent.CommandConfirmed(historyId)) + else + Maybe.just(PodEvent.CommandDenied(historyId)) + } + ?: Maybe.empty() // no active programming command + } + override fun increaseEapAkaSequenceNumber(): ByteArray { podState.eapAkaSequenceNumber++ return EapSqn(podState.eapAkaSequenceNumber).value @@ -191,7 +233,9 @@ class OmnipodDashPodStateManagerImpl @Inject constructor( podState.minutesSinceActivation = response.minutesSinceActivation podState.activeAlerts = response.activeAlerts - podState.lastUpdated = System.currentTimeMillis() + podState.lastUpdatedSystem = System.currentTimeMillis() + podState.lastStatusResponseReceived = SystemClock.elapsedRealtime() + store() rxBus.send(EventOmnipodDashPumpValuesChanged()) } @@ -211,7 +255,8 @@ class OmnipodDashPodStateManagerImpl @Inject constructor( podState.lotNumber = response.lotNumber podState.podSequenceNumber = response.podSequenceNumber - podState.lastUpdated = System.currentTimeMillis() + podState.lastUpdatedSystem = System.currentTimeMillis() + store() rxBus.send(EventOmnipodDashPumpValuesChanged()) } @@ -237,7 +282,8 @@ class OmnipodDashPodStateManagerImpl @Inject constructor( podState.podSequenceNumber = response.podSequenceNumber podState.uniqueId = response.uniqueIdReceivedInCommand - podState.lastUpdated = System.currentTimeMillis() + podState.lastUpdatedSystem = System.currentTimeMillis() + store() rxBus.send(EventOmnipodDashPumpValuesChanged()) } @@ -293,7 +339,8 @@ class OmnipodDashPodStateManagerImpl @Inject constructor( var activationProgress: ActivationProgress = ActivationProgress.NOT_STARTED var lastConnection: Long = 0 - var lastUpdated: Long = 0 + var lastUpdatedSystem: Long = 0 + var lastStatusResponseReceived: Long = 0 var messageSequenceNumber: Short = 0 var sequenceNumberOfLastProgrammingCommand: Short? = null @@ -322,5 +369,6 @@ class OmnipodDashPodStateManagerImpl @Inject constructor( var basalProgram: BasalProgram? = null var tempBasal: OmnipodDashPodStateManager.TempBasal? = null + var activeCommand: OmnipodDashPodStateManager.ActiveCommand? = null } } diff --git a/omnipod-dash/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/dash/history/DashHistory.kt b/omnipod-dash/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/dash/history/DashHistory.kt index f2af4c972c..a7161169e6 100644 --- a/omnipod-dash/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/dash/history/DashHistory.kt +++ b/omnipod-dash/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/dash/history/DashHistory.kt @@ -4,6 +4,7 @@ import com.github.guepardoapps.kulid.ULID import info.nightscout.androidaps.plugins.pump.omnipod.common.definition.OmnipodCommandType import info.nightscout.androidaps.plugins.pump.omnipod.common.definition.OmnipodCommandType.SET_BOLUS import info.nightscout.androidaps.plugins.pump.omnipod.common.definition.OmnipodCommandType.SET_TEMPORARY_BASAL +import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.state.OmnipodDashPodStateManager import info.nightscout.androidaps.plugins.pump.omnipod.dash.history.data.BolusRecord import info.nightscout.androidaps.plugins.pump.omnipod.dash.history.data.HistoryRecord import info.nightscout.androidaps.plugins.pump.omnipod.dash.history.data.InitialResult @@ -22,13 +23,13 @@ class DashHistory @Inject constructor( private val historyMapper: HistoryMapper ) { - fun markSuccess(id: String, date: Long): Completable = dao.markResolved( + private fun markSuccess(id: String): Completable = dao.markResolved( id, ResolvedResult.SUCCESS, currentTimeMillis() ) - fun markFailure(id: String, date: Long): Completable = dao.markResolved( + private fun markFailure(id: String): Completable = dao.markResolved( id, ResolvedResult.FAILURE, currentTimeMillis() @@ -37,8 +38,8 @@ class DashHistory @Inject constructor( @Suppress("ReturnCount") fun createRecord( commandType: OmnipodCommandType, - date: Long, - initialResult: InitialResult = InitialResult.UNCONFIRMED, + date: Long = System.currentTimeMillis(), + initialResult: InitialResult = InitialResult.CREATED, tempBasalRecord: TempBasalRecord? = null, bolusRecord: BolusRecord? = null, resolveResult: ResolvedResult? = null, @@ -72,4 +73,29 @@ class DashHistory @Inject constructor( dao.all().map { list -> list.map(historyMapper::entityToDomain) } fun getRecordsAfter(time: Long): Single> = dao.allSince(time) + + fun updateFromState(podState: OmnipodDashPodStateManager): Completable { + return podState.activeCommand?.run { + when { + + createdRealtime <= podState.lastStatusResponseReceived && + sequence == podState.sequenceNumberOfLastProgrammingCommand -> + dao.setInitialResult(historyId, InitialResult.SENT) + .andThen(markSuccess(historyId)) + + createdRealtime <= podState.lastStatusResponseReceived && + sequence != podState.sequenceNumberOfLastProgrammingCommand -> + markFailure(historyId) + + // no response received after this point + createdRealtime <= sentRealtime -> + dao.setInitialResult(historyId, InitialResult.SENT) + + createdRealtime > sentRealtime -> + dao.setInitialResult(historyId, InitialResult.FAILURE_SENDING) + + else -> Completable.error(IllegalStateException("This can't happen. Could not update history")) + } + } ?: Completable.complete() // no active programming command + } } diff --git a/omnipod-dash/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/dash/history/data/ResultStates.kt b/omnipod-dash/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/dash/history/data/ResultStates.kt index 8f0c353763..c7b134c5fb 100644 --- a/omnipod-dash/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/dash/history/data/ResultStates.kt +++ b/omnipod-dash/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/dash/history/data/ResultStates.kt @@ -1,7 +1,7 @@ package info.nightscout.androidaps.plugins.pump.omnipod.dash.history.data enum class InitialResult { - SUCCESS, FAILURE, UNCONFIRMED + CREATED, FAILURE_SENDING, UNCONFIRMED, SENT } enum class ResolvedResult { diff --git a/omnipod-dash/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/dash/history/database/HistoryRecordDao.kt b/omnipod-dash/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/dash/history/database/HistoryRecordDao.kt index 93b292c067..21ff7dbced 100644 --- a/omnipod-dash/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/dash/history/database/HistoryRecordDao.kt +++ b/omnipod-dash/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/dash/history/database/HistoryRecordDao.kt @@ -5,6 +5,7 @@ import androidx.room.Delete import androidx.room.Insert import androidx.room.OnConflictStrategy import androidx.room.Query +import info.nightscout.androidaps.plugins.pump.omnipod.dash.history.data.InitialResult import info.nightscout.androidaps.plugins.pump.omnipod.dash.history.data.ResolvedResult import io.reactivex.Completable import io.reactivex.Single @@ -32,4 +33,7 @@ abstract class HistoryRecordDao { @Query("UPDATE historyrecords SET resolvedResult = :resolvedResult, resolvedAt = :resolvedAt WHERE id = :id ") abstract fun markResolved(id: String, resolvedResult: ResolvedResult, resolvedAt: Long): Completable + + @Query("UPDATE historyrecords SET initialResult = :initialResult WHERE id = :id ") + abstract fun setInitialResult(id: String, initialResult: InitialResult): Completable } diff --git a/omnipod-dash/src/test/java/info/nightscout/androidaps/plugins/pump/omnipod/dash/driver/pod/response/DefaultStatusResponseTest.kt b/omnipod-dash/src/test/java/info/nightscout/androidaps/plugins/pump/omnipod/dash/driver/pod/response/DefaultStatusResponseTest.kt index 9de6513fd4..bcce591db4 100644 --- a/omnipod-dash/src/test/java/info/nightscout/androidaps/plugins/pump/omnipod/dash/driver/pod/response/DefaultStatusResponseTest.kt +++ b/omnipod-dash/src/test/java/info/nightscout/androidaps/plugins/pump/omnipod/dash/driver/pod/response/DefaultStatusResponseTest.kt @@ -28,25 +28,25 @@ class DefaultStatusResponseTest { /** * response (hex) 08202EAA0C0A1D1905281000004387D3039A - Status response: 29 - Pod status: RUNNING_BELOW_MIN_VOLUME - Basal active: true - Temp Basal active: false - Immediate bolus active: false - Extended bolus active: false - Bolus pulses remaining: 0 - sequence number of last programing command: 2 - Total full pulses delivered: 2640 - Full reservoir pulses remaining: 979 - Time since activation: 4321 - Alert 1 is InActive - Alert 2 is InActive - Alert 3 is InActive - Alert 4 is InActive - Alert 5 is InActive - Alert 6 is InActive - Alert 7 is InActive - Occlusion alert active false + Status response: 29 + Pod status: RUNNING_BELOW_MIN_VOLUME + Basal active: true + Temp Basal active: false + Immediate bolus active: false + Extended bolus active: false + Bolus pulses remaining: 0 + sequence number of last programing command: 2 + Total full pulses delivered: 2640 + Full reservoir pulses remaining: 979 + Time since activation: 4321 + Alert 1 is InActive + Alert 2 is InActive + Alert 3 is InActive + Alert 4 is InActive + Alert 5 is InActive + Alert 6 is InActive + Alert 7 is InActive + Occlusion alert active false */ @Test @Throws(DecoderException::class) fun testValidResponseBelowMin() { val encoded = Hex.decodeHex("1D1905281000004387D3039A") @@ -67,25 +67,25 @@ class DefaultStatusResponseTest { /** * response (hex) 08202EAA080A1D180519C00E0039A7FF8085 - Status response: 29 - Pod status: RUNNING_ABOVE_MIN_VOLUME - Basal active: true - Temp Basal active: false - Immediate bolus active: false - Extended bolus active: false - Bolus pulses remaining: 14 - sequence number of last programing command: 8 - Total full pulses delivered: 2611 - Full reservoir pulses remaining: 1023 - Time since activation: 3689 - Alert 1 is InActive - Alert 2 is InActive - Alert 3 is InActive - Alert 4 is InActive - Alert 5 is InActive - Alert 6 is InActive - Alert 7 is InActive - Occlusion alert active false + Status response: 29 + Pod status: RUNNING_ABOVE_MIN_VOLUME + Basal active: true + Temp Basal active: false + Immediate bolus active: false + Extended bolus active: false + Bolus pulses remaining: 14 + sequence number of last programing command: 8 + Total full pulses delivered: 2611 + Full reservoir pulses remaining: 1023 + Time since activation: 3689 + Alert 1 is InActive + Alert 2 is InActive + Alert 3 is InActive + Alert 4 is InActive + Alert 5 is InActive + Alert 6 is InActive + Alert 7 is InActive + Occlusion alert active false */ @Test @Throws(DecoderException::class) fun testValidResponseBolusPulsesRemaining() { val encoded = Hex.decodeHex("1D180519C00E0039A7FF8085")