From 100be23cf4427d2a6f9a0b07f0aa8a9160f08d6d Mon Sep 17 00:00:00 2001 From: jbr7rr <> Date: Tue, 10 Oct 2023 12:36:12 +0200 Subject: [PATCH] Medtrum: Improve failure modes; - ReadDataPacket: Add crc check - BLEComm: Differentiate between retryable and non-retryable error - setBolus: Use readStatus to wait for bolus if any --- .../pump/medtrum/comm/ReadDataPacket.kt | 24 +++ .../pump/medtrum/comm/WriteCommandPackets.kt | 18 +- .../comm/packets/NotificationPacket.kt | 48 +++--- .../pump/medtrum/services/BLEComm.kt | 14 +- .../pump/medtrum/services/MedtrumService.kt | 10 +- .../nightscout/pump/medtrum/util/CrcUtil.kt | 13 ++ pump/medtrum/src/main/res/values/strings.xml | 1 + .../pump/medtrum/comm/ReadDataPacketTest.kt | 154 ++++++++++++++++++ 8 files changed, 234 insertions(+), 48 deletions(-) create mode 100644 pump/medtrum/src/main/java/info/nightscout/pump/medtrum/util/CrcUtil.kt create mode 100644 pump/medtrum/src/test/java/info/nightscout/pump/medtrum/comm/ReadDataPacketTest.kt diff --git a/pump/medtrum/src/main/java/info/nightscout/pump/medtrum/comm/ReadDataPacket.kt b/pump/medtrum/src/main/java/info/nightscout/pump/medtrum/comm/ReadDataPacket.kt index e091cb8f73..9eb2e01e89 100644 --- a/pump/medtrum/src/main/java/info/nightscout/pump/medtrum/comm/ReadDataPacket.kt +++ b/pump/medtrum/src/main/java/info/nightscout/pump/medtrum/comm/ReadDataPacket.kt @@ -1,12 +1,32 @@ package info.nightscout.pump.medtrum.comm +import CrcUtils.calcCrc8 + class ReadDataPacket(data: ByteArray) { private var totalData = data.copyOfRange(0, data.size - 1) // Strip crc + private var failed = false private var dataSize: Byte = data[0] + private var sequenceNumber: Byte = data[3] + + init { + val crcInitialChunk = calcCrc8(data.copyOfRange(0, data.size - 1), data.size - 1) + + if (crcInitialChunk != data[data.size - 1]) { + failed = true + } + } fun addData(newData: ByteArray) { totalData += newData.copyOfRange(4, newData.size - 1) // Strip header and crc + sequenceNumber++ + val crcNewChunk = calcCrc8(newData.copyOfRange(0, newData.size - 1), newData.size - 1) + if (crcNewChunk != newData[newData.size - 1]) { + failed = true + } + if (sequenceNumber != newData[3]) { + failed = true + } } fun allDataReceived(): Boolean { @@ -16,4 +36,8 @@ class ReadDataPacket(data: ByteArray) { fun getData(): ByteArray { return totalData } + + fun failed(): Boolean { + return failed + } } diff --git a/pump/medtrum/src/main/java/info/nightscout/pump/medtrum/comm/WriteCommandPackets.kt b/pump/medtrum/src/main/java/info/nightscout/pump/medtrum/comm/WriteCommandPackets.kt index 31655f3ed9..5d3974e133 100644 --- a/pump/medtrum/src/main/java/info/nightscout/pump/medtrum/comm/WriteCommandPackets.kt +++ b/pump/medtrum/src/main/java/info/nightscout/pump/medtrum/comm/WriteCommandPackets.kt @@ -1,8 +1,8 @@ package info.nightscout.pump.medtrum.comm -class WriteCommandPackets(data: ByteArray, sequenceNumber: Int) { +import CrcUtils.calcCrc8 - private val CRC_8_TABLE: IntArray = intArrayOf(0, 155, 173, 54, 193, 90, 108, 247, 25, 130, 180, 47, 216, 67, 117, 238, 50, 169, 159, 4, 243, 104, 94, 197, 43, 176, 134, 29, 234, 113, 71, 220, 100, 255, 201, 82, 165, 62, 8, 147, 125, 230, 208, 75, 188, 39, 17, 138, 86, 205, 251, 96, 151, 12, 58, 161, 79, 212, 226, 121, 142, 21, 35, 184, 200, 83, 101, 254, 9, 146, 164, 63, 209, 74, 124, 231, 16, 139, 189, 38, 250, 97, 87, 204, 59, 160, 150, 13, 227, 120, 78, 213, 34, 185, 143, 20, 172, 55, 1, 154, 109, 246, 192, 91, 181, 46, 24, 131, 116, 239, 217, 66, 158, 5, 51, 168, 95, 196, 242, 105, 135, 28, 42, 177, 70, 221, 235, 112, 11, 144, 166, 61, 202, 81, 103, 252, 18, 137, 191, 36, 211, 72, 126, 229, 57, 162, 148, 15, 248, 99, 85, 206, 32, 187, 141, 22, 225, 122, 76, 215, 111, 244, 194, 89, 174, 53, 3, 152, 118, 237, 219, 64, 183, 44, 26, 129, 93, 198, 240, 107, 156, 7, 49, 170, 68, 223, 233, 114, 133, 30, 40, 179, 195, 88, 110, 245, 2, 153, 175, 52, 218, 65, 119, 236, 27, 128, 182, 45, 241, 106, 92, 199, 48, 171, 157, 6, 232, 115, 69, 222, 41, 178, 132, 31, 167, 60, 10, 145, 102, 253, 203, 80, 190, 37, 19, 136, 127, 228, 210, 73, 149, 14, 56, 163, 84, 207, 249, 98, 140, 23, 33, 186, 77, 214, 224, 123) +class WriteCommandPackets(data: ByteArray, sequenceNumber: Int) { private val packages = mutableListOf() private var index = 0 @@ -17,7 +17,7 @@ class WriteCommandPackets(data: ByteArray, sequenceNumber: Int) { ) var tmp: ByteArray = header + data.copyOfRange(1, data.size) - val totalCommand: ByteArray = tmp + calcCrc8(tmp, tmp.size).toByte() + val totalCommand: ByteArray = tmp + calcCrc8(tmp, tmp.size) if ((totalCommand.size - header.size) <= 15) { packages.add(totalCommand + 0.toByte()) @@ -28,7 +28,7 @@ class WriteCommandPackets(data: ByteArray, sequenceNumber: Int) { while (remainingCommand.size > 15) { header[3] = pkgIndex.toByte() tmp = header + remainingCommand.copyOfRange(0, 15) - packages.add(tmp + calcCrc8(tmp, tmp.size).toByte()) + packages.add(tmp + calcCrc8(tmp, tmp.size)) remainingCommand = remainingCommand.copyOfRange(15, remainingCommand.size) pkgIndex = (pkgIndex + 1) % 256 @@ -37,7 +37,7 @@ class WriteCommandPackets(data: ByteArray, sequenceNumber: Int) { // Add last package header[3] = pkgIndex.toByte() tmp = header + remainingCommand - packages.add(tmp + calcCrc8(tmp, tmp.size).toByte()) + packages.add(tmp + calcCrc8(tmp, tmp.size)) } } @@ -53,12 +53,4 @@ class WriteCommandPackets(data: ByteArray, sequenceNumber: Int) { fun allPacketsConsumed(): Boolean { return index >= packages.size } - - private fun calcCrc8(value: ByteArray, size: Int): Int { - var crc8 = 0 - for (i in 0 until size) { - crc8 = CRC_8_TABLE[(value[i].toInt() and 255) xor (crc8 and 255)] and 255 - } - return crc8 - } } diff --git a/pump/medtrum/src/main/java/info/nightscout/pump/medtrum/comm/packets/NotificationPacket.kt b/pump/medtrum/src/main/java/info/nightscout/pump/medtrum/comm/packets/NotificationPacket.kt index 1af3b3b6dd..929f574165 100644 --- a/pump/medtrum/src/main/java/info/nightscout/pump/medtrum/comm/packets/NotificationPacket.kt +++ b/pump/medtrum/src/main/java/info/nightscout/pump/medtrum/comm/packets/NotificationPacket.kt @@ -12,8 +12,6 @@ import info.nightscout.pump.medtrum.extension.toLong import info.nightscout.pump.medtrum.util.MedtrumTimeUtil import javax.inject.Inject -typealias MaskHandler = (ByteArray, Int) -> Int - class NotificationPacket(val injector: HasAndroidInjector) { /** @@ -81,7 +79,7 @@ class NotificationPacket(val injector: HasAndroidInjector) { private const val SIZE_UNUSED_LEGACY = 2 } - val maskHandlers: Map = mapOf( + val maskHandlers: Map Int> = mapOf( MASK_SUSPEND to ::handleSuspend, MASK_NORMAL_BOLUS to ::handleNormalBolus, MASK_EXTENDED_BOLUS to ::handleExtendedBolus, @@ -126,27 +124,27 @@ class NotificationPacket(val injector: HasAndroidInjector) { fun handleMaskedMessage(data: ByteArray): Boolean { val fieldMask = data.copyOfRange(0, 2).toInt() var offset = 2 - + val expectedLength = calculateExpectedLengthBasedOnFieldMask(fieldMask) if (data.size < expectedLength) { aapsLogger.error(LTag.PUMPCOMM, "Incorrect message length. Expected at least $expectedLength bytes.") return false } - + aapsLogger.debug(LTag.PUMPCOMM, "Message field mask: $fieldMask") - + for ((mask, handler) in maskHandlers) { if (fieldMask and mask != 0) { offset = handler(data, offset) } } - + return true } private fun calculateExpectedLengthBasedOnFieldMask(fieldMask: Int): Int { var expectedLength = SIZE_FIELD_MASK - + val sizeMap = mapOf( MASK_SUSPEND to SIZE_SUSPEND, MASK_NORMAL_BOLUS to SIZE_NORMAL_BOLUS, @@ -165,13 +163,13 @@ class NotificationPacket(val injector: HasAndroidInjector) { MASK_UNUSED_AUTO_STATUS to SIZE_UNUSED_AUTO_STATUS, MASK_UNUSED_LEGACY to SIZE_UNUSED_LEGACY ) - + for ((mask, size) in sizeMap) { if (fieldMask and mask != 0) { expectedLength += size } } - + return expectedLength } @@ -181,7 +179,7 @@ class NotificationPacket(val injector: HasAndroidInjector) { aapsLogger.debug(LTag.PUMPCOMM, "Suspend time: ${medtrumPump.suspendTime}") return offset + SIZE_SUSPEND } - + private fun handleNormalBolus(data: ByteArray, offset: Int): Int { aapsLogger.debug(LTag.PUMPCOMM, "Normal bolus notification received") val bolusData = data.copyOfRange(offset, offset + 1).toInt() @@ -192,13 +190,13 @@ class NotificationPacket(val injector: HasAndroidInjector) { medtrumPump.handleBolusStatusUpdate(bolusType, bolusCompleted, bolusDelivered) return offset + SIZE_NORMAL_BOLUS } - + private fun handleExtendedBolus(data: ByteArray, offset: Int): Int { aapsLogger.error(LTag.PUMPCOMM, "Extended bolus notification received, extended bolus not supported!") aapsLogger.debug(LTag.PUMPCOMM, "Extended bolus data: ${data.copyOfRange(offset, offset + SIZE_EXTENDED_BOLUS).toLong()}") return offset + SIZE_EXTENDED_BOLUS } - + private fun handleBasal(data: ByteArray, offset: Int): Int { aapsLogger.debug(LTag.PUMPCOMM, "Basal notification received") val basalType = enumValues()[data.copyOfRange(offset, offset + 1).toInt()] @@ -218,21 +216,21 @@ class NotificationPacket(val injector: HasAndroidInjector) { } return offset + SIZE_BASAL } - + private fun handleSetup(data: ByteArray, offset: Int): Int { aapsLogger.debug(LTag.PUMPCOMM, "Setup notification received") medtrumPump.primeProgress = data.copyOfRange(offset, offset + 1).toInt() aapsLogger.debug(LTag.PUMPCOMM, "Prime progress: ${medtrumPump.primeProgress}") return offset + SIZE_SETUP } - + private fun handleReservoir(data: ByteArray, offset: Int): Int { aapsLogger.debug(LTag.PUMPCOMM, "Reservoir notification received") medtrumPump.reservoir = data.copyOfRange(offset, offset + 2).toInt() * 0.05 aapsLogger.debug(LTag.PUMPCOMM, "Reservoir: ${medtrumPump.reservoir}") return offset + SIZE_RESERVOIR } - + private fun handleStartTime(data: ByteArray, offset: Int): Int { aapsLogger.debug(LTag.PUMPCOMM, "Start time notification received") newPatchStartTime = MedtrumTimeUtil().convertPumpTimeToSystemTimeMillis(data.copyOfRange(offset, offset + 4).toLong()) @@ -243,7 +241,7 @@ class NotificationPacket(val injector: HasAndroidInjector) { aapsLogger.debug(LTag.PUMPCOMM, "Patch start time: $newPatchStartTime") return offset + SIZE_START_TIME } - + private fun handleBattery(data: ByteArray, offset: Int): Int { aapsLogger.debug(LTag.PUMPCOMM, "Battery notification received") val parameter = data.copyOfRange(offset, offset + 3).toInt() @@ -253,7 +251,7 @@ class NotificationPacket(val injector: HasAndroidInjector) { aapsLogger.debug(LTag.PUMPCOMM, "Battery voltage A: ${medtrumPump.batteryVoltage_A}, battery voltage B: ${medtrumPump.batteryVoltage_B}") return offset + SIZE_BATTERY } - + private fun handleStorage(data: ByteArray, offset: Int): Int { aapsLogger.debug(LTag.PUMPCOMM, "Storage notification received") val sequence = data.copyOfRange(offset, offset + 2).toInt() @@ -272,7 +270,7 @@ class NotificationPacket(val injector: HasAndroidInjector) { aapsLogger.debug(LTag.PUMPCOMM, "Last known sequence number: ${medtrumPump.currentSequenceNumber}, patch id: ${patchId}") return offset + SIZE_STORAGE } - + private fun handleAlarm(data: ByteArray, offset: Int): Int { val alarmFlags = data.copyOfRange(offset, offset + 2).toInt() val alarmParameter = data.copyOfRange(offset + 2, offset + 4).toInt() @@ -300,39 +298,39 @@ class NotificationPacket(val injector: HasAndroidInjector) { } return offset + SIZE_ALARM } - + private fun handleAge(data: ByteArray, offset: Int): Int { aapsLogger.debug(LTag.PUMPCOMM, "Age notification received") medtrumPump.patchAge = data.copyOfRange(offset, offset + 4).toLong() aapsLogger.debug(LTag.PUMPCOMM, "Patch age: ${medtrumPump.patchAge}") return offset + SIZE_AGE } - + private fun handleUnknown1(data: ByteArray, offset: Int): Int { aapsLogger.debug(LTag.PUMPCOMM, "Magneto placement notification received!") val magnetoPlacement = data.copyOfRange(offset, offset + 2).toInt() aapsLogger.debug(LTag.PUMPCOMM, "Magneto placement: $magnetoPlacement") return offset + SIZE_MAGNETO_PLACE } - + private fun handleUnusedCGM(data: ByteArray, offset: Int): Int { aapsLogger.debug(LTag.PUMPCOMM, "Unused CGM notification received, not handled!") aapsLogger.debug(LTag.PUMPCOMM, "Unused CGM data: ${data.copyOfRange(offset, offset + SIZE_UNUSED_CGM).toLong()}") return offset + SIZE_UNUSED_CGM } - + private fun handleUnusedCommandConfirm(data: ByteArray, offset: Int): Int { aapsLogger.warn(LTag.PUMPCOMM, "Unused command confirm notification received, not handled!") aapsLogger.debug(LTag.PUMPCOMM, "Unused command confirm data: ${data.copyOfRange(offset, offset + SIZE_UNUSED_COMMAND_CONFIRM).toLong()}") return offset + SIZE_UNUSED_COMMAND_CONFIRM } - + private fun handleUnusedAutoStatus(data: ByteArray, offset: Int): Int { aapsLogger.debug(LTag.PUMPCOMM, "Unused auto status notification received, not handled!") aapsLogger.debug(LTag.PUMPCOMM, "Unused auto status data: ${data.copyOfRange(offset, offset + SIZE_UNUSED_AUTO_STATUS).toLong()}") return offset + SIZE_UNUSED_AUTO_STATUS } - + private fun handleUnusedLegacy(data: ByteArray, offset: Int): Int { aapsLogger.debug(LTag.PUMPCOMM, "Unused legacy notification received, not handled!") aapsLogger.debug(LTag.PUMPCOMM, "Unused legacy data: ${data.copyOfRange(offset, offset + SIZE_UNUSED_LEGACY).toLong()}") diff --git a/pump/medtrum/src/main/java/info/nightscout/pump/medtrum/services/BLEComm.kt b/pump/medtrum/src/main/java/info/nightscout/pump/medtrum/services/BLEComm.kt index cc5283b9dd..c2b76b71fa 100644 --- a/pump/medtrum/src/main/java/info/nightscout/pump/medtrum/services/BLEComm.kt +++ b/pump/medtrum/src/main/java/info/nightscout/pump/medtrum/services/BLEComm.kt @@ -40,7 +40,7 @@ interface BLECommCallback { fun onBLEDisconnected() fun onNotification(notification: ByteArray) fun onIndication(indication: ByteArray) - fun onSendMessageError(reason: String) + fun onSendMessageError(reason: String, isRetryAble: Boolean) } @Singleton @@ -258,7 +258,11 @@ class BLEComm @Inject internal constructor( mReadPacket?.addData(value) } if (mReadPacket?.allDataReceived() == true) { - mReadPacket?.getData()?.let { mCallback?.onIndication(it) } + if (mReadPacket?.failed() == true) { + mCallback?.onSendMessageError("ReadDataPacket failed", false) + } else { + mReadPacket?.getData()?.let { mCallback?.onIndication(it) } + } mReadPacket = null } } @@ -279,7 +283,7 @@ class BLEComm @Inject internal constructor( } } } else { - mCallback?.onSendMessageError("onCharacteristicWrite failure") + mCallback?.onSendMessageError("onCharacteristicWrite failure", true) } } @@ -404,7 +408,7 @@ class BLEComm @Inject internal constructor( writeCharacteristic(uartWriteBTGattChar, value) } else { aapsLogger.error(LTag.PUMPBTCOMM, "sendMessage error in writePacket!") - mCallback?.onSendMessageError("error in writePacket!") + mCallback?.onSendMessageError("error in writePacket!", false) } } @@ -430,7 +434,7 @@ class BLEComm @Inject internal constructor( aapsLogger.debug(LTag.PUMPBTCOMM, "writeCharacteristic: ${Arrays.toString(data)}") val success = mBluetoothGatt?.writeCharacteristic(characteristic) if (success != true) { - mCallback?.onSendMessageError("Failed to write characteristic") + mCallback?.onSendMessageError("Failed to write characteristic", true) } } }, WRITE_DELAY_MILLIS) diff --git a/pump/medtrum/src/main/java/info/nightscout/pump/medtrum/services/MedtrumService.kt b/pump/medtrum/src/main/java/info/nightscout/pump/medtrum/services/MedtrumService.kt index c04f5e9f1d..d3b85463e1 100644 --- a/pump/medtrum/src/main/java/info/nightscout/pump/medtrum/services/MedtrumService.kt +++ b/pump/medtrum/src/main/java/info/nightscout/pump/medtrum/services/MedtrumService.kt @@ -339,7 +339,7 @@ class MedtrumService : DaggerService(), BLECommCallback { if (!sendBolusCommand(insulin)) { aapsLogger.error(LTag.PUMPCOMM, "Failed to set bolus") - commandQueue.loadEvents(null) // make sure if anything is delivered (which is highly unlikely at this point) we get it + commandQueue.readStatus(rh.gs(R.string.bolus_error), null) // make sure if anything is delivered (which is highly unlikely at this point) we get it t.insulin = 0.0 return false } @@ -735,9 +735,9 @@ class MedtrumService : DaggerService(), BLECommCallback { currentState.onIndication(indication) } - override fun onSendMessageError(reason: String) { + override fun onSendMessageError(reason: String, isRetryAble: Boolean) { aapsLogger.debug(LTag.PUMPCOMM, "<<<<< error during send message $reason") - currentState.onSendMessageError(reason) + currentState.onSendMessageError(reason, isRetryAble) } /** Service stuff */ @@ -822,10 +822,10 @@ class MedtrumService : DaggerService(), BLECommCallback { return responseSuccess } - fun onSendMessageError(reason: String) { + fun onSendMessageError(reason: String, isRetryAble: Boolean) { aapsLogger.warn(LTag.PUMPCOMM, "onSendMessageError: " + this.toString() + "reason: $reason") // Retry 3 times - if (sendRetryCounter < 3) { + if (sendRetryCounter < 3 && isRetryAble) { sendRetryCounter++ mPacket?.getRequest()?.let { bleComm.sendMessage(it) } } else { diff --git a/pump/medtrum/src/main/java/info/nightscout/pump/medtrum/util/CrcUtil.kt b/pump/medtrum/src/main/java/info/nightscout/pump/medtrum/util/CrcUtil.kt new file mode 100644 index 0000000000..211f35da72 --- /dev/null +++ b/pump/medtrum/src/main/java/info/nightscout/pump/medtrum/util/CrcUtil.kt @@ -0,0 +1,13 @@ +object CrcUtils { + + private val lookupTable: UByteArray = ubyteArrayOf(0u, 155u, 173u, 54u, 193u, 90u, 108u, 247u, 25u, 130u, 180u, 47u, 216u, 67u, 117u, 238u, 50u, 169u, 159u, 4u, 243u, 104u, 94u, 197u, 43u, 176u, 134u, 29u, 234u, 113u, 71u, 220u, 100u, 255u, 201u, 82u, 165u, 62u, 8u, 147u, 125u, 230u, 208u, 75u, 188u, 39u, 17u, 138u, 86u, 205u, 251u, 96u, 151u, 12u, 58u, 161u, 79u, 212u, 226u, 121u, 142u, 21u, 35u, 184u, 200u, 83u, 101u, 254u, 9u, 146u, 164u, 63u, 209u, 74u, 124u, 231u, 16u, 139u, 189u, 38u, 250u, 97u, 87u, 204u, 59u, 160u, 150u, 13u, 227u, 120u, 78u, 213u, 34u, 185u, 143u, 20u, 172u, 55u, 1u, 154u, 109u, 246u, 192u, 91u, 181u, 46u, 24u, 131u, 116u, 239u, 217u, 66u, 158u, 5u, 51u, 168u, 95u, 196u, 242u, 105u, 135u, 28u, 42u, 177u, 70u, 221u, 235u, 112u, 11u, 144u, 166u, 61u, 202u, 81u, 103u, 252u, 18u, 137u, 191u, 36u, 211u, 72u, 126u, 229u, 57u, 162u, 148u, 15u, 248u, 99u, 85u, 206u, 32u, 187u, 141u, 22u, 225u, 122u, 76u, 215u, 111u, 244u, 194u, 89u, 174u, 53u, 3u, 152u, 118u, 237u, 219u, 64u, 183u, 44u, 26u, 129u, 93u, 198u, 240u, 107u, 156u, 7u, 49u, 170u, 68u, 223u, 233u, 114u, 133u, 30u, 40u, 179u, 195u, 88u, 110u, 245u, 2u, 153u, 175u, 52u, 218u, 65u, 119u, 236u, 27u, 128u, 182u, 45u, 241u, 106u, 92u, 199u, 48u, 171u, 157u, 6u, 232u, 115u, 69u, 222u, 41u, 178u, 132u, 31u, 167u, 60u, 10u, 145u, 102u, 253u, 203u, 80u, 190u, 37u, 19u, 136u, 127u, 228u, 210u, 73u, 149u, 14u, 56u, 163u, 84u, 207u, 249u, 98u, 140u, 23u, 33u, 186u, 77u, 214u, 224u, 123u) + + fun calcCrc8(value: ByteArray, size: Int): Byte { + var crc8: UByte = 0u + for (i in 0 until size) { + val tableIndex: UByte = (value[i].toUByte() xor crc8) + crc8 = lookupTable[tableIndex.toInt()] + } + return crc8.toByte() + } +} diff --git a/pump/medtrum/src/main/res/values/strings.xml b/pump/medtrum/src/main/res/values/strings.xml index d4f4b4685f..01bbe48505 100644 --- a/pump/medtrum/src/main/res/values/strings.xml +++ b/pump/medtrum/src/main/res/values/strings.xml @@ -80,6 +80,7 @@ Battery out No calibration Failed to update pump timezone, snooze message and refresh manually. + Bolus error Retry diff --git a/pump/medtrum/src/test/java/info/nightscout/pump/medtrum/comm/ReadDataPacketTest.kt b/pump/medtrum/src/test/java/info/nightscout/pump/medtrum/comm/ReadDataPacketTest.kt new file mode 100644 index 0000000000..9b61869cff --- /dev/null +++ b/pump/medtrum/src/test/java/info/nightscout/pump/medtrum/comm/ReadDataPacketTest.kt @@ -0,0 +1,154 @@ +package info.nightscout.pump.medtrum.comm + +import com.google.common.truth.Truth.assertThat +import org.junit.jupiter.api.Test + +class ReadDataPacketTest { + + @Test + fun givenCorrectBytesExpectPacketNotFailed() { + // arrange + val chunk1 = byteArrayOf(51, 99, 10, 1, 0, 0, -86, 44, 1, -1, -85, 21, -108, -62, 1, 0, 22, 0, 1, 75) + val chunk2 = byteArrayOf(51, 99, 10, 2, 0, 0, 0, -80, -116, 84, 18, 10, 0, 10, 0, 0, 0, 0, 0, -10) + val chunk3 = byteArrayOf(51, 99, 10, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -61, -59, -120, 5) + val chunk4 = byteArrayOf(51, 99, 10, 4, 19, -82, -80) + + // act + val packet = ReadDataPacket(chunk1) + packet.addData(chunk2) + packet.addData(chunk3) + packet.addData(chunk4) + + // assert + assertThat(packet.allDataReceived()).isTrue() + assertThat(packet.getData()).isEqualTo( + byteArrayOf( + 51, 99, 10, 1, 0, 0, -86, 44, 1, -1, -85, 21, -108, -62, 1, 0, 22, 0, 1, 0, 0, 0, -80, + -116, 84, 18, 10, 0, 10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -61, -59, -120, 19, -82 + ) + ) + assertThat(packet.failed()).isFalse() + } + + @Test + fun givenIncorrectCRCInFirstChunkExpectPacketFailed() { + // arrange + val chunk1 = byteArrayOf(51, 99, 10, 1, 0, 0, -86, 44, 1, -1, -85, 21, -1, -62, -1, -1, 22, 0, 1, 75) + val chunk2 = byteArrayOf(51, 99, 10, 2, 0, 0, 0, -80, -116, 84, 18, 10, 0, 10, 0, 0, 0, 0, 0, -10) + val chunk3 = byteArrayOf(51, 99, 10, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -61, -59, -120, 5) + val chunk4 = byteArrayOf(51, 99, 10, 4, 19, -82, -80) + + // act + val packet = ReadDataPacket(chunk1) + packet.addData(chunk2) + packet.addData(chunk3) + packet.addData(chunk4) + + // assert + assertThat(packet.allDataReceived()).isTrue() + assertThat(packet.failed()).isTrue() + } + + @Test + fun givenIncorrectCRCInSecondChunkExpectPacketFailed() { + // arrange + val chunk1 = byteArrayOf(51, 99, 10, 1, 0, 0, -86, 44, 1, -1, -85, 21, -108, -62, 1, 0, 22, 0, 1, 75) + val chunk2 = byteArrayOf(51, 99, 10, 2, 0, 0, 0, -80, -1, 84, 18, 10, 0, 10, -1, -1, 0, 0, 0, -10) + val chunk3 = byteArrayOf(51, 99, 10, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -61, -59, -120, 5) + val chunk4 = byteArrayOf(51, 99, 10, 4, 19, -82, -80) + + // act + val packet = ReadDataPacket(chunk1) + packet.addData(chunk2) + packet.addData(chunk3) + packet.addData(chunk4) + + // assert + assertThat(packet.allDataReceived()).isTrue() + assertThat(packet.failed()).isTrue() + } + + @Test + fun givenIncorrectCRCInThirdChunkExpectPacketFailed() { + // arrange + val chunk1 = byteArrayOf(51, 99, 10, 1, 0, 0, -86, 44, 1, -1, -85, 21, -108, -62, 1, 0, 22, 0, 1, 75) + val chunk2 = byteArrayOf(51, 99, 10, 2, 0, 0, 0, -80, -116, 84, 18, 10, 0, 10, 0, 0, 0, 0, 0, -10) + val chunk3 = byteArrayOf(51, 99, 10, 3, 0, 0, 0, 0, 0, 0, 0, -1, -1, -1, -1, -1, -61, -59, -120, 5) + val chunk4 = byteArrayOf(51, 99, 10, 4, 19, -82, -80) + + // act + val packet = ReadDataPacket(chunk1) + packet.addData(chunk2) + packet.addData(chunk3) + packet.addData(chunk4) + + // assert + assertThat(packet.allDataReceived()).isTrue() + assertThat(packet.failed()).isTrue() + } + + @Test + fun givenIncorrectCRCInLastChunkExpectPacketFailed() { + // arrange + val chunk1 = byteArrayOf(51, 99, 10, 1, 0, 0, -86, 44, 1, -1, -85, 21, -108, -62, 1, 0, 22, 0, 1, 75) + val chunk2 = byteArrayOf(51, 99, 10, 2, 0, 0, 0, -80, -116, 84, 18, 10, 0, 10, 0, 0, 0, 0, 0, -10) + val chunk3 = byteArrayOf(51, 99, 10, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -61, -59, -120, 5) + val chunk4 = byteArrayOf(51, 99, 10, -1, -1, -82, -80) + + // act + val packet = ReadDataPacket(chunk1) + packet.addData(chunk2) + packet.addData(chunk3) + packet.addData(chunk4) + + // assert + assertThat(packet.allDataReceived()).isTrue() + assertThat(packet.failed()).isTrue() + } + + @Test + fun givenIncorrectSequenceExpectPacketFailed() { + // arrange + val chunk1 = byteArrayOf(51, 99, 10, 1, 0, 0, -86, 44, 1, -1, -85, 21, -108, -62, 1, 0, 22, 0, 1, 75) + val chunk2 = byteArrayOf(51, 99, 10, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -61, -59, -120, 5) + val chunk3 = byteArrayOf(51, 99, 10, 2, 0, 0, 0, -80, -116, 84, 18, 10, 0, 10, 0, 0, 0, 0, 0, -10) + val chunk4 = byteArrayOf(51, 99, 10, 4, 19, -82, -80) + + // act + val packet = ReadDataPacket(chunk1) + packet.addData(chunk2) + packet.addData(chunk3) + packet.addData(chunk4) + + // assert + assertThat(packet.allDataReceived()).isTrue() + assertThat(packet.failed()).isTrue() + } + + @Test + fun givenCorrectBytesOneChunkExpectPacketNotFailed() { + // arrange + val chunk1 = byteArrayOf(14, 5, 0, 0, 0, 0, 2, 80, 1, 74, 64, 4, 0, -16, 0) + + // act + val packet = ReadDataPacket(chunk1) + + // assert + assertThat(packet.allDataReceived()).isTrue() + assertThat(packet.getData()).isEqualTo(byteArrayOf(14, 5, 0, 0, 0, 0, 2, 80, 1, 74, 64, 4, 0, -16)) + assertThat(packet.failed()).isFalse() + } + + @Test + fun givenIncorrectBytesOneChunkExpectPacketFailed() { + // arrange + val chunk1 = byteArrayOf(14, 5, 0, -1, -1, -1, 2, 80, 1, 74, 64, 4, 0, -16, 0) + + // act + val packet = ReadDataPacket(chunk1) + + // assert + assertThat(packet.allDataReceived()).isTrue() + assertThat(packet.failed()).isTrue() + } +}