From 7be0afda27953fd0c8f78e0de4c5fd24eb7157a5 Mon Sep 17 00:00:00 2001 From: Andrei Vereha Date: Sun, 21 Mar 2021 14:56:32 +0100 Subject: [PATCH] dash ble: implement retries when receiving messages --- .../pump/omnipod/dash/driver/comm/io/BleIO.kt | 2 +- .../comm/message/IncorrectPacketException.kt | 4 +- .../dash/driver/comm/message/MessageIO.kt | 56 +++++++++++++++++-- .../dash/driver/comm/message/PayloadJoiner.kt | 6 +- .../dash/driver/comm/packet/BlePacket.kt | 18 +++--- 5 files changed, 65 insertions(+), 21 deletions(-) diff --git a/omnipod-dash/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/dash/driver/comm/io/BleIO.kt b/omnipod-dash/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/dash/driver/comm/io/BleIO.kt index 5c0c496b5f..0a939805dd 100644 --- a/omnipod-dash/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/dash/driver/comm/io/BleIO.kt +++ b/omnipod-dash/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/dash/driver/comm/io/BleIO.kt @@ -129,6 +129,6 @@ class BleIO( companion object { - private const val DEFAULT_IO_TIMEOUT_MS = 2000.toLong() + private const val DEFAULT_IO_TIMEOUT_MS = 1000.toLong() } } diff --git a/omnipod-dash/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/dash/driver/comm/message/IncorrectPacketException.kt b/omnipod-dash/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/dash/driver/comm/message/IncorrectPacketException.kt index bb3846c853..e1e08136c3 100644 --- a/omnipod-dash/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/dash/driver/comm/message/IncorrectPacketException.kt +++ b/omnipod-dash/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/dash/driver/comm/message/IncorrectPacketException.kt @@ -3,6 +3,6 @@ package info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.message import info.nightscout.androidaps.utils.extensions.toHex class IncorrectPacketException( - val expectedIndex: Byte, - val payload: ByteArray + val payload: ByteArray, + val expectedIndex: Byte? = null ) : Exception("Invalid payload: ${payload.toHex()}. Expected index: $expectedIndex") diff --git a/omnipod-dash/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/dash/driver/comm/message/MessageIO.kt b/omnipod-dash/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/dash/driver/comm/message/MessageIO.kt index 5b536e8c34..40614cb3cb 100644 --- a/omnipod-dash/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/dash/driver/comm/message/MessageIO.kt +++ b/omnipod-dash/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/dash/driver/comm/message/MessageIO.kt @@ -10,9 +10,14 @@ import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.io.Chara import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.io.PayloadJoiner import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.packet.BlePacket import info.nightscout.androidaps.utils.extensions.toHex +import java.util.concurrent.TimeoutException class MessageIO(private val aapsLogger: AAPSLogger, private val bleIO: BleIO) { + val receivedOutOfOrder = LinkedHashMap() + var maxTries = 3 + var tries = 0 + private fun expectCommandType(actual: BleCommand, expected: BleCommand) { if (actual.data.isEmpty()) { throw UnexpectedCommandException(actual) @@ -80,33 +85,72 @@ class MessageIO(private val aapsLogger: AAPSLogger, private val bleIO: BleIO) { expectCommandType(BleCommand(expectSuccess), BleCommandSuccess()) } + private fun expectBlePacket(index: Byte): ByteArray { + receivedOutOfOrder[index]?.let { + return it + } + while (tries < maxTries) { + try { + tries++ + val payload = bleIO.receivePacket(CharacteristicType.DATA) + if (payload.isEmpty()) { + throw IncorrectPacketException(payload, index) + } + if (payload[0] == index) { + return payload + } + receivedOutOfOrder[payload[0]] = payload + bleIO.sendAndConfirmPacket(CharacteristicType.CMD, BleCommandNack(index).data) + } catch (e: TimeoutException) { + bleIO.sendAndConfirmPacket(CharacteristicType.CMD, BleCommandNack(index).data) + continue + } + } + throw MessageIOException("Ran out retries trying to receive a packet. $maxTries") + } + + private fun readReset() { + maxTries = 3 + tries = 0 + receivedOutOfOrder.clear() + } + fun receiveMessage(): MessagePacket { val expectRTS = bleIO.receivePacket(CharacteristicType.CMD, MESSAGE_READ_TIMEOUT_MS) expectCommandType(BleCommand(expectRTS), BleCommandRTS()) bleIO.sendAndConfirmPacket(CharacteristicType.CMD, BleCommandCTS().data) + readReset() + var expected: Byte = 0 try { - val joiner = PayloadJoiner(bleIO.receivePacket(CharacteristicType.DATA)) + val firstPacket = expectBlePacket(0) + val joiner = PayloadJoiner(firstPacket) + maxTries = joiner.fullFragments * 2 + 2 for (i in 1 until joiner.fullFragments + 1) { - joiner.accumulate(bleIO.receivePacket(CharacteristicType.DATA)) + expected++ + val packet = expectBlePacket(expected) + joiner.accumulate(packet) } if (joiner.oneExtraPacket) { - joiner.accumulate(bleIO.receivePacket(CharacteristicType.DATA)) + expected++ + joiner.accumulate(expectBlePacket(expected)) } val fullPayload = joiner.finalize() bleIO.sendAndConfirmPacket(CharacteristicType.CMD, BleCommandSuccess().data) return MessagePacket.parse(fullPayload) } catch (e: IncorrectPacketException) { - aapsLogger.warn(LTag.PUMPBTCOMM, "Received incorrect packet: $e") - bleIO.sendAndConfirmPacket(CharacteristicType.CMD, BleCommandNack(e.expectedIndex).data) + aapsLogger.warn(LTag.PUMPBTCOMM, "Could not read message: $e") + bleIO.sendAndConfirmPacket(CharacteristicType.CMD, BleCommandAbort().data) throw MessageIOException(cause = e) } catch (e: CrcMismatchException) { aapsLogger.warn(LTag.PUMPBTCOMM, "CRC mismatch: $e") bleIO.sendAndConfirmPacket(CharacteristicType.CMD, BleCommandFail().data) throw MessageIOException(cause = e) } + receivedOutOfOrder.clear() } companion object { - private const val MESSAGE_READ_TIMEOUT_MS = 4000.toLong() + + private const val MESSAGE_READ_TIMEOUT_MS = 2500.toLong() } } diff --git a/omnipod-dash/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/dash/driver/comm/message/PayloadJoiner.kt b/omnipod-dash/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/dash/driver/comm/message/PayloadJoiner.kt index 951e053cb3..f1d1f23850 100644 --- a/omnipod-dash/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/dash/driver/comm/message/PayloadJoiner.kt +++ b/omnipod-dash/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/dash/driver/comm/message/PayloadJoiner.kt @@ -29,11 +29,11 @@ class PayloadJoiner(private val firstPacket: ByteArray) { fun accumulate(packet: ByteArray) { if (packet.size < 3) { // idx, size, at least 1 byte of payload - throw IncorrectPacketException((expectedIndex + 1).toByte(), packet) + throw IncorrectPacketException(packet, (expectedIndex + 1).toByte()) } val idx = packet[0].toInt() if (idx != expectedIndex + 1) { - throw IncorrectPacketException((expectedIndex + 1).toByte(), packet) + throw IncorrectPacketException(packet, (expectedIndex + 1).toByte()) } expectedIndex++ when { @@ -53,7 +53,7 @@ class PayloadJoiner(private val firstPacket: ByteArray) { } idx > fullFragments -> { - throw IncorrectPacketException(idx.toByte(), packet) + throw IncorrectPacketException(packet, idx.toByte()) } } } diff --git a/omnipod-dash/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/dash/driver/comm/packet/BlePacket.kt b/omnipod-dash/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/dash/driver/comm/packet/BlePacket.kt index 58ad0a1c59..6ce5110348 100644 --- a/omnipod-dash/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/dash/driver/comm/packet/BlePacket.kt +++ b/omnipod-dash/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/dash/driver/comm/packet/BlePacket.kt @@ -47,25 +47,25 @@ data class FirstBlePacket( fun parse(payload: ByteArray): FirstBlePacket { if (payload.size < FirstBlePacket.HEADER_SIZE_WITH_MIDDLE_PACKETS) { - throw IncorrectPacketException(0, payload) + throw IncorrectPacketException(payload, 0) } if (payload[0].toInt() != 0) { // most likely we lost the first packet. // TODO: try to recover with NACKs? - throw IncorrectPacketException(0, payload) + throw IncorrectPacketException(payload, 0) } val fullFragments = payload[1].toInt() require(fullFragments < MAX_FRAGMENTS) { "Received more than ${MAX_FRAGMENTS} fragments" } when { // Without middle packets payload.size < HEADER_SIZE_WITHOUT_MIDDLE_PACKETS -> - throw IncorrectPacketException(0, payload) + throw IncorrectPacketException(payload, 0) fullFragments == 0 -> { val rest = payload[6] val end = Integer.min(rest + HEADER_SIZE_WITHOUT_MIDDLE_PACKETS, payload.size) if (end > payload.size) { - throw IncorrectPacketException(0, payload) + throw IncorrectPacketException(payload, 0) } return FirstBlePacket( fullFragments = fullFragments, @@ -78,7 +78,7 @@ data class FirstBlePacket( // With middle packets payload.size < BlePacket.MAX_SIZE -> - throw IncorrectPacketException(0, payload) + throw IncorrectPacketException(payload, 0) else -> { return FirstBlePacket( @@ -112,7 +112,7 @@ data class MiddleBlePacket(val index: Byte, override val payload: ByteArray) : B fun parse(payload: ByteArray): MiddleBlePacket { if (payload.size < MAX_SIZE) { - throw IncorrectPacketException(0, payload) + throw IncorrectPacketException(payload, 0) } return MiddleBlePacket( index = payload[0], @@ -150,12 +150,12 @@ data class LastBlePacket( fun parse(payload: ByteArray): LastBlePacket { if (payload.size < HEADER_SIZE) { - throw IncorrectPacketException(0, payload) + throw IncorrectPacketException(payload, 0) } val rest = payload[1] val end = Integer.min(rest + HEADER_SIZE, payload.size) if (payload.size < end) { - throw IncorrectPacketException(0, payload) + throw IncorrectPacketException(payload, 0) } return LastBlePacket( index = payload[0], @@ -185,7 +185,7 @@ data class LastOptionalPlusOneBlePacket( fun parse(payload: ByteArray): LastOptionalPlusOneBlePacket { val size = payload[1].toInt() if (payload.size < HEADER_SIZE + size) { - throw IncorrectPacketException(0, payload) + throw IncorrectPacketException(payload, 0) } return LastOptionalPlusOneBlePacket( index = payload[0],