From af6445a3dc699c9591786408bcdb71ff36190a01 Mon Sep 17 00:00:00 2001 From: jbr7rr <> Date: Tue, 28 Mar 2023 19:26:58 +0200 Subject: [PATCH] NotificationPacket --- .../nightscout/pump/medtrum/MedtrumPlugin.kt | 8 +- .../nightscout/pump/medtrum/MedtrumPump.kt | 36 +-- .../medtrum/comm/enums/MedtrumPumpState.kt | 9 +- .../medtrum/comm/packets/ActivatePacket.kt | 7 +- .../medtrum/comm/packets/GetTimePacket.kt | 8 +- .../comm/packets/NotificationPacket.kt | 206 ++++++++++++++++++ .../comm/packets/ReadBolusStatePacket.kt | 1 + .../medtrum/comm/packets/SynchronizePacket.kt | 46 +++- .../pump/medtrum/di/MedtrumCommModule.kt | 4 + .../pump/medtrum/services/BLEComm.kt | 21 +- .../pump/medtrum/services/MedtrumService.kt | 35 ++- .../medtrum/comm/packets/GetTimePacketTest.kt | 8 +- .../medtrum/comm/packets/MedtrumPacketTest.kt | 2 +- .../comm/packets/NotificationPacketTest.kt | 39 ++++ .../comm/packets/SynchronizePacketTest.kt | 18 +- 15 files changed, 396 insertions(+), 52 deletions(-) create mode 100644 pump/medtrum/src/main/java/info/nightscout/pump/medtrum/comm/packets/NotificationPacket.kt create mode 100644 pump/medtrum/src/test/java/info/nightscout/pump/medtrum/comm/packets/NotificationPacketTest.kt 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 d0c319acb8..e264320ba5 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 @@ -26,6 +26,7 @@ import info.nightscout.interfaces.queue.CommandQueue import info.nightscout.interfaces.queue.CustomCommand import info.nightscout.interfaces.ui.UiInteraction import info.nightscout.interfaces.utils.TimeChangeType +import info.nightscout.pump.medtrum.comm.enums.MedtrumPumpState import info.nightscout.pump.medtrum.ui.MedtrumOverviewFragment import info.nightscout.pump.medtrum.services.MedtrumService import info.nightscout.rx.AapsSchedulers @@ -62,6 +63,7 @@ class MedtrumPlugin @Inject constructor( private val fabricPrivacy: FabricPrivacy, private val dateUtil: DateUtil, private val pumpSync: PumpSync, + private val medtrumPump: MedtrumPump, private val uiInteraction: UiInteraction, private val profileFunction: ProfileFunction ) : PumpPluginBase( @@ -116,15 +118,15 @@ class MedtrumPlugin @Inject constructor( } override fun isInitialized(): Boolean { - return false + return medtrumPump.pumpState > MedtrumPumpState.EJECTED } override fun isSuspended(): Boolean { - return true + return medtrumPump.pumpState < MedtrumPumpState.ACTIVE || medtrumPump.pumpState > MedtrumPumpState.ACTIVE_ALT } override fun isBusy(): Boolean { - return true + return false } override fun isConnected(): Boolean { diff --git a/pump/medtrum/src/main/java/info/nightscout/pump/medtrum/MedtrumPump.kt b/pump/medtrum/src/main/java/info/nightscout/pump/medtrum/MedtrumPump.kt index d36f30c91a..6bda29514f 100644 --- a/pump/medtrum/src/main/java/info/nightscout/pump/medtrum/MedtrumPump.kt +++ b/pump/medtrum/src/main/java/info/nightscout/pump/medtrum/MedtrumPump.kt @@ -40,24 +40,21 @@ class MedtrumPump @Inject constructor( var pumpState = MedtrumPumpState.NONE // TODO save in SP var patchActivationState = PatchActivationState.NONE // TODO save in SP - // TODO set these setting on init - // User settings (desired values, to be set on pump) - var desiredPatchExpiration = false - var desiredAlarmSetting = AlarmSetting.LIGHT_VIBRATE_AND_BEEP.code - var desiredHourlyMaxInsulin: Int = 40 - var desiredDailyMaxInsulin: Int = 180 - - // User settings (actual value's as reported by pump) - - // Alarm settings - - // Pump status - var patchId = 0L - var lastTimeReceivedFromPump = 0L // Time in seconds! var lastKnownSequenceNumber = 0 + var lastTimeReceivedFromPump = 0L // Time in seconds! + var suspendTime = 0L // Time in seconds! + var patchStartTime = 0L // Time in seconds! + var patchAge = 0L // Time in seconds! + + var reservoir = 0.0 + var primeProgress = 0 - // Pump history + var batteryVoltage_A = 0.0 + var batteryVoltage_B = 0.0 + + var alarmFlags = 0 + var alarmParameter = 0 // Last basal status update var lastBasalType = 0 @@ -70,6 +67,15 @@ class MedtrumPump @Inject constructor( var lastStopSequence = 0 var lastStopPatchId = 0 + + // TODO set these setting on init + // User settings (desired values, to be set on pump) + var desiredPatchExpiration = false + var desiredAlarmSetting = AlarmSetting.LIGHT_VIBRATE_AND_BEEP.code + var desiredHourlyMaxInsulin: Int = 40 + var desiredDailyMaxInsulin: Int = 180 + + fun buildMedtrumProfileArray(nsProfile: Profile): ByteArray? { val list = nsProfile.getBasalValues() var basals = byteArrayOf() diff --git a/pump/medtrum/src/main/java/info/nightscout/pump/medtrum/comm/enums/MedtrumPumpState.kt b/pump/medtrum/src/main/java/info/nightscout/pump/medtrum/comm/enums/MedtrumPumpState.kt index eabfcac976..e861c8866a 100644 --- a/pump/medtrum/src/main/java/info/nightscout/pump/medtrum/comm/enums/MedtrumPumpState.kt +++ b/pump/medtrum/src/main/java/info/nightscout/pump/medtrum/comm/enums/MedtrumPumpState.kt @@ -3,7 +3,7 @@ package info.nightscout.pump.medtrum.comm.enums enum class MedtrumPumpState(val state: Byte) { NONE(0), IDLE(1), - DELIVERING(2), + FILL(2), PRIMING(3), PRIMED(4), EJECTING(5), @@ -25,5 +25,10 @@ enum class MedtrumPumpState(val state: Byte) { BASE_FAULT(101), BATTERY_OUT(102), NO_CALIBRATION(103), - STOPPED(128.toByte()) + STOPPED(128.toByte()); + + companion object { + fun fromByte(state: Byte) = values().find { it.state == state } + ?: throw IllegalAccessException("") + } } diff --git a/pump/medtrum/src/main/java/info/nightscout/pump/medtrum/comm/packets/ActivatePacket.kt b/pump/medtrum/src/main/java/info/nightscout/pump/medtrum/comm/packets/ActivatePacket.kt index 3e53670032..5911d09525 100644 --- a/pump/medtrum/src/main/java/info/nightscout/pump/medtrum/comm/packets/ActivatePacket.kt +++ b/pump/medtrum/src/main/java/info/nightscout/pump/medtrum/comm/packets/ActivatePacket.kt @@ -50,9 +50,9 @@ class ActivatePacket(injector: HasAndroidInjector, private val basalProfile: Byt * byte 5: lowSuspend // Value for auto mode, not used for AAPS * byte 6: predictiveLowSuspend // Value for auto mode, not used for AAPS * byte 7: predictiveLowSuspendRange // Value for auto mode, not used for AAPS - * byte 8-9: hourlyMaxInsulin // Max hourly dose of insulin not used for now, divided by 0.05 - * byte 10-11: daylyMaxSet // Max daily dose of insulin not used for now, divided by 0.05 - * byte 12-13: tddToday // Current TDD (of present day) not used for now, divided by 0.05 + * byte 8-9: hourlyMaxInsulin // Max hourly dose of insulin, divided by 0.05 + * byte 10-11: daylyMaxSet // Max daily dose of insulin, divided by 0.05 + * byte 12-13: tddToday // Current TDD (of present day), divided by 0.05 * byte 14: 1 // Always 1 * bytes 15 - end // Basal profile > see MedtrumPump */ @@ -82,6 +82,7 @@ class ActivatePacket(injector: HasAndroidInjector, private val basalProfile: Byt medtrumPump.patchId = patchId medtrumPump.lastTimeReceivedFromPump = time + // TODO: Handle basal here, and report to AAPS directly medtrumPump.handleBasalStatusUpdate(basalType, basalValue, basalSequence, basalPatchId, basalStartTime, time) } diff --git a/pump/medtrum/src/main/java/info/nightscout/pump/medtrum/comm/packets/GetTimePacket.kt b/pump/medtrum/src/main/java/info/nightscout/pump/medtrum/comm/packets/GetTimePacket.kt index ff1c5a7d53..cedaf6fdbe 100644 --- a/pump/medtrum/src/main/java/info/nightscout/pump/medtrum/comm/packets/GetTimePacket.kt +++ b/pump/medtrum/src/main/java/info/nightscout/pump/medtrum/comm/packets/GetTimePacket.kt @@ -1,12 +1,15 @@ package info.nightscout.pump.medtrum.comm.packets import dagger.android.HasAndroidInjector +import info.nightscout.pump.medtrum.MedtrumPump import info.nightscout.pump.medtrum.comm.enums.CommandType.GET_TIME import info.nightscout.pump.medtrum.extension.toLong +import info.nightscout.pump.medtrum.util.MedtrumTimeUtil +import javax.inject.Inject class GetTimePacket(injector: HasAndroidInjector) : MedtrumPacket(injector) { - var time: Long = 0 + @Inject lateinit var medtrumPump: MedtrumPump companion object { @@ -22,7 +25,8 @@ class GetTimePacket(injector: HasAndroidInjector) : MedtrumPacket(injector) { override fun handleResponse(data: ByteArray): Boolean { val success = super.handleResponse(data) if (success) { - time = data.copyOfRange(RESP_TIME_START, RESP_TIME_END).toLong() + val time = MedtrumTimeUtil().convertPumpTimeToSystemTimeSeconds(data.copyOfRange(RESP_TIME_START, RESP_TIME_END).toLong()) + medtrumPump.lastTimeReceivedFromPump = time } return success 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 new file mode 100644 index 0000000000..6cb5a5bfa6 --- /dev/null +++ b/pump/medtrum/src/main/java/info/nightscout/pump/medtrum/comm/packets/NotificationPacket.kt @@ -0,0 +1,206 @@ +package info.nightscout.pump.medtrum.comm.packets + +import dagger.android.HasAndroidInjector +import info.nightscout.pump.medtrum.MedtrumPump +import info.nightscout.pump.medtrum.comm.enums.MedtrumPumpState +import info.nightscout.pump.medtrum.extension.toByteArray +import info.nightscout.pump.medtrum.extension.toInt +import info.nightscout.pump.medtrum.extension.toLong +import info.nightscout.pump.medtrum.util.MedtrumTimeUtil +import info.nightscout.rx.logging.AAPSLogger +import info.nightscout.rx.logging.LTag +import javax.inject.Inject +import kotlin.experimental.and + +class NotificationPacket(val injector: HasAndroidInjector) { + + /** + * This is a bit of a special packet, as it is not a command packet + * but a notification packet. It is sent by the pump to the phone + * when the pump has a notification to send. + * + * Notifications are sent regualary, regardless of the pump state. + * + * There can be multiple messages in one packet, this is noted by the fieldMask. + * + * Byte 1: State (Handle a state change directly? before analyzing further?) + * Byte 2-3: FieldMask (BitMask which tells the fields present in the message) + * Byte 4-end : status data + * + * When multiple fields are in the message, the data is concatenated. + * This kind of message can also come as a response of SynchronizePacket, + * and can be handled here by handleMaskedMessage() as well. + */ + + @Inject lateinit var aapsLogger: AAPSLogger + @Inject lateinit var medtrumPump: MedtrumPump + + companion object { + private const val NOTIF_STATE_START = 0 + private const val NOTIF_STATE_END = NOTIF_STATE_START + 1 + + private const val MASK_SUSPEND = 0x01 + private const val MASK_NORMAL_BOLUS = 0x02 + private const val MASK_EXTENDED_BOLUS = 0x04 + private const val MASK_BASAL = 0x08 + + private const val MASK_SETUP = 0x10 + private const val MASK_RESERVOIR = 0x20 + private const val MASK_LIFE_TIME = 0x40 + private const val MASK_BATTERY = 0x80 + + private const val MASK_STORAGE = 0x100 + private const val MASK_ALARM = 0x200 + private const val MASK_START_TIME = 0x400 + private const val MASK_UNKNOWN_1 = 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 + } + + init { + injector.androidInjector().inject(this) + } + + fun handleNotification(notification: ByteArray) { + val state = MedtrumPumpState.fromByte(notification[0]) + aapsLogger.debug(LTag.PUMPCOMM, "Notification state: $state, current state: ${medtrumPump.pumpState}") + + // TODO: Do we need to emit an event on state change? + medtrumPump.pumpState = state + + if (notification.size > NOTIF_STATE_END) { + handleMaskedMessage(notification.copyOfRange(NOTIF_STATE_END, notification.size)) + } + } + + /** + * Handle a message with a field mask, can be used by other packets as well + */ + fun handleMaskedMessage(data: ByteArray) { + val fieldMask = data.copyOfRange(0, 2).toInt() + var offset = 2 + + aapsLogger.debug(LTag.PUMPCOMM, "Message field mask: $fieldMask") + + if (fieldMask and MASK_SUSPEND != 0) { + aapsLogger.debug(LTag.PUMPCOMM, "Suspend notification received") + medtrumPump.suspendTime = 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") + var bolusData = data.copyOfRange(offset, offset + 1).toInt() + var bolusType = bolusData and 0x7F + var bolusCompleted = (bolusData shr 7) and 0x01 + var bolusDelivered = data.copyOfRange(offset + 1, offset + 3).toInt() / 0.05 + // TODO Sync bolus flow: + // If bolus is known add status + // If bolus is not known start read bolus + // When bolus is completed, remove bolus from medtrumPump + aapsLogger.debug(LTag.PUMPCOMM, "Bolus type: $bolusType, bolus completed: $bolusCompleted, bolus delivered: $bolusDelivered") + offset += 3 + } + + if (fieldMask and MASK_EXTENDED_BOLUS != 0) { + aapsLogger.error(LTag.PUMPCOMM, "Extended bolus notification received, extended bolus not supported!") + // TODO Handle error and stop pump if this happens + offset += 3 + } + + if (fieldMask and MASK_BASAL != 0) { + aapsLogger.debug(LTag.PUMPCOMM, "Basal notification received") + var basalType = data.copyOfRange(offset, offset + 1) + var basalSequence = data.copyOfRange(offset + 1, offset + 3) + var basalInterval = data.copyOfRange(offset + 3, offset + 7) + var basalRateAndDelivery = data.copyOfRange(offset + 7, offset + 10).toInt() + var basalRate = basalRateAndDelivery and 0xFFF + var basalDelivery = (basalRateAndDelivery shr 12) + aapsLogger.debug(LTag.PUMPCOMM, "Basal type: $basalType, basal sequence: $basalSequence, basal interval: $basalInterval, basal rate: $basalRate, basal delivery: $basalDelivery") + // TODO Sync basal flow + offset += 10 + } + + 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 + } + + 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 + } + + if (fieldMask and MASK_LIFE_TIME != 0) { + aapsLogger.debug(LTag.PUMPCOMM, "Life time notification received") + // TODO Check if timezone offset needs to be added + medtrumPump.patchAge = data.copyOfRange(offset, offset + 4).toLong() + aapsLogger.debug(LTag.PUMPCOMM, "Patch age: ${medtrumPump.patchAge}") + offset += 4 + } + + if (fieldMask and MASK_BATTERY != 0) { + aapsLogger.debug(LTag.PUMPCOMM, "Battery notification received") + var 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 + } + + if (fieldMask and MASK_STORAGE != 0) { + aapsLogger.debug(LTag.PUMPCOMM, "Storage notification received") + // TODO, trigger check for new sequence? + medtrumPump.lastKnownSequenceNumber = data.copyOfRange(offset, offset + 2).toInt() + medtrumPump.patchId = data.copyOfRange(offset + 2, offset + 4).toLong() + aapsLogger.debug(LTag.PUMPCOMM, "Last known sequence number: ${medtrumPump.lastKnownSequenceNumber}, patch id: ${medtrumPump.patchId}") + offset += 4 + } + + if (fieldMask and MASK_ALARM != 0) { + aapsLogger.debug(LTag.PUMPCOMM, "Alarm notification received") + // Set only flags here, Alarms will be picked up by the state change + medtrumPump.alarmFlags = data.copyOfRange(offset, offset + 2).toInt() + medtrumPump.alarmParameter = data.copyOfRange(offset + 2, offset + 4).toInt() + aapsLogger.debug(LTag.PUMPCOMM, "Alarm flags: ${medtrumPump.alarmFlags}, alarm parameter: ${medtrumPump.alarmParameter}") + offset += 4 + } + + if (fieldMask and MASK_START_TIME != 0) { + aapsLogger.debug(LTag.PUMPCOMM, "Start time notification received") + medtrumPump.patchStartTime = MedtrumTimeUtil().convertPumpTimeToSystemTimeSeconds(data.copyOfRange(offset, offset + 4).toLong()) + aapsLogger.debug(LTag.PUMPCOMM, "Patch start time: ${medtrumPump.patchStartTime}") + offset += 4 + } + + if (fieldMask and MASK_UNKNOWN_1 != 0) { + aapsLogger.debug(LTag.PUMPCOMM, "Unknown 1 notification received, not handled!") + } + + if (fieldMask and MASK_UNUSED_CGM != 0) { + aapsLogger.debug(LTag.PUMPCOMM, "Unused CGM notification received, not handled!") + } + + 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!") + } + + if (fieldMask and MASK_UNUSED_AUTO_STATUS != 0) { + aapsLogger.debug(LTag.PUMPCOMM, "Unused auto status notification received, not handled!") + } + + if (fieldMask and MASK_UNUSED_LEGACY != 0) { + aapsLogger.debug(LTag.PUMPCOMM, "Unused legacy notification received, not handled!") + } + } +} diff --git a/pump/medtrum/src/main/java/info/nightscout/pump/medtrum/comm/packets/ReadBolusStatePacket.kt b/pump/medtrum/src/main/java/info/nightscout/pump/medtrum/comm/packets/ReadBolusStatePacket.kt index 33cb403bb7..222fd55181 100644 --- a/pump/medtrum/src/main/java/info/nightscout/pump/medtrum/comm/packets/ReadBolusStatePacket.kt +++ b/pump/medtrum/src/main/java/info/nightscout/pump/medtrum/comm/packets/ReadBolusStatePacket.kt @@ -20,6 +20,7 @@ class ReadBolusStatePacket(injector: HasAndroidInjector) : MedtrumPacket(injecto override fun handleResponse(data: ByteArray): Boolean { val success = super.handleResponse(data) if (success) { + // TODO: Handle bolus data here bolusData = data.copyOfRange(RESP_BOLUS_DATA_START, data.size) } 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 3524eda324..3c7f077dc2 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 @@ -1,14 +1,17 @@ package info.nightscout.pump.medtrum.comm.packets import dagger.android.HasAndroidInjector +import info.nightscout.pump.medtrum.MedtrumPump import info.nightscout.pump.medtrum.comm.enums.CommandType.SYNCHRONIZE +import info.nightscout.pump.medtrum.comm.enums.MedtrumPumpState +import info.nightscout.pump.medtrum.extension.toByteArray import info.nightscout.pump.medtrum.extension.toInt +import info.nightscout.rx.logging.LTag +import javax.inject.Inject class SynchronizePacket(injector: HasAndroidInjector) : MedtrumPacket(injector) { - var state: Int = 0 - var dataFieldsPresent: Int = 0 - var syncData: ByteArray = byteArrayOf() + @Inject lateinit var medtrumPump: MedtrumPump companion object { @@ -17,6 +20,10 @@ class SynchronizePacket(injector: HasAndroidInjector) : MedtrumPacket(injector) private const val RESP_FIELDS_START = 7 private const val RESP_FIELDS_END = RESP_FIELDS_START + 2 private const val RESP_SYNC_DATA_START = 9 + + private const val MASK_SUSPEND = 0x01 + private const val MASK_NORMAL_BOLUS = 0x02 + private const val MASK_EXTENDED_BOLUS = 0x04 } init { @@ -27,9 +34,36 @@ class SynchronizePacket(injector: HasAndroidInjector) : MedtrumPacket(injector) override fun handleResponse(data: ByteArray): Boolean { val success = super.handleResponse(data) if (success) { - state = data.copyOfRange(RESP_STATE_START, RESP_STATE_END).toInt() - dataFieldsPresent = data.copyOfRange(RESP_FIELDS_START, RESP_FIELDS_END).toInt() - syncData = data.copyOfRange(RESP_SYNC_DATA_START, data.size) + var state = MedtrumPumpState.fromByte(data[RESP_STATE_START]) + + medtrumPump.pumpState = state + + var fieldMask = data.copyOfRange(RESP_FIELDS_START, RESP_FIELDS_END).toInt() + var syncData = data.copyOfRange(RESP_SYNC_DATA_START, data.size) + var offset = 0 + + if (fieldMask != 0) { + aapsLogger.debug(LTag.PUMPCOMM, "SynchronizePacket: fieldMask: $fieldMask") + } + + // Remove bolus fields from fieldMask if fields are present + // TODO: Test if this workaround is needed (hence the warning log) + if (fieldMask and MASK_SUSPEND != 0) { + offset += 4 // If field is present, skip 4 bytes + } + if (fieldMask and MASK_NORMAL_BOLUS != 0) { + aapsLogger.warn(LTag.PUMPCOMM, "SynchronizePacket: Normal bolus present removing from fieldMask") + fieldMask = fieldMask and MASK_NORMAL_BOLUS.inv() + syncData = syncData.copyOfRange(0, offset) + syncData.copyOfRange(offset + 3, syncData.size) + } + if (fieldMask and MASK_EXTENDED_BOLUS != 0) { + aapsLogger.warn(LTag.PUMPCOMM, "SynchronizePacket: Extended bolus present removing from fieldMask") + fieldMask = fieldMask and MASK_EXTENDED_BOLUS.inv() + syncData = syncData.copyOfRange(0, offset) + syncData.copyOfRange(offset + 3, syncData.size) + } + + // Let the notification packet handle the rest of the sync data + NotificationPacket(injector).handleMaskedMessage(fieldMask.toByteArray(2) + syncData) } return success diff --git a/pump/medtrum/src/main/java/info/nightscout/pump/medtrum/di/MedtrumCommModule.kt b/pump/medtrum/src/main/java/info/nightscout/pump/medtrum/di/MedtrumCommModule.kt index 893030c6c2..393a208d06 100644 --- a/pump/medtrum/src/main/java/info/nightscout/pump/medtrum/di/MedtrumCommModule.kt +++ b/pump/medtrum/src/main/java/info/nightscout/pump/medtrum/di/MedtrumCommModule.kt @@ -7,8 +7,10 @@ import info.nightscout.pump.medtrum.comm.packets.AuthorizePacket import info.nightscout.pump.medtrum.comm.packets.CancelBolusPacket import info.nightscout.pump.medtrum.comm.packets.CancelTempBasalPacket import info.nightscout.pump.medtrum.comm.packets.GetDeviceTypePacket +import info.nightscout.pump.medtrum.comm.packets.GetRecordPacket import info.nightscout.pump.medtrum.comm.packets.GetTimePacket import info.nightscout.pump.medtrum.comm.packets.MedtrumPacket +import info.nightscout.pump.medtrum.comm.packets.NotificationPacket import info.nightscout.pump.medtrum.comm.packets.PollPatchPacket import info.nightscout.pump.medtrum.comm.packets.PrimePacket import info.nightscout.pump.medtrum.comm.packets.ReadBolusStatePacket @@ -31,8 +33,10 @@ abstract class MedtrumCommModule { @ContributesAndroidInjector abstract fun contributesCancelBolusPacket(): CancelBolusPacket @ContributesAndroidInjector abstract fun contributesCancelTempBasalPacket(): CancelTempBasalPacket @ContributesAndroidInjector abstract fun contributesGetDeviceTypePacket(): GetDeviceTypePacket + @ContributesAndroidInjector abstract fun contributesGetRecordPacket(): GetRecordPacket @ContributesAndroidInjector abstract fun contributesGetTimePacket(): GetTimePacket @ContributesAndroidInjector abstract fun contributesMedtrumPacket(): MedtrumPacket + @ContributesAndroidInjector abstract fun contributesNotificationPacket(): NotificationPacket @ContributesAndroidInjector abstract fun contributesPollPatchPacket(): PollPatchPacket @ContributesAndroidInjector abstract fun contributesPrimePacket(): PrimePacket @ContributesAndroidInjector abstract fun contributesReadBolusStatePacket(): ReadBolusStatePacket 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 f1b6e1e280..0f958790c8 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 @@ -92,6 +92,7 @@ class BLEComm @Inject internal constructor( var isConnected = false // TODO: These may be removed have no function var isConnecting = false// TODO: These may be removed have no function + private var retryCounter = 0 private var uartWrite: BluetoothGattCharacteristic? = null private var uartRead: BluetoothGattCharacteristic? = null @@ -158,6 +159,7 @@ class BLEComm @Inject internal constructor( } mDeviceSN = deviceSN isConnecting = true + retryCounter = 0 startScan() return true } @@ -374,11 +376,20 @@ class BLEComm @Inject internal constructor( mBluetoothGatt?.discoverServices() }, WRITE_DELAY_MILLIS) } else if (newState == BluetoothProfile.STATE_DISCONNECTED) { - close() - isConnected = false - isConnecting = false - mCallback?.onBLEDisconnected() - aapsLogger.debug(LTag.PUMPBTCOMM, "Device was disconnected " + gatt.device.name) //Device was disconnected + if (status == 133 && isConnecting && retryCounter < 3) { + // Special case for status 133 when we are connecting + // We need to close gatt and try to reconnect + aapsLogger.debug(LTag.PUMPBTCOMM, "onConnectionStateChange status 133") + close() + startScan() + retryCounter++ + } else { + close() + isConnected = false + isConnecting = false + mCallback?.onBLEDisconnected() + aapsLogger.debug(LTag.PUMPBTCOMM, "Device was disconnected " + gatt.device.name) //Device was disconnectedS + } } } 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 faea212261..864c4f7c82 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 @@ -17,6 +17,8 @@ import info.nightscout.interfaces.pump.PumpSync import info.nightscout.interfaces.queue.CommandQueue import info.nightscout.interfaces.ui.UiInteraction import info.nightscout.pump.medtrum.MedtrumPlugin +import info.nightscout.pump.medtrum.MedtrumPump +import info.nightscout.pump.medtrum.comm.enums.MedtrumPumpState import info.nightscout.pump.medtrum.comm.packets.* import info.nightscout.pump.medtrum.extension.toInt import info.nightscout.pump.medtrum.extension.toLong @@ -50,6 +52,7 @@ class MedtrumService : DaggerService(), BLECommCallback { @Inject lateinit var commandQueue: CommandQueue @Inject lateinit var context: Context @Inject lateinit var medtrumPlugin: MedtrumPlugin + @Inject lateinit var medtrumPump: MedtrumPump @Inject lateinit var activePlugin: ActivePlugin @Inject lateinit var constraintChecker: Constraints @Inject lateinit var uiInteraction: UiInteraction @@ -72,6 +75,29 @@ class MedtrumService : DaggerService(), BLECommCallback { // TODO: Stuff like this in a settings class? private var mLastDeviceTime: Long = 0 + companion object { + + private val MASK_SUSPEND = 0x01 + private val MASK_NORMAL_BOLUS = 0x02 + private val MASK_EXTENDED_BOLUS = 0x04 + private val MASK_BASAL = 0x08 + + private val MASK_SETUP = 0x10 + private val MASK_RESERVOIR = 0x20 + private val MASK_LIFE_TIME = 0x40 + private val MASK_BATTERY = 0x80 + + private val MASK_STORAGE = 0x100 + private val MASK_ALARM = 0x200 + private val MASK_START_TIME = 0x400 + private val MASK_UNKNOWN_1 = 0x800 + + private val MASK_UNUSED_CGM = 0x1000 + private val MASK_UNUSED_COMMAND_CONFIRM = 0x2000 + private val MASK_UNUSED_AUTO_STATUS = 0x4000 + private val MASK_UNUSED_LEGACY = 0x8000 + } + override fun onCreate() { super.onCreate() bleComm.setCallback(this) @@ -182,7 +208,7 @@ class MedtrumService : DaggerService(), BLECommCallback { override fun onNotification(notification: ByteArray) { aapsLogger.debug(LTag.PUMPCOMM, "<<<<< onNotification" + notification.contentToString()) - // TODO + NotificationPacket(injector).handleNotification(notification) } override fun onIndication(indication: ByteArray) { @@ -315,15 +341,14 @@ class MedtrumService : DaggerService(), BLECommCallback { override fun onIndication(data: ByteArray) { if (mPacket?.handleResponse(data) == true) { // Succes! - mLastDeviceTime = (mPacket as GetTimePacket).time val currTimeSec = dateUtil.nowWithoutMilliseconds() / 1000 - if (abs(timeUtil.convertPumpTimeToSystemTimeSeconds(mLastDeviceTime) - currTimeSec) <= 5) { // Allow 5 sec deviation + if (abs(timeUtil.convertPumpTimeToSystemTimeSeconds(medtrumPump.lastTimeReceivedFromPump) - currTimeSec) <= 5) { // Allow 5 sec deviation toState(SynchronizeState()) } else { aapsLogger.debug( LTag.PUMPCOMM, - "GetTimeState.onIndication need to set time. systemTime: $currTimeSec PumpTime: $mLastDeviceTime Pump Time to system time: " + timeUtil.convertPumpTimeToSystemTimeSeconds( - mLastDeviceTime + "GetTimeState.onIndication need to set time. systemTime: $currTimeSec PumpTime: ${medtrumPump.lastTimeReceivedFromPump} Pump Time to system time: " + timeUtil.convertPumpTimeToSystemTimeSeconds( + medtrumPump.lastTimeReceivedFromPump ) ) toState(SetTimeState()) diff --git a/pump/medtrum/src/test/java/info/nightscout/pump/medtrum/comm/packets/GetTimePacketTest.kt b/pump/medtrum/src/test/java/info/nightscout/pump/medtrum/comm/packets/GetTimePacketTest.kt index 4ea715a677..4fff76bc90 100644 --- a/pump/medtrum/src/test/java/info/nightscout/pump/medtrum/comm/packets/GetTimePacketTest.kt +++ b/pump/medtrum/src/test/java/info/nightscout/pump/medtrum/comm/packets/GetTimePacketTest.kt @@ -4,6 +4,7 @@ import dagger.android.AndroidInjector import dagger.android.HasAndroidInjector import info.nightscout.pump.medtrum.MedtrumTestBase import info.nightscout.pump.medtrum.extension.toByteArray +import info.nightscout.pump.medtrum.util.MedtrumTimeUtil import org.junit.jupiter.api.Test import org.junit.Assert.* @@ -13,8 +14,9 @@ class GetTimePacketTest : MedtrumTestBase() { private val packetInjector = HasAndroidInjector { AndroidInjector { - if (it is MedtrumPacket) { + if (it is GetTimePacket) { it.aapsLogger = aapsLogger + it.medtrumPump = medtrumPump } } } @@ -46,7 +48,7 @@ class GetTimePacketTest : MedtrumTestBase() { // Expected values assertEquals(true, result) assertEquals(false, packet.failed) - assertEquals(time, packet.time) + assertEquals(MedtrumTimeUtil().convertPumpTimeToSystemTimeSeconds(time), medtrumPump.lastTimeReceivedFromPump) } @Test fun handleResponseGivenResponseWhenMessageTooShortThenResultFalse() { @@ -63,6 +65,6 @@ class GetTimePacketTest : MedtrumTestBase() { // Expected values assertEquals(false, result) assertEquals(true, packet.failed) - assertEquals(0, packet.time) + assertEquals(0, medtrumPump.lastTimeReceivedFromPump) } } diff --git a/pump/medtrum/src/test/java/info/nightscout/pump/medtrum/comm/packets/MedtrumPacketTest.kt b/pump/medtrum/src/test/java/info/nightscout/pump/medtrum/comm/packets/MedtrumPacketTest.kt index 33fb7ff2b1..0c6bf53ac9 100644 --- a/pump/medtrum/src/test/java/info/nightscout/pump/medtrum/comm/packets/MedtrumPacketTest.kt +++ b/pump/medtrum/src/test/java/info/nightscout/pump/medtrum/comm/packets/MedtrumPacketTest.kt @@ -9,7 +9,7 @@ import org.junit.Assert.* class MedtrumPacketTest : MedtrumTestBase() { - /** Test base behavoir of the medtrum packet, thse */ + /** Test base behavoir of the medtrum packet */ private val packetInjector = HasAndroidInjector { AndroidInjector { 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 new file mode 100644 index 0000000000..8019981ebb --- /dev/null +++ b/pump/medtrum/src/test/java/info/nightscout/pump/medtrum/comm/packets/NotificationPacketTest.kt @@ -0,0 +1,39 @@ +package info.nightscout.pump.medtrum.comm.packets + +import dagger.android.AndroidInjector +import dagger.android.HasAndroidInjector +import info.nightscout.pump.medtrum.MedtrumTestBase +import info.nightscout.pump.medtrum.comm.enums.MedtrumPumpState +import info.nightscout.pump.medtrum.extension.toByteArray +import org.junit.jupiter.api.Test +import org.junit.Assert.* + +class NotificationPacketTest : MedtrumTestBase() { + + /** Test base behavoir of the Notification packet */ + + private val packetInjector = HasAndroidInjector { + AndroidInjector { + if (it is NotificationPacket) { + it.aapsLogger = aapsLogger + it.medtrumPump = medtrumPump + } + } + } + + @Test fun handleNotificationGivenStatusAndDataThenStateSaved() { + // Inputs + val state: Byte = 1 + + // Call + NotificationPacket(packetInjector).handleNotification(byteArrayOf(state)) + + // Expected values + assertEquals(medtrumPump.pumpState, MedtrumPumpState.fromByte(state)) + } + + @Test fun handleMaskedMessageGivenMaskAndDataThenDataSaved() { + // TODO: Implement + assertTrue(false) + } +} \ No newline at end of file diff --git a/pump/medtrum/src/test/java/info/nightscout/pump/medtrum/comm/packets/SynchronizePacketTest.kt b/pump/medtrum/src/test/java/info/nightscout/pump/medtrum/comm/packets/SynchronizePacketTest.kt index 121f51b3a6..2250202bf1 100644 --- a/pump/medtrum/src/test/java/info/nightscout/pump/medtrum/comm/packets/SynchronizePacketTest.kt +++ b/pump/medtrum/src/test/java/info/nightscout/pump/medtrum/comm/packets/SynchronizePacketTest.kt @@ -13,8 +13,13 @@ class SynchronizePacketTest : MedtrumTestBase() { private val packetInjector = HasAndroidInjector { AndroidInjector { - if (it is MedtrumPacket) { + if (it is SynchronizePacket) { it.aapsLogger = aapsLogger + it.medtrumPump = medtrumPump + } + if (it is NotificationPacket) { + it.aapsLogger = aapsLogger + it.medtrumPump = medtrumPump } } } @@ -36,10 +41,10 @@ class SynchronizePacketTest : MedtrumTestBase() { // Inputs val opCode = 3 val responseCode = 0 - val state = 1 + val state: Byte = 1 val dataFieldsPresent = 4046 - val syncData = byteArrayOf(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15) - val response = byteArrayOf(0) + opCode.toByte() + 0.toByteArray(2) + responseCode.toByteArray(2) + state.toByteArray(1) + dataFieldsPresent.toByteArray(2) + syncData + val syncData = byteArrayOf(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42) + val response = byteArrayOf(0) + opCode.toByte() + 0.toByteArray(2) + responseCode.toByteArray(2) + state + dataFieldsPresent.toByteArray(2) + syncData // Call val packet = SynchronizePacket(packetInjector) @@ -48,9 +53,8 @@ class SynchronizePacketTest : MedtrumTestBase() { // Expected values assertEquals(true, result) assertEquals(false, packet.failed) - assertEquals(state, packet.state) - assertEquals(dataFieldsPresent, packet.dataFieldsPresent) - assertEquals(syncData.contentToString(), packet.syncData.contentToString()) + assertEquals(state, packet.medtrumPump.pumpState.state) + // TODO: Maybe test cutting behavoir } @Test fun handleResponseGivenResponseWhenMessageTooShortThenResultFalse() {