dash ble: implement retries when receiving messages

This commit is contained in:
Andrei Vereha 2021-03-21 14:56:32 +01:00
parent de6ca939ed
commit 7be0afda27
5 changed files with 65 additions and 21 deletions

View file

@ -129,6 +129,6 @@ class BleIO(
companion object { companion object {
private const val DEFAULT_IO_TIMEOUT_MS = 2000.toLong() private const val DEFAULT_IO_TIMEOUT_MS = 1000.toLong()
} }
} }

View file

@ -3,6 +3,6 @@ package info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.message
import info.nightscout.androidaps.utils.extensions.toHex import info.nightscout.androidaps.utils.extensions.toHex
class IncorrectPacketException( class IncorrectPacketException(
val expectedIndex: Byte, val payload: ByteArray,
val payload: ByteArray val expectedIndex: Byte? = null
) : Exception("Invalid payload: ${payload.toHex()}. Expected index: $expectedIndex") ) : Exception("Invalid payload: ${payload.toHex()}. Expected index: $expectedIndex")

View file

@ -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.io.PayloadJoiner
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.packet.BlePacket import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.packet.BlePacket
import info.nightscout.androidaps.utils.extensions.toHex import info.nightscout.androidaps.utils.extensions.toHex
import java.util.concurrent.TimeoutException
class MessageIO(private val aapsLogger: AAPSLogger, private val bleIO: BleIO) { class MessageIO(private val aapsLogger: AAPSLogger, private val bleIO: BleIO) {
val receivedOutOfOrder = LinkedHashMap<Byte, ByteArray>()
var maxTries = 3
var tries = 0
private fun expectCommandType(actual: BleCommand, expected: BleCommand) { private fun expectCommandType(actual: BleCommand, expected: BleCommand) {
if (actual.data.isEmpty()) { if (actual.data.isEmpty()) {
throw UnexpectedCommandException(actual) throw UnexpectedCommandException(actual)
@ -80,33 +85,72 @@ class MessageIO(private val aapsLogger: AAPSLogger, private val bleIO: BleIO) {
expectCommandType(BleCommand(expectSuccess), BleCommandSuccess()) 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 { fun receiveMessage(): MessagePacket {
val expectRTS = bleIO.receivePacket(CharacteristicType.CMD, MESSAGE_READ_TIMEOUT_MS) val expectRTS = bleIO.receivePacket(CharacteristicType.CMD, MESSAGE_READ_TIMEOUT_MS)
expectCommandType(BleCommand(expectRTS), BleCommandRTS()) expectCommandType(BleCommand(expectRTS), BleCommandRTS())
bleIO.sendAndConfirmPacket(CharacteristicType.CMD, BleCommandCTS().data) bleIO.sendAndConfirmPacket(CharacteristicType.CMD, BleCommandCTS().data)
readReset()
var expected: Byte = 0
try { 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) { 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) { if (joiner.oneExtraPacket) {
joiner.accumulate(bleIO.receivePacket(CharacteristicType.DATA)) expected++
joiner.accumulate(expectBlePacket(expected))
} }
val fullPayload = joiner.finalize() val fullPayload = joiner.finalize()
bleIO.sendAndConfirmPacket(CharacteristicType.CMD, BleCommandSuccess().data) bleIO.sendAndConfirmPacket(CharacteristicType.CMD, BleCommandSuccess().data)
return MessagePacket.parse(fullPayload) return MessagePacket.parse(fullPayload)
} catch (e: IncorrectPacketException) { } catch (e: IncorrectPacketException) {
aapsLogger.warn(LTag.PUMPBTCOMM, "Received incorrect packet: $e") aapsLogger.warn(LTag.PUMPBTCOMM, "Could not read message: $e")
bleIO.sendAndConfirmPacket(CharacteristicType.CMD, BleCommandNack(e.expectedIndex).data) bleIO.sendAndConfirmPacket(CharacteristicType.CMD, BleCommandAbort().data)
throw MessageIOException(cause = e) throw MessageIOException(cause = e)
} catch (e: CrcMismatchException) { } catch (e: CrcMismatchException) {
aapsLogger.warn(LTag.PUMPBTCOMM, "CRC mismatch: $e") aapsLogger.warn(LTag.PUMPBTCOMM, "CRC mismatch: $e")
bleIO.sendAndConfirmPacket(CharacteristicType.CMD, BleCommandFail().data) bleIO.sendAndConfirmPacket(CharacteristicType.CMD, BleCommandFail().data)
throw MessageIOException(cause = e) throw MessageIOException(cause = e)
} }
receivedOutOfOrder.clear()
} }
companion object { companion object {
private const val MESSAGE_READ_TIMEOUT_MS = 4000.toLong()
private const val MESSAGE_READ_TIMEOUT_MS = 2500.toLong()
} }
} }

View file

@ -29,11 +29,11 @@ class PayloadJoiner(private val firstPacket: ByteArray) {
fun accumulate(packet: ByteArray) { fun accumulate(packet: ByteArray) {
if (packet.size < 3) { // idx, size, at least 1 byte of payload 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() val idx = packet[0].toInt()
if (idx != expectedIndex + 1) { if (idx != expectedIndex + 1) {
throw IncorrectPacketException((expectedIndex + 1).toByte(), packet) throw IncorrectPacketException(packet, (expectedIndex + 1).toByte())
} }
expectedIndex++ expectedIndex++
when { when {
@ -53,7 +53,7 @@ class PayloadJoiner(private val firstPacket: ByteArray) {
} }
idx > fullFragments -> { idx > fullFragments -> {
throw IncorrectPacketException(idx.toByte(), packet) throw IncorrectPacketException(packet, idx.toByte())
} }
} }
} }

View file

@ -47,25 +47,25 @@ data class FirstBlePacket(
fun parse(payload: ByteArray): FirstBlePacket { fun parse(payload: ByteArray): FirstBlePacket {
if (payload.size < FirstBlePacket.HEADER_SIZE_WITH_MIDDLE_PACKETS) { if (payload.size < FirstBlePacket.HEADER_SIZE_WITH_MIDDLE_PACKETS) {
throw IncorrectPacketException(0, payload) throw IncorrectPacketException(payload, 0)
} }
if (payload[0].toInt() != 0) { if (payload[0].toInt() != 0) {
// most likely we lost the first packet. // most likely we lost the first packet.
// TODO: try to recover with NACKs? // TODO: try to recover with NACKs?
throw IncorrectPacketException(0, payload) throw IncorrectPacketException(payload, 0)
} }
val fullFragments = payload[1].toInt() val fullFragments = payload[1].toInt()
require(fullFragments < MAX_FRAGMENTS) { "Received more than ${MAX_FRAGMENTS} fragments" } require(fullFragments < MAX_FRAGMENTS) { "Received more than ${MAX_FRAGMENTS} fragments" }
when { when {
// Without middle packets // Without middle packets
payload.size < HEADER_SIZE_WITHOUT_MIDDLE_PACKETS -> payload.size < HEADER_SIZE_WITHOUT_MIDDLE_PACKETS ->
throw IncorrectPacketException(0, payload) throw IncorrectPacketException(payload, 0)
fullFragments == 0 -> { fullFragments == 0 -> {
val rest = payload[6] val rest = payload[6]
val end = Integer.min(rest + HEADER_SIZE_WITHOUT_MIDDLE_PACKETS, payload.size) val end = Integer.min(rest + HEADER_SIZE_WITHOUT_MIDDLE_PACKETS, payload.size)
if (end > payload.size) { if (end > payload.size) {
throw IncorrectPacketException(0, payload) throw IncorrectPacketException(payload, 0)
} }
return FirstBlePacket( return FirstBlePacket(
fullFragments = fullFragments, fullFragments = fullFragments,
@ -78,7 +78,7 @@ data class FirstBlePacket(
// With middle packets // With middle packets
payload.size < BlePacket.MAX_SIZE -> payload.size < BlePacket.MAX_SIZE ->
throw IncorrectPacketException(0, payload) throw IncorrectPacketException(payload, 0)
else -> { else -> {
return FirstBlePacket( return FirstBlePacket(
@ -112,7 +112,7 @@ data class MiddleBlePacket(val index: Byte, override val payload: ByteArray) : B
fun parse(payload: ByteArray): MiddleBlePacket { fun parse(payload: ByteArray): MiddleBlePacket {
if (payload.size < MAX_SIZE) { if (payload.size < MAX_SIZE) {
throw IncorrectPacketException(0, payload) throw IncorrectPacketException(payload, 0)
} }
return MiddleBlePacket( return MiddleBlePacket(
index = payload[0], index = payload[0],
@ -150,12 +150,12 @@ data class LastBlePacket(
fun parse(payload: ByteArray): LastBlePacket { fun parse(payload: ByteArray): LastBlePacket {
if (payload.size < HEADER_SIZE) { if (payload.size < HEADER_SIZE) {
throw IncorrectPacketException(0, payload) throw IncorrectPacketException(payload, 0)
} }
val rest = payload[1] val rest = payload[1]
val end = Integer.min(rest + HEADER_SIZE, payload.size) val end = Integer.min(rest + HEADER_SIZE, payload.size)
if (payload.size < end) { if (payload.size < end) {
throw IncorrectPacketException(0, payload) throw IncorrectPacketException(payload, 0)
} }
return LastBlePacket( return LastBlePacket(
index = payload[0], index = payload[0],
@ -185,7 +185,7 @@ data class LastOptionalPlusOneBlePacket(
fun parse(payload: ByteArray): LastOptionalPlusOneBlePacket { fun parse(payload: ByteArray): LastOptionalPlusOneBlePacket {
val size = payload[1].toInt() val size = payload[1].toInt()
if (payload.size < HEADER_SIZE + size) { if (payload.size < HEADER_SIZE + size) {
throw IncorrectPacketException(0, payload) throw IncorrectPacketException(payload, 0)
} }
return LastOptionalPlusOneBlePacket( return LastOptionalPlusOneBlePacket(
index = payload[0], index = payload[0],