NotificationPacket

This commit is contained in:
jbr7rr 2023-03-28 19:26:58 +02:00
parent cafdd6cf8f
commit af6445a3dc
15 changed files with 396 additions and 52 deletions

View file

@ -26,6 +26,7 @@ import info.nightscout.interfaces.queue.CommandQueue
import info.nightscout.interfaces.queue.CustomCommand import info.nightscout.interfaces.queue.CustomCommand
import info.nightscout.interfaces.ui.UiInteraction import info.nightscout.interfaces.ui.UiInteraction
import info.nightscout.interfaces.utils.TimeChangeType 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.ui.MedtrumOverviewFragment
import info.nightscout.pump.medtrum.services.MedtrumService import info.nightscout.pump.medtrum.services.MedtrumService
import info.nightscout.rx.AapsSchedulers import info.nightscout.rx.AapsSchedulers
@ -62,6 +63,7 @@ class MedtrumPlugin @Inject constructor(
private val fabricPrivacy: FabricPrivacy, private val fabricPrivacy: FabricPrivacy,
private val dateUtil: DateUtil, private val dateUtil: DateUtil,
private val pumpSync: PumpSync, private val pumpSync: PumpSync,
private val medtrumPump: MedtrumPump,
private val uiInteraction: UiInteraction, private val uiInteraction: UiInteraction,
private val profileFunction: ProfileFunction private val profileFunction: ProfileFunction
) : PumpPluginBase( ) : PumpPluginBase(
@ -116,15 +118,15 @@ class MedtrumPlugin @Inject constructor(
} }
override fun isInitialized(): Boolean { override fun isInitialized(): Boolean {
return false return medtrumPump.pumpState > MedtrumPumpState.EJECTED
} }
override fun isSuspended(): Boolean { override fun isSuspended(): Boolean {
return true return medtrumPump.pumpState < MedtrumPumpState.ACTIVE || medtrumPump.pumpState > MedtrumPumpState.ACTIVE_ALT
} }
override fun isBusy(): Boolean { override fun isBusy(): Boolean {
return true return false
} }
override fun isConnected(): Boolean { override fun isConnected(): Boolean {

View file

@ -40,24 +40,21 @@ class MedtrumPump @Inject constructor(
var pumpState = MedtrumPumpState.NONE // TODO save in SP var pumpState = MedtrumPumpState.NONE // TODO save in SP
var patchActivationState = PatchActivationState.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 patchId = 0L
var lastTimeReceivedFromPump = 0L // Time in seconds!
var lastKnownSequenceNumber = 0 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!
// Pump history var reservoir = 0.0
var primeProgress = 0
var batteryVoltage_A = 0.0
var batteryVoltage_B = 0.0
var alarmFlags = 0
var alarmParameter = 0
// Last basal status update // Last basal status update
var lastBasalType = 0 var lastBasalType = 0
@ -70,6 +67,15 @@ class MedtrumPump @Inject constructor(
var lastStopSequence = 0 var lastStopSequence = 0
var lastStopPatchId = 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? { fun buildMedtrumProfileArray(nsProfile: Profile): ByteArray? {
val list = nsProfile.getBasalValues() val list = nsProfile.getBasalValues()
var basals = byteArrayOf() var basals = byteArrayOf()

View file

@ -3,7 +3,7 @@ package info.nightscout.pump.medtrum.comm.enums
enum class MedtrumPumpState(val state: Byte) { enum class MedtrumPumpState(val state: Byte) {
NONE(0), NONE(0),
IDLE(1), IDLE(1),
DELIVERING(2), FILL(2),
PRIMING(3), PRIMING(3),
PRIMED(4), PRIMED(4),
EJECTING(5), EJECTING(5),
@ -25,5 +25,10 @@ enum class MedtrumPumpState(val state: Byte) {
BASE_FAULT(101), BASE_FAULT(101),
BATTERY_OUT(102), BATTERY_OUT(102),
NO_CALIBRATION(103), NO_CALIBRATION(103),
STOPPED(128.toByte()) STOPPED(128.toByte());
companion object {
fun fromByte(state: Byte) = values().find { it.state == state }
?: throw IllegalAccessException("")
}
} }

View file

@ -50,9 +50,9 @@ class ActivatePacket(injector: HasAndroidInjector, private val basalProfile: Byt
* byte 5: lowSuspend // Value for auto mode, not used for AAPS * byte 5: lowSuspend // Value for auto mode, not used for AAPS
* byte 6: predictiveLowSuspend // 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 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 8-9: hourlyMaxInsulin // Max hourly dose of insulin, divided by 0.05
* byte 10-11: daylyMaxSet // Max daily dose of insulin not used for now, 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) not used for now, divided by 0.05 * byte 12-13: tddToday // Current TDD (of present day), divided by 0.05
* byte 14: 1 // Always 1 * byte 14: 1 // Always 1
* bytes 15 - end // Basal profile > see MedtrumPump * bytes 15 - end // Basal profile > see MedtrumPump
*/ */
@ -82,6 +82,7 @@ class ActivatePacket(injector: HasAndroidInjector, private val basalProfile: Byt
medtrumPump.patchId = patchId medtrumPump.patchId = patchId
medtrumPump.lastTimeReceivedFromPump = time medtrumPump.lastTimeReceivedFromPump = time
// TODO: Handle basal here, and report to AAPS directly
medtrumPump.handleBasalStatusUpdate(basalType, basalValue, basalSequence, basalPatchId, basalStartTime, time) medtrumPump.handleBasalStatusUpdate(basalType, basalValue, basalSequence, basalPatchId, basalStartTime, time)
} }

View file

@ -1,12 +1,15 @@
package info.nightscout.pump.medtrum.comm.packets package info.nightscout.pump.medtrum.comm.packets
import dagger.android.HasAndroidInjector 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.comm.enums.CommandType.GET_TIME
import info.nightscout.pump.medtrum.extension.toLong import info.nightscout.pump.medtrum.extension.toLong
import info.nightscout.pump.medtrum.util.MedtrumTimeUtil
import javax.inject.Inject
class GetTimePacket(injector: HasAndroidInjector) : MedtrumPacket(injector) { class GetTimePacket(injector: HasAndroidInjector) : MedtrumPacket(injector) {
var time: Long = 0 @Inject lateinit var medtrumPump: MedtrumPump
companion object { companion object {
@ -22,7 +25,8 @@ class GetTimePacket(injector: HasAndroidInjector) : MedtrumPacket(injector) {
override fun handleResponse(data: ByteArray): Boolean { override fun handleResponse(data: ByteArray): Boolean {
val success = super.handleResponse(data) val success = super.handleResponse(data)
if (success) { 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 return success

View file

@ -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!")
}
}
}

View file

@ -20,6 +20,7 @@ class ReadBolusStatePacket(injector: HasAndroidInjector) : MedtrumPacket(injecto
override fun handleResponse(data: ByteArray): Boolean { override fun handleResponse(data: ByteArray): Boolean {
val success = super.handleResponse(data) val success = super.handleResponse(data)
if (success) { if (success) {
// TODO: Handle bolus data here
bolusData = data.copyOfRange(RESP_BOLUS_DATA_START, data.size) bolusData = data.copyOfRange(RESP_BOLUS_DATA_START, data.size)
} }

View file

@ -1,14 +1,17 @@
package info.nightscout.pump.medtrum.comm.packets package info.nightscout.pump.medtrum.comm.packets
import dagger.android.HasAndroidInjector 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.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.pump.medtrum.extension.toInt
import info.nightscout.rx.logging.LTag
import javax.inject.Inject
class SynchronizePacket(injector: HasAndroidInjector) : MedtrumPacket(injector) { class SynchronizePacket(injector: HasAndroidInjector) : MedtrumPacket(injector) {
var state: Int = 0 @Inject lateinit var medtrumPump: MedtrumPump
var dataFieldsPresent: Int = 0
var syncData: ByteArray = byteArrayOf()
companion object { companion object {
@ -17,6 +20,10 @@ class SynchronizePacket(injector: HasAndroidInjector) : MedtrumPacket(injector)
private const val RESP_FIELDS_START = 7 private const val RESP_FIELDS_START = 7
private const val RESP_FIELDS_END = RESP_FIELDS_START + 2 private const val RESP_FIELDS_END = RESP_FIELDS_START + 2
private const val RESP_SYNC_DATA_START = 9 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 { init {
@ -27,9 +34,36 @@ class SynchronizePacket(injector: HasAndroidInjector) : MedtrumPacket(injector)
override fun handleResponse(data: ByteArray): Boolean { override fun handleResponse(data: ByteArray): Boolean {
val success = super.handleResponse(data) val success = super.handleResponse(data)
if (success) { if (success) {
state = data.copyOfRange(RESP_STATE_START, RESP_STATE_END).toInt() var state = MedtrumPumpState.fromByte(data[RESP_STATE_START])
dataFieldsPresent = data.copyOfRange(RESP_FIELDS_START, RESP_FIELDS_END).toInt()
syncData = data.copyOfRange(RESP_SYNC_DATA_START, data.size) 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 return success

View file

@ -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.CancelBolusPacket
import info.nightscout.pump.medtrum.comm.packets.CancelTempBasalPacket import info.nightscout.pump.medtrum.comm.packets.CancelTempBasalPacket
import info.nightscout.pump.medtrum.comm.packets.GetDeviceTypePacket 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.GetTimePacket
import info.nightscout.pump.medtrum.comm.packets.MedtrumPacket 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.PollPatchPacket
import info.nightscout.pump.medtrum.comm.packets.PrimePacket import info.nightscout.pump.medtrum.comm.packets.PrimePacket
import info.nightscout.pump.medtrum.comm.packets.ReadBolusStatePacket import info.nightscout.pump.medtrum.comm.packets.ReadBolusStatePacket
@ -31,8 +33,10 @@ abstract class MedtrumCommModule {
@ContributesAndroidInjector abstract fun contributesCancelBolusPacket(): CancelBolusPacket @ContributesAndroidInjector abstract fun contributesCancelBolusPacket(): CancelBolusPacket
@ContributesAndroidInjector abstract fun contributesCancelTempBasalPacket(): CancelTempBasalPacket @ContributesAndroidInjector abstract fun contributesCancelTempBasalPacket(): CancelTempBasalPacket
@ContributesAndroidInjector abstract fun contributesGetDeviceTypePacket(): GetDeviceTypePacket @ContributesAndroidInjector abstract fun contributesGetDeviceTypePacket(): GetDeviceTypePacket
@ContributesAndroidInjector abstract fun contributesGetRecordPacket(): GetRecordPacket
@ContributesAndroidInjector abstract fun contributesGetTimePacket(): GetTimePacket @ContributesAndroidInjector abstract fun contributesGetTimePacket(): GetTimePacket
@ContributesAndroidInjector abstract fun contributesMedtrumPacket(): MedtrumPacket @ContributesAndroidInjector abstract fun contributesMedtrumPacket(): MedtrumPacket
@ContributesAndroidInjector abstract fun contributesNotificationPacket(): NotificationPacket
@ContributesAndroidInjector abstract fun contributesPollPatchPacket(): PollPatchPacket @ContributesAndroidInjector abstract fun contributesPollPatchPacket(): PollPatchPacket
@ContributesAndroidInjector abstract fun contributesPrimePacket(): PrimePacket @ContributesAndroidInjector abstract fun contributesPrimePacket(): PrimePacket
@ContributesAndroidInjector abstract fun contributesReadBolusStatePacket(): ReadBolusStatePacket @ContributesAndroidInjector abstract fun contributesReadBolusStatePacket(): ReadBolusStatePacket

View file

@ -92,6 +92,7 @@ class BLEComm @Inject internal constructor(
var isConnected = false // TODO: These may be removed have no function var isConnected = false // TODO: These may be removed have no function
var isConnecting = 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 uartWrite: BluetoothGattCharacteristic? = null
private var uartRead: BluetoothGattCharacteristic? = null private var uartRead: BluetoothGattCharacteristic? = null
@ -158,6 +159,7 @@ class BLEComm @Inject internal constructor(
} }
mDeviceSN = deviceSN mDeviceSN = deviceSN
isConnecting = true isConnecting = true
retryCounter = 0
startScan() startScan()
return true return true
} }
@ -374,11 +376,20 @@ class BLEComm @Inject internal constructor(
mBluetoothGatt?.discoverServices() mBluetoothGatt?.discoverServices()
}, WRITE_DELAY_MILLIS) }, WRITE_DELAY_MILLIS)
} else if (newState == BluetoothProfile.STATE_DISCONNECTED) { } else if (newState == BluetoothProfile.STATE_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() close()
isConnected = false isConnected = false
isConnecting = false isConnecting = false
mCallback?.onBLEDisconnected() mCallback?.onBLEDisconnected()
aapsLogger.debug(LTag.PUMPBTCOMM, "Device was disconnected " + gatt.device.name) //Device was disconnected aapsLogger.debug(LTag.PUMPBTCOMM, "Device was disconnected " + gatt.device.name) //Device was disconnectedS
}
} }
} }

View file

@ -17,6 +17,8 @@ import info.nightscout.interfaces.pump.PumpSync
import info.nightscout.interfaces.queue.CommandQueue import info.nightscout.interfaces.queue.CommandQueue
import info.nightscout.interfaces.ui.UiInteraction import info.nightscout.interfaces.ui.UiInteraction
import info.nightscout.pump.medtrum.MedtrumPlugin 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.comm.packets.*
import info.nightscout.pump.medtrum.extension.toInt import info.nightscout.pump.medtrum.extension.toInt
import info.nightscout.pump.medtrum.extension.toLong import info.nightscout.pump.medtrum.extension.toLong
@ -50,6 +52,7 @@ class MedtrumService : DaggerService(), BLECommCallback {
@Inject lateinit var commandQueue: CommandQueue @Inject lateinit var commandQueue: CommandQueue
@Inject lateinit var context: Context @Inject lateinit var context: Context
@Inject lateinit var medtrumPlugin: MedtrumPlugin @Inject lateinit var medtrumPlugin: MedtrumPlugin
@Inject lateinit var medtrumPump: MedtrumPump
@Inject lateinit var activePlugin: ActivePlugin @Inject lateinit var activePlugin: ActivePlugin
@Inject lateinit var constraintChecker: Constraints @Inject lateinit var constraintChecker: Constraints
@Inject lateinit var uiInteraction: UiInteraction @Inject lateinit var uiInteraction: UiInteraction
@ -72,6 +75,29 @@ class MedtrumService : DaggerService(), BLECommCallback {
// TODO: Stuff like this in a settings class? // TODO: Stuff like this in a settings class?
private var mLastDeviceTime: Long = 0 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() { override fun onCreate() {
super.onCreate() super.onCreate()
bleComm.setCallback(this) bleComm.setCallback(this)
@ -182,7 +208,7 @@ class MedtrumService : DaggerService(), BLECommCallback {
override fun onNotification(notification: ByteArray) { override fun onNotification(notification: ByteArray) {
aapsLogger.debug(LTag.PUMPCOMM, "<<<<< onNotification" + notification.contentToString()) aapsLogger.debug(LTag.PUMPCOMM, "<<<<< onNotification" + notification.contentToString())
// TODO NotificationPacket(injector).handleNotification(notification)
} }
override fun onIndication(indication: ByteArray) { override fun onIndication(indication: ByteArray) {
@ -315,15 +341,14 @@ class MedtrumService : DaggerService(), BLECommCallback {
override fun onIndication(data: ByteArray) { override fun onIndication(data: ByteArray) {
if (mPacket?.handleResponse(data) == true) { if (mPacket?.handleResponse(data) == true) {
// Succes! // Succes!
mLastDeviceTime = (mPacket as GetTimePacket).time
val currTimeSec = dateUtil.nowWithoutMilliseconds() / 1000 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()) toState(SynchronizeState())
} else { } else {
aapsLogger.debug( aapsLogger.debug(
LTag.PUMPCOMM, LTag.PUMPCOMM,
"GetTimeState.onIndication need to set time. systemTime: $currTimeSec PumpTime: $mLastDeviceTime Pump Time to system time: " + timeUtil.convertPumpTimeToSystemTimeSeconds( "GetTimeState.onIndication need to set time. systemTime: $currTimeSec PumpTime: ${medtrumPump.lastTimeReceivedFromPump} Pump Time to system time: " + timeUtil.convertPumpTimeToSystemTimeSeconds(
mLastDeviceTime medtrumPump.lastTimeReceivedFromPump
) )
) )
toState(SetTimeState()) toState(SetTimeState())

View file

@ -4,6 +4,7 @@ import dagger.android.AndroidInjector
import dagger.android.HasAndroidInjector import dagger.android.HasAndroidInjector
import info.nightscout.pump.medtrum.MedtrumTestBase import info.nightscout.pump.medtrum.MedtrumTestBase
import info.nightscout.pump.medtrum.extension.toByteArray import info.nightscout.pump.medtrum.extension.toByteArray
import info.nightscout.pump.medtrum.util.MedtrumTimeUtil
import org.junit.jupiter.api.Test import org.junit.jupiter.api.Test
import org.junit.Assert.* import org.junit.Assert.*
@ -13,8 +14,9 @@ class GetTimePacketTest : MedtrumTestBase() {
private val packetInjector = HasAndroidInjector { private val packetInjector = HasAndroidInjector {
AndroidInjector { AndroidInjector {
if (it is MedtrumPacket) { if (it is GetTimePacket) {
it.aapsLogger = aapsLogger it.aapsLogger = aapsLogger
it.medtrumPump = medtrumPump
} }
} }
} }
@ -46,7 +48,7 @@ class GetTimePacketTest : MedtrumTestBase() {
// Expected values // Expected values
assertEquals(true, result) assertEquals(true, result)
assertEquals(false, packet.failed) assertEquals(false, packet.failed)
assertEquals(time, packet.time) assertEquals(MedtrumTimeUtil().convertPumpTimeToSystemTimeSeconds(time), medtrumPump.lastTimeReceivedFromPump)
} }
@Test fun handleResponseGivenResponseWhenMessageTooShortThenResultFalse() { @Test fun handleResponseGivenResponseWhenMessageTooShortThenResultFalse() {
@ -63,6 +65,6 @@ class GetTimePacketTest : MedtrumTestBase() {
// Expected values // Expected values
assertEquals(false, result) assertEquals(false, result)
assertEquals(true, packet.failed) assertEquals(true, packet.failed)
assertEquals(0, packet.time) assertEquals(0, medtrumPump.lastTimeReceivedFromPump)
} }
} }

View file

@ -9,7 +9,7 @@ import org.junit.Assert.*
class MedtrumPacketTest : MedtrumTestBase() { class MedtrumPacketTest : MedtrumTestBase() {
/** Test base behavoir of the medtrum packet, thse */ /** Test base behavoir of the medtrum packet */
private val packetInjector = HasAndroidInjector { private val packetInjector = HasAndroidInjector {
AndroidInjector { AndroidInjector {

View file

@ -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)
}
}

View file

@ -13,8 +13,13 @@ class SynchronizePacketTest : MedtrumTestBase() {
private val packetInjector = HasAndroidInjector { private val packetInjector = HasAndroidInjector {
AndroidInjector { AndroidInjector {
if (it is MedtrumPacket) { if (it is SynchronizePacket) {
it.aapsLogger = aapsLogger it.aapsLogger = aapsLogger
it.medtrumPump = medtrumPump
}
if (it is NotificationPacket) {
it.aapsLogger = aapsLogger
it.medtrumPump = medtrumPump
} }
} }
} }
@ -36,10 +41,10 @@ class SynchronizePacketTest : MedtrumTestBase() {
// Inputs // Inputs
val opCode = 3 val opCode = 3
val responseCode = 0 val responseCode = 0
val state = 1 val state: Byte = 1
val dataFieldsPresent = 4046 val dataFieldsPresent = 4046
val syncData = byteArrayOf(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15) 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.toByteArray(1) + dataFieldsPresent.toByteArray(2) + syncData val response = byteArrayOf(0) + opCode.toByte() + 0.toByteArray(2) + responseCode.toByteArray(2) + state + dataFieldsPresent.toByteArray(2) + syncData
// Call // Call
val packet = SynchronizePacket(packetInjector) val packet = SynchronizePacket(packetInjector)
@ -48,9 +53,8 @@ class SynchronizePacketTest : MedtrumTestBase() {
// Expected values // Expected values
assertEquals(true, result) assertEquals(true, result)
assertEquals(false, packet.failed) assertEquals(false, packet.failed)
assertEquals(state, packet.state) assertEquals(state, packet.medtrumPump.pumpState.state)
assertEquals(dataFieldsPresent, packet.dataFieldsPresent) // TODO: Maybe test cutting behavoir
assertEquals(syncData.contentToString(), packet.syncData.contentToString())
} }
@Test fun handleResponseGivenResponseWhenMessageTooShortThenResultFalse() { @Test fun handleResponseGivenResponseWhenMessageTooShortThenResultFalse() {