simplify read error handling

This commit is contained in:
Andrei Vereha 2021-04-02 14:03:22 +02:00
parent e8bc50f458
commit 0f58185109
18 changed files with 80 additions and 100 deletions

View file

@ -542,7 +542,6 @@ class OmnipodDashPumpPlugin @Inject constructor(
"Time, Date and/or TimeZone changed. [timeChangeType=" + timeChangeType.name + ", eventHandlingEnabled=" + eventHandlingEnabled + "]"
)
if (timeChangeType == TimeChangeType.TimeChanged) {
aapsLogger.info(LTag.PUMP, "Ignoring time change because it is not a DST or TZ change")
return

View file

@ -248,7 +248,8 @@ class OmnipodDashManagerImpl @Inject constructor(
Observable.defer {
Observable.timer(podStateManager.firstPrimeBolusVolume!!.toLong(), TimeUnit.SECONDS)
.flatMap { Observable.empty() }
})
}
)
observables.add(
Observable.defer {
bleManager.sendCommand(
@ -349,7 +350,8 @@ class OmnipodDashManagerImpl @Inject constructor(
Observable.defer {
Observable.timer(podStateManager.secondPrimeBolusVolume!!.toLong(), TimeUnit.SECONDS)
.flatMap { Observable.empty() }
})
}
)
observables.add(
observeSendProgramBolusCommand(
podStateManager.secondPrimeBolusVolume!! * 0.05,

View file

@ -1,3 +1,3 @@
package info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.exceptions
class CouldNotParseResponseException(message: String?) : Exception(message)
class CouldNotParseResponseException(message: String?) : Exception(message)

View file

@ -6,4 +6,4 @@ import kotlin.reflect.KClass
class IllegalResponseException(
expectedResponseType: KClass<out Response>,
actualResponse: Response
) : Exception("Illegal response: expected ${expectedResponseType.simpleName} but got $actualResponse")
) : Exception("Illegal response: expected ${expectedResponseType.simpleName} but got $actualResponse")

View file

@ -2,4 +2,4 @@ package info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.excepti
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}")
class NakResponseException(val response: NakResponse) : Exception("Received NAK response: ${response.nakErrorType.value} ${response.nakErrorType.name}")

View file

@ -10,4 +10,4 @@ class PodAlarmException(val response: AlarmStatusResponse) : Exception(
response.alarmType.value.toInt() and 0xff,
response.alarmType.name
)
)
)

View file

