From aff38851e1a60cbe582b24267030361dae3be6df Mon Sep 17 00:00:00 2001 From: Bart Sopers Date: Sun, 14 Mar 2021 20:14:11 +0100 Subject: [PATCH 01/13] Add Omnipod Dash response parsing --- .../driver/comm/OmnipodDashBleManagerImpl.kt | 1 + .../exceptions/IllegalResponseException.kt | 3 ++ .../dash/driver/comm/session/ResponseUtil.kt | 44 +++++++++++++++++++ .../dash/driver/comm/session/Session.kt | 7 ++- 4 files changed, 53 insertions(+), 2 deletions(-) create mode 100644 omnipod-dash/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/dash/driver/comm/exceptions/IllegalResponseException.kt create mode 100644 omnipod-dash/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/dash/driver/comm/session/ResponseUtil.kt 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 26e5475b1b..b5f794a946 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 @@ -90,6 +90,7 @@ class OmnipodDashBleManagerImpl @Inject constructor( return bleIO } + @Throws(IllegalResponseException::class, UnsupportedOperationException::class) override fun sendCommand(cmd: Command): Observable = Observable.create { emitter -> try { val keys = sessionKeys diff --git a/omnipod-dash/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/dash/driver/comm/exceptions/IllegalResponseException.kt b/omnipod-dash/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/dash/driver/comm/exceptions/IllegalResponseException.kt new file mode 100644 index 0000000000..76f0b639ec --- /dev/null +++ b/omnipod-dash/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/dash/driver/comm/exceptions/IllegalResponseException.kt @@ -0,0 +1,3 @@ +package info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.exceptions + +class IllegalResponseException(message: String?) : Exception(message) \ No newline at end of file diff --git a/omnipod-dash/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/dash/driver/comm/session/ResponseUtil.kt b/omnipod-dash/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/dash/driver/comm/session/ResponseUtil.kt new file mode 100644 index 0000000000..225cd5516a --- /dev/null +++ b/omnipod-dash/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/dash/driver/comm/session/ResponseUtil.kt @@ -0,0 +1,44 @@ +package info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.session + +import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.exceptions.IllegalResponseException +import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.response.* +import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.util.byValue + +object ResponseUtil { + + @Throws(IllegalResponseException::class, UnsupportedOperationException::class) + fun parseResponse(payload: ByteArray): Response { + return when (val responseType = byValue(payload[0], ResponseType.UNKNOWN)) { + ResponseType.ACTIVATION_RESPONSE -> parseActivationResponse(payload) + ResponseType.DEFAULT_STATUS_RESPONSE -> DefaultStatusResponse(payload) + ResponseType.ADDITIONAL_STATUS_RESPONSE -> parseAdditionalStatusResponse(payload) + ResponseType.NAK_RESPONSE -> NakResponse(payload) + ResponseType.UNKNOWN -> throw IllegalResponseException("Unrecognized message type: $responseType") + } + } + + @Throws(IllegalResponseException::class) + private fun parseActivationResponse(payload: ByteArray): Response { + return when (val activationResponseType = byValue(payload[1], ResponseType.ActivationResponseType.UNKNOWN)) { + ResponseType.ActivationResponseType.GET_VERSION_RESPONSE -> VersionResponse(payload) + ResponseType.ActivationResponseType.SET_UNIQUE_ID_RESPONSE -> SetUniqueIdResponse(payload) + ResponseType.ActivationResponseType.UNKNOWN -> throw IllegalResponseException("Unrecognized activation response type: $activationResponseType") + } + } + + @Throws(IllegalResponseException::class, UnsupportedOperationException::class) + private fun parseAdditionalStatusResponse(payload: ByteArray): Response { + return when (val additionalStatusResponseType = byValue(payload[2], ResponseType.StatusResponseType.UNKNOWN)) { + ResponseType.StatusResponseType.DEFAULT_STATUS_RESPONSE -> DefaultStatusResponse(payload) // Unreachable; this response type is only used for requesting a default status response + ResponseType.StatusResponseType.STATUS_RESPONSE_PAGE_1 -> throw UnsupportedOperationException("Status response page 1 is not (yet) implemented") + ResponseType.StatusResponseType.ALARM_STATUS -> AlarmStatusResponse(payload) + ResponseType.StatusResponseType.STATUS_RESPONSE_PAGE_3 -> throw UnsupportedOperationException("Status response page 3 is not (yet) implemented") + ResponseType.StatusResponseType.STATUS_RESPONSE_PAGE_5 -> throw UnsupportedOperationException("Status response page 5 is not (yet) implemented") + ResponseType.StatusResponseType.STATUS_RESPONSE_PAGE_6 -> throw UnsupportedOperationException("Status response page 6 is not (yet) implemented") + ResponseType.StatusResponseType.STATUS_RESPONSE_PAGE_70 -> throw UnsupportedOperationException("Status response page 70 is not (yet) implemented") + ResponseType.StatusResponseType.STATUS_RESPONSE_PAGE_80 -> throw UnsupportedOperationException("Status response page 80 is not (yet) implemented") + ResponseType.StatusResponseType.STATUS_RESPONSE_PAGE_81 -> throw UnsupportedOperationException("Status response page 81 is not (yet) implemented") + ResponseType.StatusResponseType.UNKNOWN -> throw IllegalResponseException("Unrecognized additional status response type: $additionalStatusResponseType") + } + } +} \ No newline at end of file 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 d1e0064b62..ac04803ea0 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 @@ -4,13 +4,13 @@ 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.endecrypt.EnDecrypt +import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.exceptions.IllegalResponseException 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.MessageType 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.command.base.Command -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.utils.extensions.toHex @@ -29,6 +29,7 @@ class Session( * <- response, ACK TODO: retries? * -> ACK */ + @Throws(IllegalResponseException::class, UnsupportedOperationException::class) fun sendCommand(cmd: Command): Response { sessionKeys.msgSequenceNumber++ aapsLogger.debug(LTag.PUMPBTCOMM, "Sending command: ${cmd.encoded.toHex()} in packet $cmd") @@ -49,11 +50,13 @@ class Session( return response } + @Throws(IllegalResponseException::class, UnsupportedOperationException::class) private fun parseResponse(decrypted: MessagePacket): Response { val payload = parseKeys(arrayOf(RESPONSE_PREFIX), decrypted.payload)[0] aapsLogger.info(LTag.PUMPBTCOMM, "Received decrypted response: ${payload.toHex()} in packet: $decrypted") - return NakResponse(payload) + + return ResponseUtil.parseResponse(payload) } private fun getAck(response: MessagePacket): MessagePacket { From 10c316edd9f3554e5e9df76bf8ead868d0876998 Mon Sep 17 00:00:00 2001 From: Bart Sopers Date: Sun, 14 Mar 2021 21:25:15 +0100 Subject: [PATCH 02/13] Omnipod Dash: provide and verify expected response type --- .../dash/driver/OmnipodDashManagerImpl.kt | 27 ++++++-- .../dash/driver/comm/OmnipodDashBleManager.kt | 4 +- .../driver/comm/OmnipodDashBleManagerImpl.kt | 65 ++++++++++--------- .../CouldNotParseResponseException.kt | 3 + .../exceptions/IllegalResponseException.kt | 8 ++- .../comm/exceptions/PodAlarmException.kt | 5 ++ .../dash/driver/comm/session/ResponseUtil.kt | 14 ++-- .../dash/driver/comm/session/Session.kt | 17 ++++- 8 files changed, 92 insertions(+), 51 deletions(-) create mode 100644 omnipod-dash/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/dash/driver/comm/exceptions/CouldNotParseResponseException.kt create mode 100644 omnipod-dash/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/dash/driver/comm/exceptions/PodAlarmException.kt 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 08b79f9d79..53f86bc677 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 @@ -10,7 +10,6 @@ import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.definitio import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.response.* import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.state.OmnipodDashPodStateManager import info.nightscout.androidaps.utils.rx.AapsSchedulers -import info.nightscout.androidaps.utils.rx.retryWithBackoff import io.reactivex.Observable import io.reactivex.functions.Action import io.reactivex.functions.Consumer @@ -72,19 +71,29 @@ class OmnipodDashManagerImpl @Inject constructor( .setNumberOfUnits(units) .setDelayBetweenPulsesInEighthSeconds(rateInEighthPulsesPerSeconds) .setProgramReminder(ProgramReminder(confirmationBeeps, completionBeeps, 0)) - .build() + .build(), + DefaultStatusResponse::class ) } } private fun observeSendGetPodStatusCommand(type: ResponseType.StatusResponseType = ResponseType.StatusResponseType.DEFAULT_STATUS_RESPONSE): Observable { + // TODO move somewhere else + val expectedResponseType = when (type) { + ResponseType.StatusResponseType.DEFAULT_STATUS_RESPONSE -> DefaultStatusResponse::class + ResponseType.StatusResponseType.ALARM_STATUS -> AlarmStatusResponse::class + + else -> return Observable.error(UnsupportedOperationException("No response type to class mapping for ${type.name}")) + } + return Observable.defer { bleManager.sendCommand( GetStatusCommand.Builder() .setUniqueId(podStateManager.uniqueId!!.toInt()) .setSequenceNumber(podStateManager.messageSequenceNumber) .setStatusResponseType(type) - .build() + .build(), + expectedResponseType ) } } @@ -115,7 +124,8 @@ class OmnipodDashManagerImpl @Inject constructor( .setSequenceNumber(podStateManager.messageSequenceNumber) .setNonce(1229869870) // TODO .setAlertConfigurations(alertConfigurations) - .build() + .build(), + DefaultStatusResponse::class ) } } @@ -130,7 +140,8 @@ class OmnipodDashManagerImpl @Inject constructor( .setProgramReminder(ProgramReminder(atStart = false, atEnd = false, atInterval = 0)) .setBasalProgram(basalProgram) .setCurrentTime(Date()) - .build() + .build(), + DefaultStatusResponse::class ) } } @@ -159,7 +170,8 @@ class OmnipodDashManagerImpl @Inject constructor( .setLotNumber(podStateManager.lotNumber!!.toInt()) // .setPodSequenceNumber(podStateManager.podSequenceNumber!!.toInt()) .setInitializationTime(Date()) - .build() + .build(), + SetUniqueIdResponse::class ) // } @@ -169,7 +181,8 @@ class OmnipodDashManagerImpl @Inject constructor( GetVersionCommand.Builder() // .setSequenceNumber(podStateManager.messageSequenceNumber) // .setUniqueId(DEFAULT_UNIQUE_ID) // - .build() + .build(), + VersionResponse::class ) // } diff --git a/omnipod-dash/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/dash/driver/comm/OmnipodDashBleManager.kt b/omnipod-dash/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/dash/driver/comm/OmnipodDashBleManager.kt index 814685ccd0..f3708c8031 100644 --- a/omnipod-dash/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/dash/driver/comm/OmnipodDashBleManager.kt +++ b/omnipod-dash/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/dash/driver/comm/OmnipodDashBleManager.kt @@ -3,11 +3,13 @@ package info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.status.ConnectionStatus import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.event.PodEvent import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.command.base.Command +import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.response.Response import io.reactivex.Observable +import kotlin.reflect.KClass interface OmnipodDashBleManager { - fun sendCommand(cmd: Command): Observable + fun sendCommand(cmd: Command, responseType: KClass): Observable fun getStatus(): ConnectionStatus 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 b5f794a946..333cb5a9f7 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 @@ -24,6 +24,7 @@ import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.session. import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.status.ConnectionStatus import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.event.PodEvent import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.command.base.Command +import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.response.Response import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.state.OmnipodDashPodStateManager import info.nightscout.androidaps.utils.extensions.toHex import io.reactivex.Observable @@ -32,6 +33,7 @@ import java.util.concurrent.LinkedBlockingDeque import java.util.concurrent.TimeoutException import javax.inject.Inject import javax.inject.Singleton +import kotlin.reflect.KClass @Singleton class OmnipodDashBleManagerImpl @Inject constructor( @@ -90,40 +92,39 @@ class OmnipodDashBleManagerImpl @Inject constructor( return bleIO } - @Throws(IllegalResponseException::class, UnsupportedOperationException::class) - override fun sendCommand(cmd: Command): Observable = Observable.create { emitter -> - try { - val keys = sessionKeys - val mIO = msgIO - if (keys == null || mIO == null) { - throw Exception("Not connected") + override fun sendCommand(cmd: Command, responseType: KClass): Observable = + Observable.create { emitter -> + try { + val keys = sessionKeys + val mIO = msgIO + if (keys == null || mIO == null) { + throw Exception("Not connected") + } + emitter.onNext(PodEvent.CommandSending(cmd)) + // TODO switch to RX + emitter.onNext(PodEvent.CommandSent(cmd)) + + val enDecrypt = EnDecrypt( + aapsLogger, + keys.nonce, + keys.ck + ) + + val session = Session( + aapsLogger = aapsLogger, + msgIO = mIO, + myId = myId, + podId = podId, + sessionKeys = keys, + enDecrypt = enDecrypt + ) + val response = session.sendCommand(cmd, responseType) + emitter.onNext(PodEvent.ResponseReceived(response)) + emitter.onComplete() + } catch (ex: Exception) { + emitter.tryOnError(ex) } - emitter.onNext(PodEvent.CommandSending(cmd)) - // TODO switch to RX - emitter.onNext(PodEvent.CommandSent(cmd)) - - val enDecrypt = EnDecrypt( - aapsLogger, - keys.nonce, - keys.ck - ) - - val session = Session( - aapsLogger = aapsLogger, - msgIO = mIO, - myId = myId, - podId = podId, - sessionKeys = keys, - enDecrypt = enDecrypt - ) - val response = session.sendCommand(cmd) - emitter.onNext(PodEvent.ResponseReceived(response)) - - emitter.onComplete() - } catch (ex: Exception) { - emitter.tryOnError(ex) } - } override fun getStatus(): ConnectionStatus { var s: ConnectionStatus diff --git a/omnipod-dash/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/dash/driver/comm/exceptions/CouldNotParseResponseException.kt b/omnipod-dash/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/dash/driver/comm/exceptions/CouldNotParseResponseException.kt new file mode 100644 index 0000000000..7279052046 --- /dev/null +++ b/omnipod-dash/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/dash/driver/comm/exceptions/CouldNotParseResponseException.kt @@ -0,0 +1,3 @@ +package info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.exceptions + +class CouldNotParseResponseException(message: String?) : Exception(message) \ No newline at end of file diff --git a/omnipod-dash/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/dash/driver/comm/exceptions/IllegalResponseException.kt b/omnipod-dash/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/dash/driver/comm/exceptions/IllegalResponseException.kt index 76f0b639ec..10b21a9355 100644 --- a/omnipod-dash/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/dash/driver/comm/exceptions/IllegalResponseException.kt +++ b/omnipod-dash/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/dash/driver/comm/exceptions/IllegalResponseException.kt @@ -1,3 +1,9 @@ package info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.exceptions -class IllegalResponseException(message: String?) : Exception(message) \ No newline at end of file +import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.response.Response +import kotlin.reflect.KClass + +class IllegalResponseException( + expectedResponseType: KClass, + actualResponse: Response +) : Exception("Illegal response: expected ${expectedResponseType.simpleName} but got $actualResponse") \ No newline at end of file diff --git a/omnipod-dash/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/dash/driver/comm/exceptions/PodAlarmException.kt b/omnipod-dash/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/dash/driver/comm/exceptions/PodAlarmException.kt new file mode 100644 index 0000000000..4d6cd067b9 --- /dev/null +++ b/omnipod-dash/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/dash/driver/comm/exceptions/PodAlarmException.kt @@ -0,0 +1,5 @@ +package info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.exceptions + +import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.response.AlarmStatusResponse + +class PodAlarmException(val response: AlarmStatusResponse) : Exception("Pod is in alarm: ${response.alarmType.value} ${response.alarmType.name}") \ No newline at end of file diff --git a/omnipod-dash/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/dash/driver/comm/session/ResponseUtil.kt b/omnipod-dash/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/dash/driver/comm/session/ResponseUtil.kt index 225cd5516a..ec2599b56d 100644 --- a/omnipod-dash/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/dash/driver/comm/session/ResponseUtil.kt +++ b/omnipod-dash/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/dash/driver/comm/session/ResponseUtil.kt @@ -1,32 +1,32 @@ package info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.session -import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.exceptions.IllegalResponseException +import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.exceptions.CouldNotParseResponseException import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.response.* import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.util.byValue object ResponseUtil { - @Throws(IllegalResponseException::class, UnsupportedOperationException::class) + @Throws(CouldNotParseResponseException::class, UnsupportedOperationException::class) fun parseResponse(payload: ByteArray): Response { return when (val responseType = byValue(payload[0], ResponseType.UNKNOWN)) { ResponseType.ACTIVATION_RESPONSE -> parseActivationResponse(payload) ResponseType.DEFAULT_STATUS_RESPONSE -> DefaultStatusResponse(payload) ResponseType.ADDITIONAL_STATUS_RESPONSE -> parseAdditionalStatusResponse(payload) ResponseType.NAK_RESPONSE -> NakResponse(payload) - ResponseType.UNKNOWN -> throw IllegalResponseException("Unrecognized message type: $responseType") + ResponseType.UNKNOWN -> throw CouldNotParseResponseException("Unrecognized message type: $responseType") } } - @Throws(IllegalResponseException::class) + @Throws(CouldNotParseResponseException::class) private fun parseActivationResponse(payload: ByteArray): Response { return when (val activationResponseType = byValue(payload[1], ResponseType.ActivationResponseType.UNKNOWN)) { ResponseType.ActivationResponseType.GET_VERSION_RESPONSE -> VersionResponse(payload) ResponseType.ActivationResponseType.SET_UNIQUE_ID_RESPONSE -> SetUniqueIdResponse(payload) - ResponseType.ActivationResponseType.UNKNOWN -> throw IllegalResponseException("Unrecognized activation response type: $activationResponseType") + ResponseType.ActivationResponseType.UNKNOWN -> throw CouldNotParseResponseException("Unrecognized activation response type: $activationResponseType") } } - @Throws(IllegalResponseException::class, UnsupportedOperationException::class) + @Throws(CouldNotParseResponseException::class, UnsupportedOperationException::class) private fun parseAdditionalStatusResponse(payload: ByteArray): Response { return when (val additionalStatusResponseType = byValue(payload[2], ResponseType.StatusResponseType.UNKNOWN)) { ResponseType.StatusResponseType.DEFAULT_STATUS_RESPONSE -> DefaultStatusResponse(payload) // Unreachable; this response type is only used for requesting a default status response @@ -38,7 +38,7 @@ object ResponseUtil { ResponseType.StatusResponseType.STATUS_RESPONSE_PAGE_70 -> throw UnsupportedOperationException("Status response page 70 is not (yet) implemented") ResponseType.StatusResponseType.STATUS_RESPONSE_PAGE_80 -> throw UnsupportedOperationException("Status response page 80 is not (yet) implemented") ResponseType.StatusResponseType.STATUS_RESPONSE_PAGE_81 -> throw UnsupportedOperationException("Status response page 81 is not (yet) implemented") - ResponseType.StatusResponseType.UNKNOWN -> throw IllegalResponseException("Unrecognized additional status response type: $additionalStatusResponseType") + ResponseType.StatusResponseType.UNKNOWN -> throw CouldNotParseResponseException("Unrecognized additional status response type: $additionalStatusResponseType") } } } \ No newline at end of file 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 ac04803ea0..ab096579e2 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 @@ -4,15 +4,19 @@ 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.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.IllegalResponseException +import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.exceptions.PodAlarmException 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.MessageType 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.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.Response import info.nightscout.androidaps.utils.extensions.toHex +import kotlin.reflect.KClass class Session( private val aapsLogger: AAPSLogger, @@ -29,8 +33,8 @@ class Session( * <- response, ACK TODO: retries? * -> ACK */ - @Throws(IllegalResponseException::class, UnsupportedOperationException::class) - fun sendCommand(cmd: Command): Response { + @Throws(CouldNotParseResponseException::class, UnsupportedOperationException::class) + fun sendCommand(cmd: Command, responseType: KClass): Response { sessionKeys.msgSequenceNumber++ aapsLogger.debug(LTag.PUMPBTCOMM, "Sending command: ${cmd.encoded.toHex()} in packet $cmd") @@ -43,6 +47,13 @@ class Session( aapsLogger.debug(LTag.PUMPBTCOMM, "Received response: $decrypted") val response = parseResponse(decrypted) + if (!responseType.isInstance(response)) { + if (response is AlarmStatusResponse) { + throw PodAlarmException(response) + } + throw IllegalResponseException(responseType, response) + } + sessionKeys.msgSequenceNumber++ val ack = getAck(responseMsg) aapsLogger.debug(LTag.PUMPBTCOMM, "Sending ACK: ${ack.payload.toHex()} in packet $ack") @@ -50,7 +61,7 @@ class Session( return response } - @Throws(IllegalResponseException::class, UnsupportedOperationException::class) + @Throws(CouldNotParseResponseException::class, UnsupportedOperationException::class) private fun parseResponse(decrypted: MessagePacket): Response { val payload = parseKeys(arrayOf(RESPONSE_PREFIX), decrypted.payload)[0] From 1872e1ceb79e07c8729ec0bb4ea9a7fd4f3ffc75 Mon Sep 17 00:00:00 2001 From: Bart Sopers Date: Sun, 14 Mar 2021 21:45:52 +0100 Subject: [PATCH 03/13] Correctly handle NAK response for Omnipod Dash --- .../dash/driver/comm/exceptions/NakResponseException.kt | 5 +++++ .../plugins/pump/omnipod/dash/driver/comm/session/Session.kt | 5 +++++ 2 files changed, 10 insertions(+) create mode 100644 omnipod-dash/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/dash/driver/comm/exceptions/NakResponseException.kt diff --git a/omnipod-dash/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/dash/driver/comm/exceptions/NakResponseException.kt b/omnipod-dash/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/dash/driver/comm/exceptions/NakResponseException.kt new file mode 100644 index 0000000000..6ec605979e --- /dev/null +++ b/omnipod-dash/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/dash/driver/comm/exceptions/NakResponseException.kt @@ -0,0 +1,5 @@ +package info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.exceptions + +import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.response.NakResponse + +class NakResponseException(val response: NakResponse) : Exception("Received NAK response: ${response.nakErrorType.value} ${response.nakErrorType.name}") \ No newline at end of file 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 ab096579e2..4169dac629 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 @@ -6,6 +6,7 @@ import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.Id 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.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.MessageIO import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.message.MessagePacket @@ -14,6 +15,7 @@ import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.message. 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.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.utils.extensions.toHex import kotlin.reflect.KClass @@ -51,6 +53,9 @@ class Session( if (response is AlarmStatusResponse) { throw PodAlarmException(response) } + if (response is NakResponse) { + throw NakResponseException(response) + } throw IllegalResponseException(responseType, response) } From 98dc2f92ef03f31665653362a98952ff78e2eb59 Mon Sep 17 00:00:00 2001 From: Bart Sopers Date: Sun, 14 Mar 2021 22:39:07 +0100 Subject: [PATCH 04/13] Add delay for prime and cannula insertion for Omnipod Dash --- .../pump/omnipod/dash/driver/OmnipodDashManagerImpl.kt | 6 ++++++ 1 file changed, 6 insertions(+) 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 53f86bc677..b64a2dd2f3 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 @@ -25,6 +25,10 @@ class OmnipodDashManagerImpl @Inject constructor( private val bleManager: OmnipodDashBleManager, private val aapsSchedulers: AapsSchedulers ) : OmnipodDashManager { + companion object { + const val PRIME_BOLUS_DURATION_SECONDS = 35L + const val CANNULA_INSERTION_BOLUS_DURATION_SECONDS = 10L + } private val observePodReadyForActivationPart1: Observable get() = Observable.defer { @@ -217,6 +221,7 @@ class OmnipodDashManagerImpl @Inject constructor( ) } if (podStateManager.activationProgress.isBefore(ActivationProgress.PRIMING)) { + observables.add(Observable.timer(PRIME_BOLUS_DURATION_SECONDS, TimeUnit.SECONDS).flatMap { Observable.empty() }) observables.add( observeSendProgramBolusCommand( podStateManager.firstPrimeBolusVolume!! * 0.05, @@ -306,6 +311,7 @@ class OmnipodDashManagerImpl @Inject constructor( ) } if (podStateManager.activationProgress.isBefore(ActivationProgress.INSERTING_CANNULA)) { + observables.add(Observable.timer(CANNULA_INSERTION_BOLUS_DURATION_SECONDS, TimeUnit.SECONDS).flatMap { Observable.empty() }) observables.add( observeSendProgramBolusCommand( podStateManager.secondPrimeBolusVolume!! * 0.05, From 137908eb20b054d6b2b936d86ba3c5c466eefe56 Mon Sep 17 00:00:00 2001 From: Bart Sopers Date: Sun, 14 Mar 2021 22:49:25 +0100 Subject: [PATCH 05/13] Fix crash in Omnipod Dash Pod activation --- .../dash/driver/OmnipodDashManagerImpl.kt | 28 +++++++++++++------ 1 file changed, 20 insertions(+), 8 deletions(-) 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 b64a2dd2f3..32d8baee94 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 @@ -25,7 +25,9 @@ class OmnipodDashManagerImpl @Inject constructor( private val bleManager: OmnipodDashBleManager, private val aapsSchedulers: AapsSchedulers ) : OmnipodDashManager { + companion object { + const val PRIME_BOLUS_DURATION_SECONDS = 35L const val CANNULA_INSERTION_BOLUS_DURATION_SECONDS = 10L } @@ -221,14 +223,22 @@ class OmnipodDashManagerImpl @Inject constructor( ) } if (podStateManager.activationProgress.isBefore(ActivationProgress.PRIMING)) { - observables.add(Observable.timer(PRIME_BOLUS_DURATION_SECONDS, TimeUnit.SECONDS).flatMap { Observable.empty() }) observables.add( - observeSendProgramBolusCommand( - podStateManager.firstPrimeBolusVolume!! * 0.05, - podStateManager.primePulseRate!!.toByte(), - confirmationBeeps = false, - completionBeeps = false - ).doOnComplete(ActivationProgressUpdater(ActivationProgress.PRIMING)) + Observable.timer(PRIME_BOLUS_DURATION_SECONDS, TimeUnit.SECONDS).flatMap { Observable.empty() }) + observables.add( + Observable.defer { + bleManager.sendCommand( + ProgramBolusCommand.Builder() + .setUniqueId(podStateManager.uniqueId!!.toInt()) + .setSequenceNumber(podStateManager.messageSequenceNumber) + .setNonce(1229869870) // TODO + .setNumberOfUnits(podStateManager.firstPrimeBolusVolume!! * 0.05) + .setDelayBetweenPulsesInEighthSeconds(podStateManager.primePulseRate!!.toByte()) + .setProgramReminder(ProgramReminder(false, false, 0)) + .build(), + DefaultStatusResponse::class + ) + }.doOnComplete(ActivationProgressUpdater(ActivationProgress.PRIMING)) ) } if (podStateManager.activationProgress.isBefore(ActivationProgress.REPROGRAMMED_LUMP_OF_COAL_ALERT)) { @@ -311,7 +321,9 @@ class OmnipodDashManagerImpl @Inject constructor( ) } if (podStateManager.activationProgress.isBefore(ActivationProgress.INSERTING_CANNULA)) { - observables.add(Observable.timer(CANNULA_INSERTION_BOLUS_DURATION_SECONDS, TimeUnit.SECONDS).flatMap { Observable.empty() }) + observables.add( + Observable.timer(CANNULA_INSERTION_BOLUS_DURATION_SECONDS, TimeUnit.SECONDS) + .flatMap { Observable.empty() }) observables.add( observeSendProgramBolusCommand( podStateManager.secondPrimeBolusVolume!! * 0.05, From 06de8a04e2621eb754a015d266e9360da61327b0 Mon Sep 17 00:00:00 2001 From: Bart Sopers Date: Sun, 14 Mar 2021 23:11:48 +0100 Subject: [PATCH 06/13] Log actual received message type for unknown Omnipod Dash responses --- .../pump/omnipod/dash/driver/comm/session/ResponseUtil.kt | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/omnipod-dash/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/dash/driver/comm/session/ResponseUtil.kt b/omnipod-dash/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/dash/driver/comm/session/ResponseUtil.kt index ec2599b56d..cf3f314b59 100644 --- a/omnipod-dash/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/dash/driver/comm/session/ResponseUtil.kt +++ b/omnipod-dash/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/dash/driver/comm/session/ResponseUtil.kt @@ -13,7 +13,7 @@ object ResponseUtil { ResponseType.DEFAULT_STATUS_RESPONSE -> DefaultStatusResponse(payload) ResponseType.ADDITIONAL_STATUS_RESPONSE -> parseAdditionalStatusResponse(payload) ResponseType.NAK_RESPONSE -> NakResponse(payload) - ResponseType.UNKNOWN -> throw CouldNotParseResponseException("Unrecognized message type: $responseType") + ResponseType.UNKNOWN -> throw CouldNotParseResponseException("Unrecognized message type: ${payload[0]}") } } @@ -22,7 +22,7 @@ object ResponseUtil { return when (val activationResponseType = byValue(payload[1], ResponseType.ActivationResponseType.UNKNOWN)) { ResponseType.ActivationResponseType.GET_VERSION_RESPONSE -> VersionResponse(payload) ResponseType.ActivationResponseType.SET_UNIQUE_ID_RESPONSE -> SetUniqueIdResponse(payload) - ResponseType.ActivationResponseType.UNKNOWN -> throw CouldNotParseResponseException("Unrecognized activation response type: $activationResponseType") + ResponseType.ActivationResponseType.UNKNOWN -> throw CouldNotParseResponseException("Unrecognized activation response type: ${payload[1]}") } } @@ -38,7 +38,7 @@ object ResponseUtil { ResponseType.StatusResponseType.STATUS_RESPONSE_PAGE_70 -> throw UnsupportedOperationException("Status response page 70 is not (yet) implemented") ResponseType.StatusResponseType.STATUS_RESPONSE_PAGE_80 -> throw UnsupportedOperationException("Status response page 80 is not (yet) implemented") ResponseType.StatusResponseType.STATUS_RESPONSE_PAGE_81 -> throw UnsupportedOperationException("Status response page 81 is not (yet) implemented") - ResponseType.StatusResponseType.UNKNOWN -> throw CouldNotParseResponseException("Unrecognized additional status response type: $additionalStatusResponseType") + ResponseType.StatusResponseType.UNKNOWN -> throw CouldNotParseResponseException("Unrecognized additional status response type: ${payload[2]}") } } } \ No newline at end of file From dd480134e20a70c81763fef3e7ea0586c6e9fd48 Mon Sep 17 00:00:00 2001 From: Bart Sopers Date: Sun, 14 Mar 2021 23:13:41 +0100 Subject: [PATCH 07/13] Remove unused variables --- .../pump/omnipod/dash/driver/comm/session/ResponseUtil.kt | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/omnipod-dash/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/dash/driver/comm/session/ResponseUtil.kt b/omnipod-dash/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/dash/driver/comm/session/ResponseUtil.kt index cf3f314b59..8f920e349e 100644 --- a/omnipod-dash/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/dash/driver/comm/session/ResponseUtil.kt +++ b/omnipod-dash/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/dash/driver/comm/session/ResponseUtil.kt @@ -8,7 +8,7 @@ object ResponseUtil { @Throws(CouldNotParseResponseException::class, UnsupportedOperationException::class) fun parseResponse(payload: ByteArray): Response { - return when (val responseType = byValue(payload[0], ResponseType.UNKNOWN)) { + return when (byValue(payload[0], ResponseType.UNKNOWN)) { ResponseType.ACTIVATION_RESPONSE -> parseActivationResponse(payload) ResponseType.DEFAULT_STATUS_RESPONSE -> DefaultStatusResponse(payload) ResponseType.ADDITIONAL_STATUS_RESPONSE -> parseAdditionalStatusResponse(payload) @@ -19,7 +19,7 @@ object ResponseUtil { @Throws(CouldNotParseResponseException::class) private fun parseActivationResponse(payload: ByteArray): Response { - return when (val activationResponseType = byValue(payload[1], ResponseType.ActivationResponseType.UNKNOWN)) { + return when (byValue(payload[1], ResponseType.ActivationResponseType.UNKNOWN)) { ResponseType.ActivationResponseType.GET_VERSION_RESPONSE -> VersionResponse(payload) ResponseType.ActivationResponseType.SET_UNIQUE_ID_RESPONSE -> SetUniqueIdResponse(payload) ResponseType.ActivationResponseType.UNKNOWN -> throw CouldNotParseResponseException("Unrecognized activation response type: ${payload[1]}") @@ -28,7 +28,7 @@ object ResponseUtil { @Throws(CouldNotParseResponseException::class, UnsupportedOperationException::class) private fun parseAdditionalStatusResponse(payload: ByteArray): Response { - return when (val additionalStatusResponseType = byValue(payload[2], ResponseType.StatusResponseType.UNKNOWN)) { + return when (byValue(payload[2], ResponseType.StatusResponseType.UNKNOWN)) { ResponseType.StatusResponseType.DEFAULT_STATUS_RESPONSE -> DefaultStatusResponse(payload) // Unreachable; this response type is only used for requesting a default status response ResponseType.StatusResponseType.STATUS_RESPONSE_PAGE_1 -> throw UnsupportedOperationException("Status response page 1 is not (yet) implemented") ResponseType.StatusResponseType.ALARM_STATUS -> AlarmStatusResponse(payload) From 6d4521ca6bc19a7f7bb56859683a3f1454b5934f Mon Sep 17 00:00:00 2001 From: Bart Sopers Date: Mon, 15 Mar 2021 00:51:07 +0100 Subject: [PATCH 08/13] WIP on integrating Omnipod Dash activation wizard --- .../omnipod/dash/driver/OmnipodDashManagerImpl.kt | 2 +- .../omnipod/dash/driver/comm/session/Session.kt | 14 ++++++++++++-- .../pod/state/OmnipodDashPodStateManagerImpl.kt | 4 ++-- .../omnipod/dash/ui/DashPodManagementActivity.kt | 5 +---- .../omnipod/dash/ui/OmnipodDashOverviewFragment.kt | 2 +- .../res/layout/omnipod_dash_pod_management.xml | 3 ++- 6 files changed, 19 insertions(+), 11 deletions(-) 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 32d8baee94..ecb847fb35 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 @@ -161,7 +161,7 @@ class OmnipodDashManagerImpl @Inject constructor( if (podStateManager.podStatus == PodStatus.CLUTCH_DRIVE_ENGAGED) { Observable.empty() } else { - Observable.error(IllegalStateException("Unexpected Pod status")) + Observable.error(IllegalStateException("Unexpected Pod status: got ${podStateManager.podStatus}, expected CLUTCH_DRIVE_ENGAGED")) } } ) 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 4169dac629..6714e218aa 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 @@ -47,6 +47,7 @@ class Session( val responseMsg = msgIO.receiveMessage() val decrypted = enDecrypt.decrypt(responseMsg) aapsLogger.debug(LTag.PUMPBTCOMM, "Received response: $decrypted") + val response = parseResponse(decrypted) if (!responseType.isInstance(response)) { @@ -69,8 +70,17 @@ class Session( @Throws(CouldNotParseResponseException::class, UnsupportedOperationException::class) private fun parseResponse(decrypted: MessagePacket): Response { - val payload = parseKeys(arrayOf(RESPONSE_PREFIX), decrypted.payload)[0] - aapsLogger.info(LTag.PUMPBTCOMM, "Received decrypted response: ${payload.toHex()} in packet: $decrypted") + val data = parseKeys(arrayOf(RESPONSE_PREFIX), decrypted.payload)[0] + aapsLogger.info(LTag.PUMPBTCOMM, "Received decrypted response: ${data.toHex()} in packet: $decrypted") + + // TODO verify length + + val uniqueId = data.copyOfRange(0, 4) + val lenghtAndSequenceNumber = data.copyOfRange(4, 6) + val payload = data.copyOfRange(6, data.size - 2) + val crc = data.copyOfRange(data.size - 2, data.size) + + // TODO validate uniqueId, sequenceNumber and crc return ResponseUtil.parseResponse(payload) } 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 dcf5289266..b55719a341 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 @@ -216,8 +216,8 @@ class OmnipodDashPodStateManagerImpl @Inject constructor( override fun updateFromSetUniqueIdResponse(response: SetUniqueIdResponse) { podState.pulseRate = response.pumpRate podState.primePulseRate = response.primePumpRate - podState.firstPrimeBolusVolume = response.numberOfPrimePulses - podState.secondPrimeBolusVolume = response.numberOfEngagingClutchDrivePulses + podState.firstPrimeBolusVolume = response.numberOfEngagingClutchDrivePulses + podState.secondPrimeBolusVolume = response.numberOfPrimePulses podState.podLifeInHours = response.podExpirationTimeInHours podState.bleVersion = SoftwareVersion( response.bleVersionMajor, diff --git a/omnipod-dash/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/dash/ui/DashPodManagementActivity.kt b/omnipod-dash/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/dash/ui/DashPodManagementActivity.kt index 58779f9682..4e0df13403 100644 --- a/omnipod-dash/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/dash/ui/DashPodManagementActivity.kt +++ b/omnipod-dash/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/dash/ui/DashPodManagementActivity.kt @@ -25,9 +25,6 @@ import io.reactivex.disposables.CompositeDisposable import io.reactivex.rxkotlin.plusAssign import javax.inject.Inject -/** - * Created by andy on 30/08/2019 - */ class DashPodManagementActivity : NoSplashAppCompatActivity() { @Inject lateinit var rxBus: RxBusWrapper @@ -70,7 +67,7 @@ class DashPodManagementActivity : NoSplashAppCompatActivity() { this, resourceHelper.gs(R.string.omnipod_common_pod_management_discard_pod_confirmation), Thread { - // TODO discard Pod + podStateManager.reset() } ) } diff --git a/omnipod-dash/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/dash/ui/OmnipodDashOverviewFragment.kt b/omnipod-dash/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/dash/ui/OmnipodDashOverviewFragment.kt index 749f0d0de5..601932644a 100644 --- a/omnipod-dash/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/dash/ui/OmnipodDashOverviewFragment.kt +++ b/omnipod-dash/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/dash/ui/OmnipodDashOverviewFragment.kt @@ -313,7 +313,7 @@ class OmnipodDashOverviewFragment : DaggerFragment() { podInfoBinding.reservoir.text = resourceHelper.gs( R.string.omnipod_common_overview_reservoir_value, - podStateManager.pulsesRemaining + (podStateManager.pulsesRemaining!! / 20.0) ) podInfoBinding.reservoir.setTextColor( if (podStateManager.pulsesRemaining!! < lowReservoirThreshold) { diff --git a/omnipod-dash/src/main/res/layout/omnipod_dash_pod_management.xml b/omnipod-dash/src/main/res/layout/omnipod_dash_pod_management.xml index 9790b2335a..461781eabe 100644 --- a/omnipod-dash/src/main/res/layout/omnipod_dash_pod_management.xml +++ b/omnipod-dash/src/main/res/layout/omnipod_dash_pod_management.xml @@ -119,6 +119,7 @@ android:orientation="vertical" app:layout_constraintGuide_percent="0.5" /> + Date: Mon, 15 Mar 2021 01:34:02 +0100 Subject: [PATCH 09/13] More WIP on Omnipod Dash Pod activation --- .../dash/driver/OmnipodDashManagerImpl.kt | 47 +++++++++---------- 1 file changed, 21 insertions(+), 26 deletions(-) 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 ecb847fb35..d050e82476 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 @@ -105,19 +105,16 @@ class OmnipodDashManagerImpl @Inject constructor( } private val observeVerifyCannulaInsertion: Observable - get() = Observable.defer { - observeSendGetPodStatusCommand() - .ignoreElements() // - .andThen( - Observable.defer { - if (podStateManager.podStatus == PodStatus.RUNNING_ABOVE_MIN_VOLUME) { - Observable.empty() - } else { - Observable.error(IllegalStateException("Unexpected Pod status")) - } - } - ) - } + get() = Observable.concat( + observeSendGetPodStatusCommand(), + Observable.defer { + if (podStateManager.podStatus == PodStatus.RUNNING_ABOVE_MIN_VOLUME) { + Observable.empty() + } else { + Observable.error(IllegalStateException("Unexpected Pod status")) + } + } + ) private fun observeSendProgramAlertsCommand( alertConfigurations: List, @@ -153,19 +150,16 @@ class OmnipodDashManagerImpl @Inject constructor( } private val observeVerifyPrime: Observable - get() = Observable.defer { - observeSendGetPodStatusCommand() - .ignoreElements() // - .andThen( - Observable.defer { - if (podStateManager.podStatus == PodStatus.CLUTCH_DRIVE_ENGAGED) { - Observable.empty() - } else { - Observable.error(IllegalStateException("Unexpected Pod status: got ${podStateManager.podStatus}, expected CLUTCH_DRIVE_ENGAGED")) - } - } - ) - } + get() = Observable.concat( + observeSendGetPodStatusCommand(ResponseType.StatusResponseType.DEFAULT_STATUS_RESPONSE), + Observable.defer { + if (podStateManager.podStatus == PodStatus.CLUTCH_DRIVE_ENGAGED) { + Observable.empty() + } else { + Observable.error(IllegalStateException("Unexpected Pod status: got ${podStateManager.podStatus}, expected CLUTCH_DRIVE_ENGAGED")) + } + } + ) private val observeSendSetUniqueIdCommand: Observable get() = Observable.defer { @@ -196,6 +190,7 @@ class OmnipodDashManagerImpl @Inject constructor( return Observable.concat( observePodReadyForActivationPart1, observePairNewPod, + observeConnectToPod, // FIXME needed after disconnect; observePairNewPod does not connect in that case. observeActivationPart1Commands(lowReservoirAlertTrigger) ).doOnComplete(ActivationProgressUpdater(ActivationProgress.PHASE_1_COMPLETED)) // TODO these would be common for any observable returned in a public function in this class From 653deff0b6bd13a7b9844b53575179578f2238b2 Mon Sep 17 00:00:00 2001 From: Bart Sopers Date: Sat, 20 Mar 2021 20:45:53 +0100 Subject: [PATCH 10/13] Omnipod Dash: improve logging and fix prime timeouts --- .../dash/driver/OmnipodDashManagerImpl.kt | 12 +++-- .../comm/exceptions/PodAlarmException.kt | 10 +++- .../omnipod/dash/driver/event/PodEvent.kt | 47 ++++++++++++++++--- .../action/DashInsertCannulaViewModel.kt | 9 +++- .../pump/omnipod/dash/util/Functions.kt | 2 +- 5 files changed, 68 insertions(+), 12 deletions(-) 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 d050e82476..cb20bc17ec 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 @@ -135,6 +135,7 @@ class OmnipodDashManagerImpl @Inject constructor( private fun observeProgramBasalCommand(basalProgram: BasalProgram): Observable { return Observable.defer { + logger.debug(LTag.PUMPCOMM, "Programming basal. basalProgram={}", basalProgram) bleManager.sendCommand( ProgramBasalCommand.Builder() .setUniqueId(podStateManager.uniqueId!!.toInt()) @@ -219,7 +220,10 @@ class OmnipodDashManagerImpl @Inject constructor( } if (podStateManager.activationProgress.isBefore(ActivationProgress.PRIMING)) { observables.add( - Observable.timer(PRIME_BOLUS_DURATION_SECONDS, TimeUnit.SECONDS).flatMap { Observable.empty() }) + Observable.defer { + Observable.timer(podStateManager.firstPrimeBolusVolume!!.toLong(), TimeUnit.SECONDS) + .flatMap { Observable.empty() } + }) observables.add( Observable.defer { bleManager.sendCommand( @@ -317,8 +321,10 @@ class OmnipodDashManagerImpl @Inject constructor( } if (podStateManager.activationProgress.isBefore(ActivationProgress.INSERTING_CANNULA)) { observables.add( - Observable.timer(CANNULA_INSERTION_BOLUS_DURATION_SECONDS, TimeUnit.SECONDS) - .flatMap { Observable.empty() }) + Observable.defer { + Observable.timer(podStateManager.secondPrimeBolusVolume!!.toLong(), TimeUnit.SECONDS) + .flatMap { Observable.empty() } + }) observables.add( observeSendProgramBolusCommand( podStateManager.secondPrimeBolusVolume!! * 0.05, diff --git a/omnipod-dash/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/dash/driver/comm/exceptions/PodAlarmException.kt b/omnipod-dash/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/dash/driver/comm/exceptions/PodAlarmException.kt index 4d6cd067b9..8abd3386b3 100644 --- a/omnipod-dash/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/dash/driver/comm/exceptions/PodAlarmException.kt +++ b/omnipod-dash/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/dash/driver/comm/exceptions/PodAlarmException.kt @@ -1,5 +1,13 @@ package info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.exceptions import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.response.AlarmStatusResponse +import java.util.* -class PodAlarmException(val response: AlarmStatusResponse) : Exception("Pod is in alarm: ${response.alarmType.value} ${response.alarmType.name}") \ No newline at end of file +class PodAlarmException(val response: AlarmStatusResponse) : Exception( + String.format( + Locale.getDefault(), + "Pod is in alarm: %03d %s", + response.alarmType.value.toInt() and 0xff, + response.alarmType.name + ) +) \ No newline at end of file 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 367393292e..18b8d7fb71 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 @@ -7,18 +7,53 @@ import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.response. sealed class PodEvent { /* BT connection events */ - class AlreadyConnected(val bluetoothAddress: String) : PodEvent() + class AlreadyConnected(val bluetoothAddress: String) : PodEvent() { + + override fun toString(): String { + return "AlreadyConnected(bluetoothAddress='$bluetoothAddress')" + } + } + object AlreadyPaired : PodEvent() object Scanning : PodEvent() object BluetoothConnecting : PodEvent() - class BluetoothConnected(val bluetoothAddress: String) : PodEvent() + class BluetoothConnected(val bluetoothAddress: String) : PodEvent() { + + override fun toString(): String { + return "BluetoothConnected(bluetoothAddress='$bluetoothAddress')" + } + } + object Pairing : PodEvent() - class Paired(val uniqueId: Id) : PodEvent() + class Paired(val uniqueId: Id) : PodEvent() { + + override fun toString(): String { + return "Paired(uniqueId=$uniqueId)" + } + } + object EstablishingSession : PodEvent() object Connected : PodEvent() /* Message exchange events */ - class CommandSending(val command: Command) : PodEvent() - class CommandSent(val command: Command) : PodEvent() - class ResponseReceived(val response: Response) : PodEvent() + class CommandSending(val command: Command) : PodEvent() { + + override fun toString(): String { + return "CommandSending(command=$command)" + } + } + + class CommandSent(val command: Command) : PodEvent() { + + override fun toString(): String { + return "CommandSent(command=$command)" + } + } + + class ResponseReceived(val response: Response) : PodEvent() { + + override fun toString(): String { + return "ResponseReceived(response=$response)" + } + } } diff --git a/omnipod-dash/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/dash/ui/wizard/activation/viewmodel/action/DashInsertCannulaViewModel.kt b/omnipod-dash/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/dash/ui/wizard/activation/viewmodel/action/DashInsertCannulaViewModel.kt index cf0cc52d6f..5372a6967f 100644 --- a/omnipod-dash/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/dash/ui/wizard/activation/viewmodel/action/DashInsertCannulaViewModel.kt +++ b/omnipod-dash/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/dash/ui/wizard/activation/viewmodel/action/DashInsertCannulaViewModel.kt @@ -32,7 +32,14 @@ class DashInsertCannulaViewModel @Inject constructor( if (profile == null) { source.onError(IllegalStateException("No profile set")) } else { - val disposable = omnipodManager.activatePodPart2(mapProfileToBasalProgram(profile)).subscribeBy( + val basalProgram = mapProfileToBasalProgram(profile) + logger.debug( + LTag.PUMPCOMM, + "Mapped profile to basal program. profile={}, basalProgram={}", + profile, + basalProgram + ) + val disposable = omnipodManager.activatePodPart2(basalProgram).subscribeBy( onNext = { podEvent -> logger.debug( LTag.PUMP, diff --git a/omnipod-dash/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/dash/util/Functions.kt b/omnipod-dash/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/dash/util/Functions.kt index baa0ecf7c9..8b1e08848e 100644 --- a/omnipod-dash/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/dash/util/Functions.kt +++ b/omnipod-dash/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/dash/util/Functions.kt @@ -41,7 +41,7 @@ fun mapProfileToBasalProgram(profile: Profile): BasalProgram { } if (entries.size == 0 && basalValue.timeAsSeconds != 0) { - throw java.lang.IllegalArgumentException("First basal segment start time should be 0") + throw IllegalArgumentException("First basal segment start time should be 0") } if (entries.size > 0 && entries[entries.size - 1].endSlotIndex != startSlotIndex) { From 76c4d01ee75b2636cccea862e23bfa9fae6ddf83 Mon Sep 17 00:00:00 2001 From: Bart Sopers Date: Tue, 30 Mar 2021 01:21:35 +0200 Subject: [PATCH 11/13] Bugfix in setting Dash basal --- .../dash/driver/OmnipodDashManagerImpl.kt | 5 +-- .../dash/driver/comm/session/Session.kt | 5 ++- .../insulin/program/util/ProgramBasalUtil.kt | 7 +++-- .../driver/pod/definition/ProgramReminder.kt | 4 +++ .../dash/ui/OmnipodDashOverviewFragment.kt | 7 +++-- .../pod/command/ProgramBasalCommandTest.kt | 31 +++++++++++++++++-- 6 files changed, 50 insertions(+), 9 deletions(-) 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 cb20bc17ec..cc61847e8c 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 @@ -135,7 +135,8 @@ class OmnipodDashManagerImpl @Inject constructor( private fun observeProgramBasalCommand(basalProgram: BasalProgram): Observable { return Observable.defer { - logger.debug(LTag.PUMPCOMM, "Programming basal. basalProgram={}", basalProgram) + val currentTime = Date() + logger.debug(LTag.PUMPCOMM, "Programming basal. currentTime={}, basalProgram={}", currentTime, basalProgram) bleManager.sendCommand( ProgramBasalCommand.Builder() .setUniqueId(podStateManager.uniqueId!!.toInt()) @@ -143,7 +144,7 @@ class OmnipodDashManagerImpl @Inject constructor( .setNonce(1229869870) // TODO .setProgramReminder(ProgramReminder(atStart = false, atEnd = false, atInterval = 0)) .setBasalProgram(basalProgram) - .setCurrentTime(Date()) + .setCurrentTime(currentTime) .build(), DefaultStatusResponse::class ) 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 6714e218aa..3a9bbc2886 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 @@ -38,7 +38,10 @@ class Session( @Throws(CouldNotParseResponseException::class, UnsupportedOperationException::class) fun sendCommand(cmd: Command, responseType: KClass): Response { sessionKeys.msgSequenceNumber++ - aapsLogger.debug(LTag.PUMPBTCOMM, "Sending command: ${cmd.encoded.toHex()} in packet $cmd") + aapsLogger.debug( + LTag.PUMPBTCOMM, + "Sending command: ${cmd.javaClass.simpleName}: ${cmd.encoded.toHex()} in packet $cmd" + ) val msg = getCmdMessage(cmd) aapsLogger.debug(LTag.PUMPBTCOMM, "Sending command(wrapped): ${msg.payload.toHex()}") diff --git a/omnipod-dash/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/dash/driver/pod/command/insulin/program/util/ProgramBasalUtil.kt b/omnipod-dash/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/dash/driver/pod/command/insulin/program/util/ProgramBasalUtil.kt index b784ebcdbc..fc3f0c1503 100644 --- a/omnipod-dash/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/dash/driver/pod/command/insulin/program/util/ProgramBasalUtil.kt +++ b/omnipod-dash/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/dash/driver/pod/command/insulin/program/util/ProgramBasalUtil.kt @@ -56,6 +56,7 @@ object ProgramBasalUtil { fun mapPulsesPerSlotToShortInsulinProgramElements(pulsesPerSlot: ShortArray?): List { require(pulsesPerSlot!!.size <= NUMBER_OF_BASAL_SLOTS) { "Basal program must contain at most 48 slots" } + val elements: MutableList = ArrayList() var extraAlternatePulse = false var previousPulsesPerSlot: Short = 0 @@ -84,7 +85,7 @@ object ProgramBasalUtil { extraAlternatePulse = false } currentTotalNumberOfSlots++ - } else if (numberOfSlotsInCurrentElement.toInt() == 1 && pulsesPerSlot[currentTotalNumberOfSlots.toInt()].toInt() == previousPulsesPerSlot + 1) { + } else if (numberOfSlotsInCurrentElement.toInt() == 1 && !extraAlternatePulse && pulsesPerSlot[currentTotalNumberOfSlots.toInt()].toInt() == previousPulsesPerSlot + 1) { // Second slot of segment with extra alternate pulse var expectAlternatePulseForNextSegment = false currentTotalNumberOfSlots++ @@ -94,10 +95,10 @@ object ProgramBasalUtil { // Loop rest alternate pulse segment if (pulsesPerSlot[currentTotalNumberOfSlots.toInt()].toInt() == previousPulsesPerSlot + (if (expectAlternatePulseForNextSegment) 1 else 0)) { // Still in alternate pulse segment - currentTotalNumberOfSlots++ expectAlternatePulseForNextSegment = !expectAlternatePulseForNextSegment if (numberOfSlotsInCurrentElement < MAX_NUMBER_OF_SLOTS_IN_INSULIN_PROGRAM_ELEMENT) { numberOfSlotsInCurrentElement++ + currentTotalNumberOfSlots++ } else { // End of alternate pulse segment (no slots left in element) elements.add( @@ -110,6 +111,7 @@ object ProgramBasalUtil { previousPulsesPerSlot = pulsesPerSlot[currentTotalNumberOfSlots.toInt()] numberOfSlotsInCurrentElement = 1 extraAlternatePulse = false + currentTotalNumberOfSlots++ break } } else { @@ -193,6 +195,7 @@ object ProgramBasalUtil { val hourOfDay = instance[Calendar.HOUR_OF_DAY] val minuteOfHour = instance[Calendar.MINUTE] val secondOfMinute = instance[Calendar.SECOND] + val index = ((hourOfDay * 60 + minuteOfHour) / 30).toByte() val secondOfDay = secondOfMinute + hourOfDay * 3600 + minuteOfHour * 60 val secondsRemaining = ((index + 1) * 1800 - secondOfDay).toShort() diff --git a/omnipod-dash/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/dash/driver/pod/definition/ProgramReminder.kt b/omnipod-dash/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/dash/driver/pod/definition/ProgramReminder.kt index 371f90d9e4..06e338bf97 100644 --- a/omnipod-dash/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/dash/driver/pod/definition/ProgramReminder.kt +++ b/omnipod-dash/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/dash/driver/pod/definition/ProgramReminder.kt @@ -17,4 +17,8 @@ class ProgramReminder( or ((atInterval and 0x3f).toInt()) ).toByte() ) + + override fun toString(): String { + return "ProgramReminder(atStart=$atStart, atEnd=$atEnd, atInterval=$atInterval)" + } } diff --git a/omnipod-dash/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/dash/ui/OmnipodDashOverviewFragment.kt b/omnipod-dash/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/dash/ui/OmnipodDashOverviewFragment.kt index 601932644a..b2351d4f75 100644 --- a/omnipod-dash/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/dash/ui/OmnipodDashOverviewFragment.kt +++ b/omnipod-dash/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/dash/ui/OmnipodDashOverviewFragment.kt @@ -295,7 +295,10 @@ class OmnipodDashOverviewFragment : DaggerFragment() { // total delivered podInfoBinding.totalDelivered.text = if (podStateManager.isActivationCompleted && podStateManager.pulsesDelivered != null) { - resourceHelper.gs(R.string.omnipod_common_overview_total_delivered_value, podStateManager.pulseRate) + resourceHelper.gs( + R.string.omnipod_common_overview_total_delivered_value, + podStateManager.pulsesDelivered!! * 0.05 + ) } else { PLACEHOLDER } @@ -313,7 +316,7 @@ class OmnipodDashOverviewFragment : DaggerFragment() { podInfoBinding.reservoir.text = resourceHelper.gs( R.string.omnipod_common_overview_reservoir_value, - (podStateManager.pulsesRemaining!! / 20.0) + (podStateManager.pulsesRemaining!! * 0.05) ) podInfoBinding.reservoir.setTextColor( if (podStateManager.pulsesRemaining!! < lowReservoirThreshold) { diff --git a/omnipod-dash/src/test/java/info/nightscout/androidaps/plugins/pump/omnipod/dash/driver/pod/command/ProgramBasalCommandTest.kt b/omnipod-dash/src/test/java/info/nightscout/androidaps/plugins/pump/omnipod/dash/driver/pod/command/ProgramBasalCommandTest.kt index 93fba68a72..aa6671db10 100644 --- a/omnipod-dash/src/test/java/info/nightscout/androidaps/plugins/pump/omnipod/dash/driver/pod/command/ProgramBasalCommandTest.kt +++ b/omnipod-dash/src/test/java/info/nightscout/androidaps/plugins/pump/omnipod/dash/driver/pod/command/ProgramBasalCommandTest.kt @@ -15,7 +15,7 @@ class ProgramBasalCommandTest { BasalProgram.Segment(0.toShort(), 48.toShort(), 300) ) val basalProgram = BasalProgram(segments) - val date = Date(2021, 1, 17, 14, 47, 43) + val date = Date(121, 1, 17, 14, 47, 43) val encoded = ProgramBasalCommand.Builder() // .setUniqueId(37879809) // @@ -27,6 +27,33 @@ class ProgramBasalCommandTest { .build() // .encoded - Assert.assertArrayEquals(Hex.decodeHex("0242000128241A12494E532E0005E81D1708000CF01EF01EF01E130E40001593004C4B403840005B8D80827C"), encoded) + Assert.assertArrayEquals( + Hex.decodeHex("0242000128241A12494E532E0005E81D1708000CF01EF01EF01E130E40001593004C4B403840005B8D80827C"), + encoded + ) } + + @Test @Throws(DecoderException::class) fun testProgramBasalCommandWithExtraAlternateSegmentPulse() { + val segments = listOf( + BasalProgram.Segment(0.toShort(), 48.toShort(), 5) + ) + val basalProgram = BasalProgram(segments) + val date = Date(121, 0, 30, 23, 21, 46) + + val encoded = ProgramBasalCommand.Builder() // + .setUniqueId(4241) // + .setNonce(1229869870) // + .setSequenceNumber(12.toShort()) // + .setBasalProgram(basalProgram) // + .setCurrentTime(date) // + .setProgramReminder(ProgramReminder(atStart = false, atEnd = false, atInterval = 0.toByte())) // + .build() // + .encoded + + Assert.assertArrayEquals( + Hex.decodeHex("0000109130241a12494e532e0000c52e0f700000f800f800f800130e0000000707fcad8000f015752a00033b"), + encoded + ) + } + } From 19a132c5967783ea4719a7941692dcd47155a82a Mon Sep 17 00:00:00 2001 From: Bart Sopers Date: Tue, 30 Mar 2021 01:33:01 +0200 Subject: [PATCH 12/13] Remove unused constants --- .../pump/omnipod/dash/driver/OmnipodDashManagerImpl.kt | 6 ------ 1 file changed, 6 deletions(-) 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 cc61847e8c..9260e62f80 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 @@ -26,12 +26,6 @@ class OmnipodDashManagerImpl @Inject constructor( private val aapsSchedulers: AapsSchedulers ) : OmnipodDashManager { - companion object { - - const val PRIME_BOLUS_DURATION_SECONDS = 35L - const val CANNULA_INSERTION_BOLUS_DURATION_SECONDS = 10L - } - private val observePodReadyForActivationPart1: Observable get() = Observable.defer { if (podStateManager.activationProgress.isBefore(ActivationProgress.PHASE_1_COMPLETED)) { From 811f1368308f3220bf76dee8a955cb16a2ef0b84 Mon Sep 17 00:00:00 2001 From: Bart Sopers Date: Wed, 31 Mar 2021 01:10:21 +0200 Subject: [PATCH 13/13] Implement most methods in OmnipodDashManagerImpl + cleanup --- .../common/dagger/OmnipodWizardModule.kt | 5 +- .../fragment/info/AttachPodFragment.kt | 6 +- .../common/fragment/ActionFragmentBase.kt | 15 +- .../common/fragment/WizardFragmentBase.kt | 3 +- .../omnipod/dash/OmnipodDashPumpPlugin.kt | 12 +- .../omnipod/dash/driver/OmnipodDashManager.kt | 12 +- .../dash/driver/OmnipodDashManagerImpl.kt | 281 +++++++++++++++--- .../driver/comm/OmnipodDashBleManagerImpl.kt | 2 +- .../omnipod/dash/driver/event/PodEvent.kt | 8 +- .../driver/pod/command/DeactivateCommand.kt | 10 +- .../driver/pod/command/GetStatusCommand.kt | 10 +- .../driver/pod/command/GetVersionCommand.kt | 10 +- .../pod/command/ProgramAlertsCommand.kt | 8 +- .../driver/pod/command/ProgramBasalCommand.kt | 20 +- .../driver/pod/command/ProgramBeepsCommand.kt | 16 +- .../driver/pod/command/ProgramBolusCommand.kt | 30 +- .../pod/command/ProgramInsulinCommand.kt | 12 +- .../pod/command/ProgramTempBasalCommand.kt | 18 +- .../driver/pod/command/SetUniqueIdCommand.kt | 28 +- .../pod/command/SilenceAlertsCommand.kt | 12 +- .../driver/pod/command/StopDeliveryCommand.kt | 12 +- .../pod/command/base/HeaderEnabledCommand.kt | 12 +- .../program/BasalInsulinProgramElement.kt | 6 +- .../BasalShortInsulinProgramElement.kt | 10 +- .../program/TempBasalInsulinProgramElement.kt | 4 +- .../insulin/program/util/ProgramBasalUtil.kt | 6 +- .../program/util/ProgramTempBasalUtil.kt | 6 +- .../pod/definition/AlertConfiguration.kt | 10 +- .../action/DashDeactivatePodViewModel.kt | 27 +- .../driver/comm/message/MessagePacketTest.kt | 2 +- .../comm/message/PayloadSplitJoinTest.kt | 1 + .../comm/message/PayloadSplitterTest.kt | 1 + .../message/StringLengthPrefixEncodingTest.kt | 3 +- .../dash/driver/comm/pair/KeyExchangeTest.kt | 3 +- .../driver/comm/session/EapMessageTest.kt | 4 +- .../pod/command/DeactivateCommandTest.kt | 10 +- .../pod/command/GetStatusCommandTest.kt | 10 +- .../pod/command/GetVersionCommandTest.kt | 8 +- .../pod/command/ProgramAlertsCommandTest.kt | 112 +++++-- .../pod/command/ProgramBasalCommandTest.kt | 32 +- .../pod/command/ProgramBeepsCommandTest.kt | 16 +- .../pod/command/ProgramBolusCommandTest.kt | 21 +- .../command/ProgramTempBasalCommandTest.kt | 38 ++- .../pod/command/SetUniqueIdCommandTest.kt | 14 +- .../pod/command/SilenceAlertsCommandTest.kt | 12 +- .../pod/command/StopDeliveryCommandTest.kt | 28 +- 46 files changed, 609 insertions(+), 317 deletions(-) diff --git a/omnipod-common/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/common/dagger/OmnipodWizardModule.kt b/omnipod-common/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/common/dagger/OmnipodWizardModule.kt index 5b3a0075c0..889ac70ea5 100644 --- a/omnipod-common/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/common/dagger/OmnipodWizardModule.kt +++ b/omnipod-common/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/common/dagger/OmnipodWizardModule.kt @@ -23,7 +23,10 @@ abstract class OmnipodWizardModule { @Provides @OmnipodPluginQualifier - fun providesViewModelFactory(@OmnipodPluginQualifier viewModels: MutableMap, @JvmSuppressWildcards Provider>): ViewModelProvider.Factory { + fun providesViewModelFactory( + @OmnipodPluginQualifier + viewModels: MutableMap, @JvmSuppressWildcards Provider> + ): ViewModelProvider.Factory { return ViewModelFactory(viewModels) } } diff --git a/omnipod-common/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/common/ui/wizard/activation/fragment/info/AttachPodFragment.kt b/omnipod-common/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/common/ui/wizard/activation/fragment/info/AttachPodFragment.kt index 3087690353..81befa950b 100644 --- a/omnipod-common/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/common/ui/wizard/activation/fragment/info/AttachPodFragment.kt +++ b/omnipod-common/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/common/ui/wizard/activation/fragment/info/AttachPodFragment.kt @@ -41,7 +41,11 @@ class AttachPodFragment : InfoFragmentBase() { .setIcon(android.R.drawable.ic_dialog_alert) .setTitle(getString(getTitleId())) .setMessage(getString(R.string.omnipod_common_pod_activation_wizard_attach_pod_confirm_insert_cannula_text)) - .setPositiveButton(getString(R.string.omnipod_common_ok)) { _, _ -> findNavController().navigate(getNextPageActionId()) } + .setPositiveButton(getString(R.string.omnipod_common_ok)) { _, _ -> + findNavController().navigate( + getNextPageActionId() + ) + } .setNegativeButton(getString(R.string.omnipod_common_cancel), null) .show() } diff --git a/omnipod-common/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/common/ui/wizard/common/fragment/ActionFragmentBase.kt b/omnipod-common/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/common/ui/wizard/common/fragment/ActionFragmentBase.kt index 945ebfae50..f554082f75 100644 --- a/omnipod-common/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/common/ui/wizard/common/fragment/ActionFragmentBase.kt +++ b/omnipod-common/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/common/ui/wizard/common/fragment/ActionFragmentBase.kt @@ -24,7 +24,8 @@ abstract class ActionFragmentBase : WizardFragmentBase() { binding.navButtonsLayout.buttonNext.isEnabled = false view.findViewById(R.id.omnipod_wizard_action_page_text).setText(getTextId()) - view.findViewById