diff --git a/build.gradle b/build.gradle
index 062178e7fd..a1c8bd34cb 100644
--- a/build.gradle
+++ b/build.gradle
@@ -25,7 +25,7 @@ buildscript {
preferencektx_version = '1.2.1'
commonslang3_version = '3.13.0'
commonscodec_version = '1.16.0'
- guava_version = '32.1.2-jre'
+ guava_version = '32.1.3-jre'
jodatime_version = '2.12.5'
work_version = '2.8.1'
tink_version = '1.10.0'
@@ -80,7 +80,7 @@ buildscript {
plugins {
// Test Gradle build, keep disabled under normal circumstances
// id "com.osacky.doctor" version "0.8.1"
- id "org.jlleitschuh.gradle.ktlint" version "11.6.0"
+ id "org.jlleitschuh.gradle.ktlint" version "11.6.1"
// Aggregates and/or logs Jacoco test coverage to the Gradle build log
//id 'org.barfuin.gradle.jacocolog' version '3.1.0'
id 'org.jetbrains.kotlin.android' version "$kotlin_version" apply false
diff --git a/core/ui/src/main/res/values-nb-rNO/strings.xml b/core/ui/src/main/res/values-nb-rNO/strings.xml
index 1612d72d0a..e2d717ae04 100644
--- a/core/ui/src/main/res/values-nb-rNO/strings.xml
+++ b/core/ui/src/main/res/values-nb-rNO/strings.xml
@@ -155,7 +155,7 @@
Login
Prime/fylling
Insulin
- Avbryt midlertidig målverdi
+ Avbryt midlertidig mål
Lukket Loop
Åpen Loop
Stopp ved lavt BS
diff --git a/plugins/source/src/main/res/values-nb-rNO/strings.xml b/plugins/source/src/main/res/values-nb-rNO/strings.xml
index 1d8deba163..7c3822f36a 100644
--- a/plugins/source/src/main/res/values-nb-rNO/strings.xml
+++ b/plugins/source/src/main/res/values-nb-rNO/strings.xml
@@ -34,6 +34,5 @@
I xDrip+, velg 640G/Eversens som datakilde
Innstillinger for opplasting av BS
Logg sensorbytte til NS
- Opprett hendelse \"Sensor bytte\" automatisk i NS ved start av sensoren
retning
diff --git a/pump/medtrum/src/main/java/info/nightscout/pump/medtrum/MedtrumPlugin.kt b/pump/medtrum/src/main/java/info/nightscout/pump/medtrum/MedtrumPlugin.kt
index e40762b526..2adb563031 100644
--- a/pump/medtrum/src/main/java/info/nightscout/pump/medtrum/MedtrumPlugin.kt
+++ b/pump/medtrum/src/main/java/info/nightscout/pump/medtrum/MedtrumPlugin.kt
@@ -11,6 +11,7 @@ import android.text.format.DateFormat
import androidx.preference.EditTextPreference
import androidx.preference.ListPreference
import androidx.preference.PreferenceFragmentCompat
+import androidx.preference.SwitchPreference
import app.aaps.core.interfaces.constraints.ConstraintsChecker
import app.aaps.core.interfaces.logging.AAPSLogger
import app.aaps.core.interfaces.logging.LTag
@@ -39,6 +40,7 @@ import app.aaps.core.interfaces.rx.bus.RxBus
import app.aaps.core.interfaces.rx.events.EventAppExit
import app.aaps.core.interfaces.rx.events.EventDismissNotification
import app.aaps.core.interfaces.rx.events.EventOverviewBolusProgress
+import app.aaps.core.interfaces.sharedPreferences.SP
import app.aaps.core.interfaces.ui.UiInteraction
import app.aaps.core.interfaces.utils.DateUtil
import app.aaps.core.interfaces.utils.DecimalFormatter
@@ -67,6 +69,7 @@ import kotlin.math.abs
aapsLogger: AAPSLogger,
rh: ResourceHelper,
commandQueue: CommandQueue,
+ private val sp: SP,
private val constraintChecker: ConstraintsChecker,
private val aapsSchedulers: AapsSchedulers,
private val rxBus: RxBus,
@@ -102,6 +105,9 @@ import kotlin.math.abs
.toObservable(EventAppExit::class.java)
.observeOn(aapsSchedulers.io)
.subscribe({ context.unbindService(mConnection) }, fabricPrivacy::logException)
+
+ // Force enable pump unreachable alert due to some failure modes of Medtrum pump
+ sp.putBoolean(app.aaps.core.utils.R.string.key_enable_pump_unreachable_alert, true)
}
override fun onStop() {
@@ -134,6 +140,7 @@ import kotlin.math.abs
preprocessSerialSettings(preferenceFragment)
preprocessAlarmSettings(preferenceFragment)
preprocessMaxInsulinSettings(preferenceFragment)
+ preprocessConnectionAlertSettings(preferenceFragment)
}
private fun preprocessSerialSettings(preferenceFragment: PreferenceFragmentCompat) {
@@ -241,6 +248,21 @@ import kotlin.math.abs
}
}
+ private fun preprocessConnectionAlertSettings(preferenceFragment: PreferenceFragmentCompat) {
+ val unreachableAlertSetting = preferenceFragment.findPreference(rh.gs(app.aaps.core.utils.R.string.key_enable_pump_unreachable_alert))
+ val unreachableThresholdSetting = preferenceFragment.findPreference(rh.gs(app.aaps.core.utils.R.string.key_pump_unreachable_threshold_minutes))
+
+ unreachableAlertSetting?.apply {
+ isSelectable = false
+ summary = rh.gs(R.string.enable_pump_unreachable_alert_summary)
+ }
+
+ unreachableThresholdSetting?.apply {
+ val currentValue = text
+ summary = "${rh.gs(R.string.pump_unreachable_threshold_minutes_summary)}\n${currentValue}"
+ }
+ }
+
override fun isInitialized(): Boolean {
return medtrumPump.pumpState > MedtrumPumpState.EJECTED && medtrumPump.pumpState < MedtrumPumpState.STOPPED
}
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 32ed8e39d6..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
@@ -53,14 +53,53 @@ class NotificationPacket(val injector: HasAndroidInjector) {
private const val MASK_STORAGE = 0x100
private const val MASK_ALARM = 0x200
private const val MASK_AGE = 0x400
- private const val MASK_UNKNOWN_1 = 0x800
+ private const val MASK_MAGNETO_PLACE = 0x800
private const val MASK_UNUSED_CGM = 0x1000
private const val MASK_UNUSED_COMMAND_CONFIRM = 0x2000
private const val MASK_UNUSED_AUTO_STATUS = 0x4000
private const val MASK_UNUSED_LEGACY = 0x8000
+
+ private const val SIZE_FIELD_MASK = 2
+ private const val SIZE_SUSPEND = 4
+ private const val SIZE_NORMAL_BOLUS = 3
+ private const val SIZE_EXTENDED_BOLUS = 3
+ private const val SIZE_BASAL = 12
+ private const val SIZE_SETUP = 1
+ private const val SIZE_RESERVOIR = 2
+ private const val SIZE_START_TIME = 4
+ private const val SIZE_BATTERY = 3
+ private const val SIZE_STORAGE = 4
+ private const val SIZE_ALARM = 4
+ private const val SIZE_AGE = 4
+ private const val SIZE_MAGNETO_PLACE = 2
+ private const val SIZE_UNUSED_CGM = 5
+ private const val SIZE_UNUSED_COMMAND_CONFIRM = 2
+ private const val SIZE_UNUSED_AUTO_STATUS = 2
+ private const val SIZE_UNUSED_LEGACY = 2
}
+ val maskHandlers: Map Int> = mapOf(
+ MASK_SUSPEND to ::handleSuspend,
+ MASK_NORMAL_BOLUS to ::handleNormalBolus,
+ MASK_EXTENDED_BOLUS to ::handleExtendedBolus,
+ MASK_BASAL to ::handleBasal,
+ MASK_SETUP to ::handleSetup,
+ MASK_RESERVOIR to ::handleReservoir,
+ MASK_START_TIME to ::handleStartTime,
+ MASK_BATTERY to ::handleBattery,
+ MASK_STORAGE to ::handleStorage,
+ MASK_ALARM to ::handleAlarm,
+ MASK_AGE to ::handleAge,
+ MASK_MAGNETO_PLACE to ::handleUnknown1,
+ MASK_UNUSED_CGM to ::handleUnusedCGM,
+ MASK_UNUSED_COMMAND_CONFIRM to ::handleUnusedCommandConfirm,
+ MASK_UNUSED_AUTO_STATUS to ::handleUnusedAutoStatus,
+ MASK_UNUSED_LEGACY to ::handleUnusedLegacy
+ )
+
+ var newPatchStartTime = 0L
+
init {
injector.androidInjector().inject(this)
}
@@ -74,7 +113,7 @@ class NotificationPacket(val injector: HasAndroidInjector) {
medtrumPump.pumpState = state
}
- if (notification.size > NOTIF_STATE_END) {
+ if (notification.size > NOTIF_STATE_END + SIZE_FIELD_MASK) {
handleMaskedMessage(notification.copyOfRange(NOTIF_STATE_END, notification.size))
}
}
@@ -82,164 +121,219 @@ class NotificationPacket(val injector: HasAndroidInjector) {
/**
* Handle a message with a field mask, can be used by other packets as well
*/
- fun handleMaskedMessage(data: ByteArray) {
+ fun handleMaskedMessage(data: ByteArray): Boolean {
val fieldMask = data.copyOfRange(0, 2).toInt()
var offset = 2
- var newPatchStartTime: Long? = null
+
+ 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")
- if (fieldMask and MASK_SUSPEND != 0) {
- aapsLogger.debug(LTag.PUMPCOMM, "Suspend notification received")
- medtrumPump.suspendTime = MedtrumTimeUtil().convertPumpTimeToSystemTimeMillis(data.copyOfRange(offset, offset + 4).toLong())
- aapsLogger.debug(LTag.PUMPCOMM, "Suspend time: ${medtrumPump.suspendTime}")
- offset += 4
- }
-
- if (fieldMask and MASK_NORMAL_BOLUS != 0) {
- aapsLogger.debug(LTag.PUMPCOMM, "Normal bolus notification received")
- val bolusData = data.copyOfRange(offset, offset + 1).toInt()
- val bolusType = bolusData and 0x7F
- val bolusCompleted: Boolean = ((bolusData shr 7) and 0x01) != 0
- val bolusDelivered = data.copyOfRange(offset + 1, offset + 3).toInt() * 0.05
- aapsLogger.debug(LTag.PUMPCOMM, "Bolus type: $bolusType, bolusData: $bolusData bolus completed: $bolusCompleted, bolus delivered: $bolusDelivered")
- medtrumPump.handleBolusStatusUpdate(bolusType, bolusCompleted, bolusDelivered)
- offset += 3
- }
-
- if (fieldMask and MASK_EXTENDED_BOLUS != 0) {
- aapsLogger.error(LTag.PUMPCOMM, "Extended bolus notification received, extended bolus not supported!")
- offset += 3
- }
-
- if (fieldMask and MASK_BASAL != 0) {
- aapsLogger.debug(LTag.PUMPCOMM, "Basal notification received")
- val basalType = enumValues()[data.copyOfRange(offset, offset + 1).toInt()]
- val basalSequence = data.copyOfRange(offset + 1, offset + 3).toInt()
- val basalPatchId = data.copyOfRange(offset + 3, offset + 5).toLong()
- val basalStartTime = MedtrumTimeUtil().convertPumpTimeToSystemTimeMillis(data.copyOfRange(offset + 5, offset + 9).toLong())
- val basalRateAndDelivery = data.copyOfRange(offset + 9, offset + 12).toInt()
- val basalRate = (basalRateAndDelivery and 0xFFF) * 0.05
- val basalDelivery = (basalRateAndDelivery shr 12) * 0.05
- aapsLogger.debug(
- LTag.PUMPCOMM,
- "Basal type: $basalType, basal sequence: $basalSequence, basal patch id: $basalPatchId, basal time: $basalStartTime, basal rate: $basalRate, basal delivery: $basalDelivery"
- )
- // Don't spam with basal updates here, only if the running basal rate has changed, or a new basal is set
- if (medtrumPump.lastBasalRate != basalRate || medtrumPump.lastBasalStartTime != basalStartTime) {
- medtrumPump.handleBasalStatusUpdate(basalType, basalRate, basalSequence, basalPatchId, basalStartTime)
+ for ((mask, handler) in maskHandlers) {
+ if (fieldMask and mask != 0) {
+ offset = handler(data, offset)
}
- offset += 12
}
- if (fieldMask and MASK_SETUP != 0) {
- aapsLogger.debug(LTag.PUMPCOMM, "Setup notification received")
- medtrumPump.primeProgress = data.copyOfRange(offset, offset + 1).toInt()
- aapsLogger.debug(LTag.PUMPCOMM, "Prime progress: ${medtrumPump.primeProgress}")
- offset += 1
- }
+ return true
+ }
- if (fieldMask and MASK_RESERVOIR != 0) {
- aapsLogger.debug(LTag.PUMPCOMM, "Reservoir notification received")
- medtrumPump.reservoir = data.copyOfRange(offset, offset + 2).toInt() * 0.05
- aapsLogger.debug(LTag.PUMPCOMM, "Reservoir: ${medtrumPump.reservoir}")
- offset += 2
- }
+ private fun calculateExpectedLengthBasedOnFieldMask(fieldMask: Int): Int {
+ var expectedLength = SIZE_FIELD_MASK
- if (fieldMask and MASK_START_TIME != 0) {
- aapsLogger.debug(LTag.PUMPCOMM, "Start time notification received")
- newPatchStartTime = MedtrumTimeUtil().convertPumpTimeToSystemTimeMillis(data.copyOfRange(offset, offset + 4).toLong())
- if (medtrumPump.patchStartTime != newPatchStartTime) {
- aapsLogger.debug(LTag.PUMPCOMM, "Patch start time changed from ${medtrumPump.patchStartTime} to $newPatchStartTime")
- medtrumPump.patchStartTime = newPatchStartTime
+ val sizeMap = mapOf(
+ MASK_SUSPEND to SIZE_SUSPEND,
+ MASK_NORMAL_BOLUS to SIZE_NORMAL_BOLUS,
+ MASK_EXTENDED_BOLUS to SIZE_EXTENDED_BOLUS,
+ MASK_BASAL to SIZE_BASAL,
+ MASK_SETUP to SIZE_SETUP,
+ MASK_RESERVOIR to SIZE_RESERVOIR,
+ MASK_START_TIME to SIZE_START_TIME,
+ MASK_BATTERY to SIZE_BATTERY,
+ MASK_STORAGE to SIZE_STORAGE,
+ MASK_ALARM to SIZE_ALARM,
+ MASK_AGE to SIZE_AGE,
+ MASK_MAGNETO_PLACE to SIZE_MAGNETO_PLACE,
+ MASK_UNUSED_CGM to SIZE_UNUSED_CGM,
+ MASK_UNUSED_COMMAND_CONFIRM to SIZE_UNUSED_COMMAND_CONFIRM,
+ 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
}
- aapsLogger.debug(LTag.PUMPCOMM, "Patch start time: $newPatchStartTime")
- offset += 4
}
- if (fieldMask and MASK_BATTERY != 0) {
- aapsLogger.debug(LTag.PUMPCOMM, "Battery notification received")
- val parameter = data.copyOfRange(offset, offset + 3).toInt()
- // Precision for voltage A is a guess, voltage B is the important one, threshold: < 2.64
- medtrumPump.batteryVoltage_A = (parameter and 0xFFF) / 512.0
- medtrumPump.batteryVoltage_B = (parameter shr 12) / 512.0
- aapsLogger.debug(LTag.PUMPCOMM, "Battery voltage A: ${medtrumPump.batteryVoltage_A}, battery voltage B: ${medtrumPump.batteryVoltage_B}")
- offset += 3
- }
+ return expectedLength
+ }
- if (fieldMask and MASK_STORAGE != 0) {
- aapsLogger.debug(LTag.PUMPCOMM, "Storage notification received")
- val sequence = data.copyOfRange(offset, offset + 2).toInt()
- if (sequence > medtrumPump.currentSequenceNumber) {
- medtrumPump.currentSequenceNumber = sequence
+ private fun handleSuspend(data: ByteArray, offset: Int): Int {
+ aapsLogger.debug(LTag.PUMPCOMM, "Suspend notification received")
+ medtrumPump.suspendTime = MedtrumTimeUtil().convertPumpTimeToSystemTimeMillis(data.copyOfRange(offset, offset + 4).toLong())
+ 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()
+ val bolusType = bolusData and 0x7F
+ val bolusCompleted: Boolean = ((bolusData shr 7) and 0x01) != 0
+ val bolusDelivered = data.copyOfRange(offset + 1, offset + 3).toInt() * 0.05
+ aapsLogger.debug(LTag.PUMPCOMM, "Bolus type: $bolusType, bolusData: $bolusData bolus completed: $bolusCompleted, bolus delivered: $bolusDelivered")
+ 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()]
+ val basalSequence = data.copyOfRange(offset + 1, offset + 3).toInt()
+ val basalPatchId = data.copyOfRange(offset + 3, offset + 5).toLong()
+ val basalStartTime = MedtrumTimeUtil().convertPumpTimeToSystemTimeMillis(data.copyOfRange(offset + 5, offset + 9).toLong())
+ val basalRateAndDelivery = data.copyOfRange(offset + 9, offset + 12).toInt()
+ val basalRate = (basalRateAndDelivery and 0xFFF) * 0.05
+ val basalDelivery = (basalRateAndDelivery shr 12) * 0.05
+ aapsLogger.debug(
+ LTag.PUMPCOMM,
+ "Basal type: $basalType, basal sequence: $basalSequence, basal patch id: $basalPatchId, basal time: $basalStartTime, basal rate: $basalRate, basal delivery: $basalDelivery"
+ )
+ // Don't spam with basal updates here, only if the running basal rate has changed, or a new basal is set
+ if (medtrumPump.lastBasalRate != basalRate || medtrumPump.lastBasalStartTime != basalStartTime) {
+ medtrumPump.handleBasalStatusUpdate(basalType, basalRate, basalSequence, basalPatchId, basalStartTime)
+ }
+ 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())
+ if (medtrumPump.patchStartTime != newPatchStartTime) {
+ aapsLogger.debug(LTag.PUMPCOMM, "Patch start time changed from ${medtrumPump.patchStartTime} to $newPatchStartTime")
+ medtrumPump.patchStartTime = newPatchStartTime
+ }
+ 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()
+ // Precision for voltage A is a guess, voltage B is the important one, threshold: < 2.64
+ medtrumPump.batteryVoltage_A = (parameter and 0xFFF) / 512.0
+ medtrumPump.batteryVoltage_B = (parameter shr 12) / 512.0
+ 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()
+ if (sequence > medtrumPump.currentSequenceNumber) {
+ medtrumPump.currentSequenceNumber = sequence
+ }
+ val patchId = data.copyOfRange(offset + 2, offset + 4).toLong()
+ if (patchId != medtrumPump.patchId) {
+ aapsLogger.warn(LTag.PUMPCOMM, "handleMaskedMessage: We got wrong patch id!")
+ if (newPatchStartTime != 0L) {
+ // This is a fallback for when the activate packet did not receive the ack but the patch activated anyway
+ aapsLogger.error(LTag.PUMPCOMM, "handleMaskedMessage: Also Received start time in this packet, registering new patch id: $patchId")
+ medtrumPump.handleNewPatch(patchId, sequence, newPatchStartTime)
}
- val patchId = data.copyOfRange(offset + 2, offset + 4).toLong()
- if (patchId != medtrumPump.patchId) {
- aapsLogger.warn(LTag.PUMPCOMM, "handleMaskedMessage: We got wrong patch id!")
- if (newPatchStartTime != null) {
- // This is a fallback for when the activate packet did not receive the ack but the patch activated anyway
- aapsLogger.error(LTag.PUMPCOMM, "handleMaskedMessage: Also Received start time in this packet, registering new patch id: $patchId")
- medtrumPump.handleNewPatch(patchId, sequence, newPatchStartTime)
- }
- }
- aapsLogger.debug(LTag.PUMPCOMM, "Last known sequence number: ${medtrumPump.currentSequenceNumber}, patch id: ${patchId}")
- offset += 4
}
+ aapsLogger.debug(LTag.PUMPCOMM, "Last known sequence number: ${medtrumPump.currentSequenceNumber}, patch id: ${patchId}")
+ return offset + SIZE_STORAGE
+ }
- if (fieldMask and MASK_ALARM != 0) {
- val alarmFlags = data.copyOfRange(offset, offset + 2).toInt()
- val alarmParameter = data.copyOfRange(offset + 2, offset + 4).toInt()
- aapsLogger.debug(LTag.PUMPCOMM, "Alarm notification received, Alarm flags: $alarmFlags, alarm parameter: $alarmParameter")
+ 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()
+ aapsLogger.debug(LTag.PUMPCOMM, "Alarm notification received, Alarm flags: $alarmFlags, alarm parameter: $alarmParameter")
- // If no alarm, clear activeAlarm list
- if (alarmFlags == 0 && medtrumPump.activeAlarms.isNotEmpty()) {
- medtrumPump.clearAlarmState()
- } else if (alarmFlags != 0) {
- // Check each alarm bit
- for (i in 0..3) { // Only the first 3 flags are interesting for us, the rest we will get from the pump state
- val alarmState = AlarmState.values()[i]
- if ((alarmFlags shr i) and 1 != 0) {
- // If the alarm bit is set, add the corresponding alarm to activeAlarms
- if (!medtrumPump.activeAlarms.contains(alarmState)) {
- aapsLogger.debug(LTag.PUMPCOMM, "Adding alarm $alarmState to active alarms")
- medtrumPump.addAlarm(alarmState)
- medtrumPump.pumpWarning = alarmState
- }
- } else if (medtrumPump.activeAlarms.contains(alarmState)) {
- // If the alarm bit is not set, and the corresponding alarm is in activeAlarms, remove it
- medtrumPump.removeAlarm(alarmState)
+ // If no alarm, clear activeAlarm list
+ if (alarmFlags == 0 && medtrumPump.activeAlarms.isNotEmpty()) {
+ medtrumPump.clearAlarmState()
+ } else if (alarmFlags != 0) {
+ // Check each alarm bit
+ for (i in 0..3) { // Only the first 3 flags are interesting for us, the rest we will get from the pump state
+ val alarmState = AlarmState.values()[i]
+ if ((alarmFlags shr i) and 1 != 0) {
+ // If the alarm bit is set, add the corresponding alarm to activeAlarms
+ if (!medtrumPump.activeAlarms.contains(alarmState)) {
+ aapsLogger.debug(LTag.PUMPCOMM, "Adding alarm $alarmState to active alarms")
+ medtrumPump.addAlarm(alarmState)
+ medtrumPump.pumpWarning = alarmState
}
+ } else if (medtrumPump.activeAlarms.contains(alarmState)) {
+ // If the alarm bit is not set, and the corresponding alarm is in activeAlarms, remove it
+ medtrumPump.removeAlarm(alarmState)
}
}
- offset += 4
}
+ return offset + SIZE_ALARM
+ }
- if (fieldMask and MASK_AGE != 0) {
- aapsLogger.debug(LTag.PUMPCOMM, "Age notification received")
- medtrumPump.patchAge = data.copyOfRange(offset, offset + 4).toLong()
- aapsLogger.debug(LTag.PUMPCOMM, "Patch age: ${medtrumPump.patchAge}")
- offset += 4
- }
+ 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
+ }
- if (fieldMask and MASK_UNKNOWN_1 != 0) {
- aapsLogger.debug(LTag.PUMPCOMM, "Unknown 1 notification received, not handled!")
- }
+ 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
+ }
- if (fieldMask and MASK_UNUSED_CGM != 0) {
- aapsLogger.debug(LTag.PUMPCOMM, "Unused CGM notification received, not handled!")
- }
+ 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
+ }
- if (fieldMask and MASK_UNUSED_COMMAND_CONFIRM != 0) {
- // This one is a warning, as this happens we need to know about it, and maybe implement
- aapsLogger.warn(LTag.PUMPCOMM, "Unused command confirm notification received, not handled!")
- }
+ 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
+ }
- if (fieldMask and MASK_UNUSED_AUTO_STATUS != 0) {
- aapsLogger.debug(LTag.PUMPCOMM, "Unused auto status notification received, not handled!")
- }
+ 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
+ }
- if (fieldMask and MASK_UNUSED_LEGACY != 0) {
- aapsLogger.debug(LTag.PUMPCOMM, "Unused legacy notification received, not handled!")
- }
+ 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()}")
+ return offset + SIZE_UNUSED_LEGACY
}
}
diff --git a/pump/medtrum/src/main/java/info/nightscout/pump/medtrum/comm/packets/SynchronizePacket.kt b/pump/medtrum/src/main/java/info/nightscout/pump/medtrum/comm/packets/SynchronizePacket.kt
index 97e8d15222..8d35793052 100644
--- a/pump/medtrum/src/main/java/info/nightscout/pump/medtrum/comm/packets/SynchronizePacket.kt
+++ b/pump/medtrum/src/main/java/info/nightscout/pump/medtrum/comm/packets/SynchronizePacket.kt
@@ -31,7 +31,7 @@ class SynchronizePacket(injector: HasAndroidInjector) : MedtrumPacket(injector)
}
override fun handleResponse(data: ByteArray): Boolean {
- val success = super.handleResponse(data)
+ var success = super.handleResponse(data)
if (success) {
val state = MedtrumPumpState.fromByte(data[RESP_STATE_START])
@@ -63,7 +63,7 @@ class SynchronizePacket(injector: HasAndroidInjector) : MedtrumPacket(injector)
}
// Let the notification packet handle the rest of the sync data
- NotificationPacket(injector).handleMaskedMessage(fieldMask.toByteArray(2) + syncData)
+ success = NotificationPacket(injector).handleMaskedMessage(fieldMask.toByteArray(2) + syncData)
}
return success
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..ae596414e9 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
@@ -336,21 +336,22 @@ class MedtrumService : DaggerService(), BLECommCallback {
if (!canSetBolus()) return false
val insulin = detailedBolusInfo.insulin
+ medtrumPump.bolusDone = false
+ medtrumPump.bolusStopped = false
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
+ medtrumPump.bolusDone = true
t.insulin = 0.0
return false
}
val bolusStart = System.currentTimeMillis()
- medtrumPump.bolusDone = false
- medtrumPump.bolusingTreatment = t
- medtrumPump.bolusAmountToBeDelivered = insulin
- medtrumPump.bolusStopped = false
medtrumPump.bolusProgressLastTimeStamp = bolusStart
medtrumPump.bolusStartTime = bolusStart
+ medtrumPump.bolusingTreatment = t
+ medtrumPump.bolusAmountToBeDelivered = insulin
detailedBolusInfo.timestamp = bolusStart // Make sure the timestamp is set to the start of the bolus
detailedBolusInfoStorage.add(detailedBolusInfo) // will be picked up on reading history
@@ -735,9 +736,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 +823,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 b508b7150d..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
@@ -134,6 +135,8 @@
Please wait, reading activation status from pump.
+ Unreachable alert forced enabled, because Medtrum patch can fail and be unreachable.
+ Advised to set to 30 minutes, because Medtrum patch can fail and be unreachable.
Serial Number
Enter the serial number of your pump base.
Invalid serial number!
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()
+ }
+}
diff --git a/pump/medtrum/src/test/java/info/nightscout/pump/medtrum/comm/packets/NotificationPacketTest.kt b/pump/medtrum/src/test/java/info/nightscout/pump/medtrum/comm/packets/NotificationPacketTest.kt
index 90664ee302..106d0aa892 100644
--- a/pump/medtrum/src/test/java/info/nightscout/pump/medtrum/comm/packets/NotificationPacketTest.kt
+++ b/pump/medtrum/src/test/java/info/nightscout/pump/medtrum/comm/packets/NotificationPacketTest.kt
@@ -87,4 +87,17 @@ class NotificationPacketTest : MedtrumTestBase() {
assertThat(medtrumPump.bolusingTreatment!!.insulin).isWithin(0.01).of(1.65)
assertThat(medtrumPump.reservoir).isWithin(0.01).of(161.95)
}
+
+ @Test fun handleNotificationGivenFieldMaskButMessageTooShortThenNothingSaved() {
+ // Inputs
+ val data = byteArrayOf(67, 41, 67, -1, 122, 95, 18, 0, 73, 1, 19, 0, 1, 0, 20, 0, 0, 0, 0, 16)
+
+ // Call
+ NotificationPacket(packetInjector).handleNotification(data)
+
+ // Expected values
+ assertThat(medtrumPump.suspendTime).isEqualTo(0)
+ assertThat(medtrumPump.lastBasalStartTime).isEqualTo(0)
+ assertThat(medtrumPump.currentSequenceNumber).isEqualTo(0)
+ }
}