@ -15,10 +15,6 @@ import info.nightscout.androidaps.utils.extensions.toHex
import java.util.concurrent.BlockingQueue
import java.util.concurrent.TimeUnit
sealed class BleReceiveResult
data class BleReceivePayload(val payload: ByteArray) : BleReceiveResult()
data class BleReceiveError(val msg: String, val cause: Throwable? = null) : BleReceiveResult()
sealed class BleSendResult
object BleSendSuccess : BleSendResult()
@ -39,13 +35,16 @@ open class BleIO(
* @param characteristic where to read from(CMD or DATA)
* @return a byte array with the received data or error
*/
fun receivePacket(timeoutMs: Long = DEFAULT_IO_TIMEOUT_MS): BleReceiveResult {
fun receivePacket(timeoutMs: Long = DEFAULT_IO_TIMEOUT_MS): ByteArray? {
return try {
val packet = incomingPackets.poll(timeoutMs, TimeUnit.MILLISECONDS)
if (packet == null) BleReceiveError("Timeout")
else BleReceivePayload(packet)
if (packet == null) {
aapsLogger.debug(LTag.PUMPBTCOMM, "Timeout reading $type packet")
}
packet
} catch (e: InterruptedException) {
BleReceiveError("Interrupted", cause = e)
aapsLogger.debug(LTag.PUMPBTCOMM, "Interrupted while reading packet: $e")
null
}
}

View file

@ -13,7 +13,7 @@ sealed class BleConfirmResult
object BleConfirmSuccess : BleConfirmResult()
data class BleConfirmIncorrectData(val payload: ByteArray) : BleConfirmResult()
data class BleConfirmError(val msg: String, val cause: Throwable? = null) : BleConfirmResult()
data class BleConfirmError(val msg: String) : BleConfirmResult()
class CmdBleIO(
logger: AAPSLogger,
@ -37,14 +37,12 @@ class CmdBleIO(
fun hello() = sendAndConfirmPacket(BleCommandHello(OmnipodDashBleManagerImpl.CONTROLLER_ID).data)
fun expectCommandType(expected: BleCommand, timeoutMs: Long = DEFAULT_IO_TIMEOUT_MS): BleConfirmResult {
return when (val actual = receivePacket(timeoutMs)) {
is BleReceiveError -> BleConfirmError(actual.toString())
is BleReceivePayload ->
if (actual.payload.isEmpty() || actual.payload[0] != expected.data[0]) {
BleConfirmIncorrectData(actual.payload)
} else {
BleConfirmSuccess
}
return receivePacket(timeoutMs)?.let {
if (it.isNotEmpty() && it[0] == expected.data[0])
BleConfirmSuccess
else
BleConfirmIncorrectData(it)
}
?: BleConfirmError("Error reading packet")
}
}

View file

@ -9,12 +9,6 @@ import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.packet.P
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.packet.PayloadSplitter
import info.nightscout.androidaps.utils.extensions.toHex
sealed class MesssageReceiveResult
data class MessageReceiveSuccess(val msg: MessagePacket) : MesssageReceiveResult()
data class MessageReceiveError(val msg: String, val cause: Throwable? = null) : MesssageReceiveResult() {
constructor(e: PacketReceiveResult) : this("Could not read DATA packet: $e")
}
sealed class MessageSendResult
object MessageSendSuccess : MessageSendResult()
data class MessageSendErrorSending(val msg: String, val cause: Throwable? = null) : MessageSendResult() {
@ -43,14 +37,13 @@ class MessageIO(
cmdBleIO.flushIncomingQueue()
dataBleIO.flushIncomingQueue()
val sendResult = cmdBleIO.sendAndConfirmPacket(BleCommandRTS.data)
if (sendResult is BleSendErrorSending) {
return MessageSendErrorSending(sendResult)
val rtsSendResult = cmdBleIO.sendAndConfirmPacket(BleCommandRTS.data)
if (rtsSendResult is BleSendErrorSending) {
return MessageSendErrorSending(rtsSendResult)
}
val expectCTS = cmdBleIO.expectCommandType(BleCommandCTS)
if (expectCTS !is BleConfirmSuccess) {
return MessageSendErrorSending(sendResult)
return MessageSendErrorSending(expectCTS.toString())
}
val payload = msg.asByteArray()
@ -90,19 +83,21 @@ class MessageIO(
}
}
fun receiveMessage(): MesssageReceiveResult {
fun receiveMessage(): MessagePacket? {
cmdBleIO.expectCommandType(BleCommandRTS, MESSAGE_READ_TIMEOUT_MS)
val sendResult = cmdBleIO.sendAndConfirmPacket(BleCommandCTS.data)
if (sendResult !is BleSendSuccess) {
return MessageReceiveError("Error sending CTS: $sendResult")
aapsLogger.warn(LTag.PUMPBTCOMM, "Error sending CTS: $sendResult")
return null
}
readReset()
var expected: Byte = 0
try {
val firstPacket = expectBlePacket(0)
if (firstPacket !is PacketReceiveSuccess) {
return MessageReceiveError(firstPacket)
aapsLogger.warn(LTag.PUMPBTCOMM, "Error reading first packet:$firstPacket")
return null
}
val joiner = PayloadJoiner(firstPacket.payload)
maxMessageReadTries = joiner.fullFragments * 2 + 2
@ -110,7 +105,8 @@ class MessageIO(
expected++
val packet = expectBlePacket(expected)
if (packet !is PacketReceiveSuccess) {
return MessageReceiveError(packet)
aapsLogger.warn(LTag.PUMPBTCOMM, "Error reading packet:$packet")
return null
}
joiner.accumulate(packet.payload)
}
@ -118,21 +114,22 @@ class MessageIO(
expected++
val packet = expectBlePacket(expected)
if (packet !is PacketReceiveSuccess) {
return MessageReceiveError(packet)
aapsLogger.warn(LTag.PUMPBTCOMM, "Error reading packet:$packet")
return null
}
joiner.accumulate(packet.payload)
}
val fullPayload = joiner.finalize()
cmdBleIO.sendAndConfirmPacket(BleCommandSuccess.data)
return MessageReceiveSuccess(MessagePacket.parse(fullPayload))
return MessagePacket.parse(fullPayload)
} catch (e: IncorrectPacketException) {
aapsLogger.warn(LTag.PUMPBTCOMM, "Could not read message: $e")
aapsLogger.warn(LTag.PUMPBTCOMM, "Received incorrect packet: $e")
cmdBleIO.sendAndConfirmPacket(BleCommandAbort.data)
return MessageReceiveError("Received incorrect packet: $e", cause = e)
return null
} catch (e: CrcMismatchException) {
aapsLogger.warn(LTag.PUMPBTCOMM, "CRC mismatch: $e")
cmdBleIO.sendAndConfirmPacket(BleCommandFail.data)
return MessageReceiveError("CRC mismatch: $e", cause = e)
return null
} finally {
readReset()
}
@ -157,7 +154,7 @@ class MessageIO(
is BleCommandNack -> {
// // Consume NACK
val received = cmdBleIO.receivePacket()
if (received !is BleReceivePayload) {
if (received == null) {
MessageSendErrorSending(received.toString())
} else {
val sendResult = dataBleIO.sendAndConfirmPacket(packets[receivedCmd.idx.toInt()].toByteArray())
@ -185,29 +182,19 @@ class MessageIO(
while (messageReadTries < maxMessageReadTries && packetTries < MAX_PACKET_READ_TRIES) {
messageReadTries++
packetTries++
when (val received = dataBleIO.receivePacket()) {
is BleReceiveError -> {
if (nackOnTimeout)
cmdBleIO.sendAndConfirmPacket(BleCommandNack(index).data)
aapsLogger.info(LTag.PUMPBTCOMM, "Error receiving DATA packet: $received")
}
is BleReceivePayload -> {
val payload = received.payload
if (payload.isEmpty()) {
aapsLogger.info(LTag.PUMPBTCOMM, "Received empty payload at index $index")
continue
}
if (payload[0] == index) {
return PacketReceiveSuccess(payload)
}
receivedOutOfOrder[payload[0]] = payload
val received = dataBleIO.receivePacket()
if (received == null || received.isEmpty()) {
if (nackOnTimeout)
cmdBleIO.sendAndConfirmPacket(BleCommandNack(index).data)
}
aapsLogger.info(LTag.PUMPBTCOMM, "Error reading index: $index. Received: $received")
continue
}
if (received[0] == index) {
return PacketReceiveSuccess(received)
}
receivedOutOfOrder[received[0]] = received
cmdBleIO.sendAndConfirmPacket(BleCommandNack(index).data)
}
return PacketReceiveError("Reached the maximum number tries to read a packet")
}

View file

@ -5,7 +5,11 @@ 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.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.*
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
@ -37,11 +41,8 @@ internal class LTKExchanger(
throw PairingException("Could not send SP1: $sp1Result")
}
val podSps1 = msgIO.receiveMessage()
if (podSps1 !is MessageReceiveSuccess) {
throw PairingException("Could not read SPS1: $podSps1")
}
processSps1FromPod(podSps1.msg)
val podSps1 = msgIO.receiveMessage() ?: throw PairingException("Could not read SPS1")
processSps1FromPod(podSps1)
// now we have all the data to generate: confPod, confPdm, ltk and noncePrefix
seq++
@ -51,11 +52,8 @@ internal class LTKExchanger(
throw PairingException("Could not send sps2: $sp2Result")
}
val podSps2 = msgIO.receiveMessage()
if (podSps2 !is MessageReceiveSuccess) {
throw PairingException("Could not read SPS2: $podSps2")
}
validatePodSps2(podSps2.msg)
val podSps2 = msgIO.receiveMessage() ?: throw PairingException("Could not read SPS2")
validatePodSps2(podSps2)
seq++
// send SP0GP0
@ -65,12 +63,10 @@ internal class LTKExchanger(
}
// No exception throwing after this point. It is possible that the pod saved the LTK
val p0 = msgIO.receiveMessage()
if (p0 is MessageReceiveSuccess) {
validateP0(p0.msg)
} else {
aapsLogger.warn(LTag.PUMPBTCOMM, "Could not read P0: $p0")
}
msgIO.receiveMessage()
?.let { validateP0(it) }
?: aapsLogger.warn(LTag.PUMPBTCOMM, "Could not read P0")
return PairResult(
ltk = keyExchange.ltk,
msgSeq = seq

View file

@ -41,4 +41,4 @@ object ResponseUtil {
ResponseType.StatusResponseType.UNKNOWN -> throw CouldNotParseResponseException("Unrecognized additional status response type: ${payload[2]}")
}
}
}
}

View file

@ -9,6 +9,7 @@ import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.exceptio
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.exceptions.NakResponseException
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.exceptions.PodAlarmException
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.message.*
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.message.MessageType
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
@ -73,11 +74,11 @@ class Session(
var responseMsgPacket: MessagePacket? = null
for (i in 0..MAX_TRIES) {
val responseMsg = msgIO.receiveMessage()
if (responseMsg !is MessageReceiveSuccess) {
if (responseMsg == null) {
aapsLogger.debug(LTag.PUMPBTCOMM, "Error receiving response: $responseMsg")
continue
}
responseMsgPacket = responseMsg.msg
responseMsgPacket = responseMsg
}
responseMsgPacket

View file

@ -7,7 +7,6 @@ import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.endecryp
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.exceptions.SessionEstablishmentException
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.MessageReceiveSuccess
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.message.MessageSendSuccess
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.message.MessageType
import info.nightscout.androidaps.utils.extensions.toHex
@ -46,11 +45,9 @@ class SessionEstablisher(
throw SessionEstablishmentException("Could not send the EAP AKA challenge: $sendResult")
}
val challengeResponse = msgIO.receiveMessage()
if (challengeResponse !is MessageReceiveSuccess) {
throw SessionEstablishmentException("Could not establish session: $challengeResponse")
}
?: throw SessionEstablishmentException("Could not establish session")
processChallengeResponse(challengeResponse.msg)
processChallengeResponse(challengeResponse)
msgSeq++
var success = eapSuccess()
@ -101,7 +98,7 @@ class SessionEstablisher(
if (eapMsg.attributes.size == 1 && eapMsg.attributes[0] is EapAkaAttributeClientErrorCode) {
throw SessionEstablishmentException(
"Received CLIENT_ERROR_CODE for EAP-AKA challenge: ${
eapMsg.attributes[0].toByteArray().toHex()
eapMsg.attributes[0].toByteArray().toHex()
}"
)
}

View file

@ -64,6 +64,5 @@ sealed class PodEvent {
override fun toString(): String {
return "ResponseReceived(command=$command, response=$response)"
}
}
}

View file

@ -13,7 +13,7 @@ class ProgramInsulinCommand internal constructor(
multiCommandFlag: Boolean,
nonce: Int,
insulinProgramElements:
List<ShortInsulinProgramElement>,
List<ShortInsulinProgramElement>,
private val checksum: Short,
private val byte9: Byte,
private val byte10And11: Short,

View file

@ -468,8 +468,8 @@ class OmnipodDashOverviewFragment : DaggerFragment() {
private fun updateRefreshStatusButton() {
buttonBinding.buttonRefreshStatus.isEnabled =
podStateManager.isUniqueIdSet &&
podStateManager.activationProgress.isAtLeast(ActivationProgress.PHASE_1_COMPLETED) &&
isQueueEmpty()
podStateManager.activationProgress.isAtLeast(ActivationProgress.PHASE_1_COMPLETED) &&
isQueueEmpty()
}
private fun updateResumeDeliveryButton() {

View file

@ -21,11 +21,14 @@ class DashDeactivatePodViewModel @Inject constructor(
) : DeactivatePodViewModel(injector, logger) {
override fun doExecuteAction(): Single<PumpEnactResult> = Single.create { source ->
commandQueueProvider.customCommand(CommandDeactivatePod(), object : Callback() {
override fun run() {
source.onSuccess(result)
commandQueueProvider.customCommand(
CommandDeactivatePod(),
object : Callback() {
override fun run() {
source.onSuccess(result)
}
}
})
)
}
override fun discardPod() {