Merge pull request #2793 from jbr7rr/medtrum-fixes

Medtrum: Fixes, tests and lints
This commit is contained in:
Milos Kozak 2023-09-17 23:45:27 +02:00 committed by GitHub
commit cf0e821b3a
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
10 changed files with 637 additions and 268 deletions

View file

@ -255,7 +255,11 @@ import kotlin.math.abs
override fun isConnected(): Boolean {
// This is a workaround to prevent AAPS to trigger connects when we have no patch activated
return if (!isInitialized()) true else medtrumService?.isConnected ?: false
return if (!isInitialized()) {
true
} else {
medtrumService?.isConnected ?: false
}
}
override fun isConnecting(): Boolean = medtrumService?.isConnecting ?: false
@ -544,31 +548,19 @@ import kotlin.math.abs
// Medtrum interface
override fun loadEvents(): PumpEnactResult {
if (!isInitialized()) {
val result = PumpEnactResult(injector).success(false)
result.comment = "pump not initialized"
return result
}
if (!isInitialized()) return PumpEnactResult(injector).success(false).enacted(false)
val connectionOK = medtrumService?.loadEvents() ?: false
return PumpEnactResult(injector).success(connectionOK)
}
override fun setUserOptions(): PumpEnactResult {
if (!isInitialized()) {
val result = PumpEnactResult(injector).success(false)
result.comment = "pump not initialized"
return result
}
if (!isInitialized()) return PumpEnactResult(injector).success(false).enacted(false)
val connectionOK = medtrumService?.setUserSettings() ?: false
return PumpEnactResult(injector).success(connectionOK)
}
override fun clearAlarms(): PumpEnactResult {
if (!isInitialized()) {
val result = PumpEnactResult(injector).success(false)
result.comment = "pump not initialized"
return result
}
if (!isInitialized()) return PumpEnactResult(injector).success(false).enacted(false)
val connectionOK = medtrumService?.clearAlarms() ?: false
return PumpEnactResult(injector).success(connectionOK)
}
@ -579,11 +571,7 @@ import kotlin.math.abs
}
override fun updateTime(): PumpEnactResult {
if (!isInitialized()) {
val result = PumpEnactResult(injector).success(false)
result.comment = "pump not initialized"
return result
}
if (!isInitialized()) return PumpEnactResult(injector).success(false).enacted(false)
val connectionOK = medtrumService?.updateTimeIfNeeded() ?: false
return PumpEnactResult(injector).success(connectionOK)
}

View file

@ -421,7 +421,7 @@ class MedtrumPump @Inject constructor(
)
aapsLogger.debug(
LTag.PUMPCOMM,
"handleBasalStatusUpdate: ${if (newRecord) "**NEW** " else ""}EVENT TEMP_START ($basalType) ${dateUtil.dateAndTimeString(basalStartTime)} ($basalStartTime) " + "Rate: $basalRate Duration: ${duration} temporaryBasalInfo: $temporaryBasalInfo, expectedTemporaryBasal: $expectedTemporaryBasal"
"handleBasalStatusUpdate: ${newRecordInfo(newRecord)}EVENT TEMP_START ($basalType) ${dateUtil.dateAndTimeString(basalStartTime)} ($basalStartTime) " + "Rate: $basalRate Duration: ${duration} temporaryBasalInfo: $temporaryBasalInfo, expectedTemporaryBasal: $expectedTemporaryBasal"
)
}
@ -438,7 +438,7 @@ class MedtrumPump @Inject constructor(
)
aapsLogger.debug(
LTag.PUMPCOMM,
"handleBasalStatusUpdate: ${if (newRecord) "**NEW** " else ""}EVENT TEMP_START ($basalType) ${dateUtil.dateAndTimeString(basalStartTime)} ($basalStartTime) expectedTemporaryBasal: $expectedTemporaryBasal"
"handleBasalStatusUpdate: ${newRecordInfo(newRecord)}EVENT TEMP_START ($basalType) ${dateUtil.dateAndTimeString(basalStartTime)} ($basalStartTime) expectedTemporaryBasal: $expectedTemporaryBasal"
)
}
@ -507,7 +507,7 @@ class MedtrumPump @Inject constructor(
)
aapsLogger.debug(
LTag.PUMPCOMM,
"handleBasalStatusUpdate: ${if (newRecord) "**NEW** " else ""}EVENT TEMP_START (FAKE)"
"handleBasalStatusUpdate: ${newRecordInfo(newRecord)}EVENT TEMP_START (FAKE)"
)
}
@ -597,4 +597,8 @@ class MedtrumPump @Inject constructor(
.let { EnumSet.copyOf(it) }
}
}
private fun newRecordInfo(newRecord: Boolean): String {
return "${if (newRecord) "**NEW** " else ""}"
}
}

View file

@ -0,0 +1,34 @@
package info.nightscout.pump.medtrum.comm.enums
enum class BasalEndReason {
SUCCESS,
SUSPEND_LOW_GLUCOSE,
SUSPEND_PREDICT_LOW_GLUCOSE,
SUSPEND_AUTO,
SUSPEND_MORE_THAN_MAX_PER_HOUR,
SUSPEND_MORE_THAN_MAX_PER_DAY,
SUSPEND_MANUAL,
STOP_OCCLUSION,
STOP_EXPIRED,
STOP_EMPTY,
STOP_PATCH_FAULT,
STOP_PATCH_FAULT2,
STOP_BASE_FAULT,
STOP_PATCH_BATTERY_EXAUSTED,
STOP_MAG_SENSOR_NO_CALIBRATION,
STOP,
STOP_LOW_BATTERY,
STOP_AUTO_EXIT,
STOP_CANCEL,
STOP_LOW_SUPER_CAPACITOR,
STOP_DISCARD,
PAUSE_INTERRUPT,
AUTO_MODE_EXIT,
AUTO_MODE_EXIT_MIN_DELIVERY_TOO_LONG,
AUTO_MODE_EXIT_NO_GLUCOSE_3_HOUR,
AUTO_MODE_EXIT_MAX_DELIVERY_TOO_LONG;
fun isSuspendedByPump(): Boolean {
return this in SUSPEND_LOW_GLUCOSE..SUSPEND_MANUAL
}
}

View file

@ -51,4 +51,19 @@ enum class BasalType {
fun isSuspendedByPump(): Boolean {
return this in SUSPEND_LOW_GLUCOSE..STOP
}
companion object {
fun fromBasalEndReason(endReason: BasalEndReason): BasalType {
return when (endReason) {
BasalEndReason.SUSPEND_LOW_GLUCOSE -> SUSPEND_LOW_GLUCOSE
BasalEndReason.SUSPEND_PREDICT_LOW_GLUCOSE -> SUSPEND_PREDICT_LOW_GLUCOSE
BasalEndReason.SUSPEND_AUTO -> SUSPEND_AUTO
BasalEndReason.SUSPEND_MORE_THAN_MAX_PER_HOUR -> SUSPEND_MORE_THAN_MAX_PER_HOUR
BasalEndReason.SUSPEND_MORE_THAN_MAX_PER_DAY -> SUSPEND_MORE_THAN_MAX_PER_DAY
BasalEndReason.SUSPEND_MANUAL -> SUSPEND_MANUAL
else -> NONE
}
}
}
}

View file

@ -27,6 +27,10 @@ enum class MedtrumPumpState(val state: Byte) {
NO_CALIBRATION(103),
STOPPED(128.toByte());
fun isSuspendedByPump(): Boolean {
return this in LOW_BG_SUSPENDED..SUSPENDED
}
companion object {
fun fromByte(state: Byte) = values().find { it.state == state }

View file

@ -5,6 +5,7 @@ import info.nightscout.interfaces.pump.DetailedBolusInfoStorage
import info.nightscout.interfaces.pump.PumpSync
import info.nightscout.interfaces.pump.TemporaryBasalStorage
import info.nightscout.pump.medtrum.MedtrumPump
import info.nightscout.pump.medtrum.comm.enums.BasalEndReason
import info.nightscout.pump.medtrum.comm.enums.CommandType.GET_RECORD
import info.nightscout.pump.medtrum.comm.enums.BasalType
import info.nightscout.pump.medtrum.comm.enums.BolusType
@ -15,6 +16,7 @@ import info.nightscout.pump.medtrum.extension.toLong
import info.nightscout.pump.medtrum.util.MedtrumTimeUtil
import info.nightscout.rx.logging.LTag
import info.nightscout.shared.utils.DateUtil
import info.nightscout.shared.utils.T
import javax.inject.Inject
class GetRecordPacket(injector: HasAndroidInjector, private val recordIndex: Int) : MedtrumPacket(injector) {
@ -74,7 +76,7 @@ class GetRecordPacket(injector: HasAndroidInjector, private val recordIndex: Int
val recordUnknown = data.copyOfRange(RESP_RECORD_UNKNOWN_START, RESP_RECORD_UNKNOWN_END).toInt()
val recordType = data.copyOfRange(RESP_RECORD_TYPE_START, RESP_RECORD_TYPE_END).toInt()
val recordSerial = data.copyOfRange(RESP_RECORD_SERIAL_START, RESP_RECORD_SERIAL_END).toLong()
val recordPatchId = data.copyOfRange(RESP_RECORD_PATCH_ID_START, RESP_RECORD_PATCH_ID_END).toInt()
val recordPatchId = data.copyOfRange(RESP_RECORD_PATCH_ID_START, RESP_RECORD_PATCH_ID_END).toLong()
val recordSequence = data.copyOfRange(RESP_RECORD_SEQUENCE_START, RESP_RECORD_SEQUENCE_END).toInt()
aapsLogger.debug(
@ -82,201 +84,14 @@ class GetRecordPacket(injector: HasAndroidInjector, private val recordIndex: Int
"GetRecordPacket HandleResponse: Record header: $recordHeader, unknown: $recordUnknown, type: $recordType, serial: $recordSerial, patchId: $recordPatchId, " + "sequence: $recordSequence"
)
medtrumPump.syncedSequenceNumber = recordSequence // Assume sync upwards
if (recordHeader == VALID_HEADER) {
when (recordType) {
BOLUS_RECORD, BOLUS_RECORD_ALT -> {
aapsLogger.debug(LTag.PUMPCOMM, "GetRecordPacket HandleResponse: BOLUS_RECORD")
val typeAndWizard = data.copyOfRange(RESP_RECORD_DATA_START, RESP_RECORD_DATA_START + 1).toInt()
val bolusCause = data.copyOfRange(RESP_RECORD_DATA_START + 1, RESP_RECORD_DATA_START + 2).toInt()
val unknown = data.copyOfRange(RESP_RECORD_DATA_START + 2, RESP_RECORD_DATA_START + 4).toInt()
val bolusStartTime = MedtrumTimeUtil().convertPumpTimeToSystemTimeMillis(data.copyOfRange(RESP_RECORD_DATA_START + 4, RESP_RECORD_DATA_START + 8).toLong())
val bolusNormalAmount = data.copyOfRange(RESP_RECORD_DATA_START + 8, RESP_RECORD_DATA_START + 10).toInt() * 0.05
val bolusNormalDelivered = data.copyOfRange(RESP_RECORD_DATA_START + 10, RESP_RECORD_DATA_START + 12).toInt() * 0.05
val bolusExtendedAmount = data.copyOfRange(RESP_RECORD_DATA_START + 12, RESP_RECORD_DATA_START + 14).toInt() * 0.05
val bolusExtendedDuration = data.copyOfRange(RESP_RECORD_DATA_START + 14, RESP_RECORD_DATA_START + 16).toLong() * 1000
val bolusExtendedDelivered = data.copyOfRange(RESP_RECORD_DATA_START + 16, RESP_RECORD_DATA_START + 18).toInt() * 0.05
val bolusCarb = data.copyOfRange(RESP_RECORD_DATA_START + 18, RESP_RECORD_DATA_START + 20).toInt()
val bolusGlucose = data.copyOfRange(RESP_RECORD_DATA_START + 20, RESP_RECORD_DATA_START + 22).toInt()
val bolusIOB = data.copyOfRange(RESP_RECORD_DATA_START + 22, RESP_RECORD_DATA_START + 24).toInt()
val unknown1 = data.copyOfRange(RESP_RECORD_DATA_START + 24, RESP_RECORD_DATA_START + 26).toInt()
val unknown2 = data.copyOfRange(RESP_RECORD_DATA_START + 26, RESP_RECORD_DATA_START + 28).toInt()
val bolusType = enumValues<BolusType>()[typeAndWizard and 0x0F]
val bolusWizard = (typeAndWizard and 0xF0) != 0
aapsLogger.debug(
LTag.PUMPCOMM,
"GetRecordPacket HandleResponse: BOLUS_RECORD: typeAndWizard: $typeAndWizard, bolusCause: $bolusCause, unknown: $unknown, bolusStartTime: $bolusStartTime, " +
"bolusNormalAmount: $bolusNormalAmount, bolusNormalDelivered: $bolusNormalDelivered, bolusExtendedAmount: $bolusExtendedAmount, bolusExtendedDuration: " +
"$bolusExtendedDuration, " + "bolusExtendedDelivered: $bolusExtendedDelivered, bolusCarb: $bolusCarb, bolusGlucose: $bolusGlucose, bolusIOB: $bolusIOB, unknown1: $unknown1, unknown2: $unknown2, " + "bolusType: $bolusType, bolusWizard: $bolusWizard"
)
when (bolusType) {
BolusType.NORMAL -> {
val detailedBolusInfo = detailedBolusInfoStorage.findDetailedBolusInfo(bolusStartTime, bolusNormalDelivered)
var newRecord = false
if (detailedBolusInfo != null) {
val syncOk = pumpSync.syncBolusWithTempId(
timestamp = bolusStartTime,
amount = bolusNormalDelivered,
temporaryId = detailedBolusInfo.timestamp,
type = detailedBolusInfo.bolusType,
pumpId = bolusStartTime,
pumpType = medtrumPump.pumpType(),
pumpSerial = medtrumPump.pumpSN.toString(radix = 16)
)
if (!syncOk) {
aapsLogger.warn(LTag.PUMPCOMM, "GetRecordPacket HandleResponse: BOLUS_RECORD: Failed to sync bolus with tempId: ${detailedBolusInfo.timestamp}")
// detailedInfo can be from another similar record. Reinsert
detailedBolusInfoStorage.add(detailedBolusInfo)
}
} else {
newRecord = pumpSync.syncBolusWithPumpId(
timestamp = bolusStartTime,
amount = bolusNormalDelivered,
type = null,
pumpId = bolusStartTime,
pumpType = medtrumPump.pumpType(),
pumpSerial = medtrumPump.pumpSN.toString(radix = 16)
)
}
aapsLogger.debug(
LTag.PUMPCOMM,
"from record: ${if (newRecord) "**NEW** " else ""}EVENT BOLUS ${dateUtil.dateAndTimeString(bolusStartTime)} ($bolusStartTime) Bolus: ${bolusNormalDelivered}U "
)
if (bolusStartTime > medtrumPump.lastBolusTime) {
medtrumPump.lastBolusTime = bolusStartTime
medtrumPump.lastBolusAmount = bolusNormalDelivered
}
}
BolusType.EXTENDED -> {
val newRecord = pumpSync.syncExtendedBolusWithPumpId(
timestamp = bolusStartTime,
amount = bolusExtendedDelivered,
duration = bolusExtendedDuration,
isEmulatingTB = false,
pumpId = bolusStartTime,
pumpType = medtrumPump.pumpType(),
pumpSerial = medtrumPump.pumpSN.toString(radix = 16)
)
aapsLogger.debug(
LTag.PUMPCOMM,
"from record: ${if (newRecord) "**NEW** " else ""}EVENT EXTENDED BOLUS ${dateUtil.dateAndTimeString(bolusStartTime)} ($bolusStartTime) Bolus: ${bolusNormalDelivered}U "
)
}
BolusType.COMBI -> {
// Note, this should never happen, as we don't use combo bolus
val detailedBolusInfo = detailedBolusInfoStorage.findDetailedBolusInfo(bolusStartTime, bolusNormalDelivered)
val newRecord = pumpSync.syncBolusWithPumpId(
timestamp = bolusStartTime,
amount = bolusNormalDelivered,
type = detailedBolusInfo?.bolusType,
pumpId = bolusStartTime,
pumpType = medtrumPump.pumpType(),
pumpSerial = medtrumPump.pumpSN.toString(radix = 16)
)
pumpSync.syncExtendedBolusWithPumpId(
timestamp = bolusStartTime,
amount = bolusExtendedDelivered,
duration = bolusExtendedDuration,
isEmulatingTB = false,
pumpId = bolusStartTime,
pumpType = medtrumPump.pumpType(),
pumpSerial = medtrumPump.pumpSN.toString(radix = 16)
)
aapsLogger.error(
LTag.PUMPCOMM,
"from record: ${if (newRecord) "**NEW** " else ""}EVENT COMBI BOLUS ${dateUtil.dateAndTimeString(bolusStartTime)} ($bolusStartTime) Bolus: ${bolusNormalDelivered}U Extended: ${bolusExtendedDelivered} THIS SHOULD NOT HAPPEN!!!"
)
if (!newRecord && detailedBolusInfo != null) {
// detailedInfo can be from another similar record. Reinsert
detailedBolusInfoStorage.add(detailedBolusInfo)
}
if (bolusStartTime > medtrumPump.lastBolusTime) {
medtrumPump.lastBolusTime = bolusStartTime
medtrumPump.lastBolusAmount = bolusNormalDelivered
}
}
else -> {
aapsLogger.error(LTag.PUMPCOMM, "GetRecordPacket HandleResponse: BOLUS_RECORD: Unknown bolus type: $bolusType")
}
}
handleBolusRecord(data)
}
BASAL_RECORD, BASAL_RECORD_ALT -> {
val medtrumTimeUtil = MedtrumTimeUtil()
val basalStartTime = medtrumTimeUtil.convertPumpTimeToSystemTimeMillis(data.copyOfRange(RESP_RECORD_DATA_START, RESP_RECORD_DATA_START + 4).toLong())
val basalEndTime = medtrumTimeUtil.convertPumpTimeToSystemTimeMillis(data.copyOfRange(RESP_RECORD_DATA_START + 4, RESP_RECORD_DATA_START + 8).toLong())
val basalType = enumValues<BasalType>()[data.copyOfRange(RESP_RECORD_DATA_START + 8, RESP_RECORD_DATA_START + 9).toInt()]
val basalEndReason = data.copyOfRange(RESP_RECORD_DATA_START + 9, RESP_RECORD_DATA_START + 10).toInt()
val basalRate = data.copyOfRange(RESP_RECORD_DATA_START + 10, RESP_RECORD_DATA_START + 12).toInt() * 0.05
val basalDelivered = data.copyOfRange(RESP_RECORD_DATA_START + 12, RESP_RECORD_DATA_START + 14).toInt() * 0.05
val basalPercent = data.copyOfRange(RESP_RECORD_DATA_START + 14, RESP_RECORD_DATA_START + 16).toInt()
aapsLogger.debug(
LTag.PUMPCOMM,
"GetRecordPacket HandleResponse: BASAL_RECORD: Start: $basalStartTime, End: $basalEndTime, Type: $basalType, EndReason: $basalEndReason, Rate: $basalRate, Delivered: $basalDelivered, Percent: $basalPercent"
)
when (basalType) {
BasalType.STANDARD -> {
aapsLogger.debug(LTag.PUMPCOMM, "GetRecordPacket HandleResponse: BASAL_RECORD: Standard basal")
// If we are here it means the basal has ended
}
BasalType.ABSOLUTE_TEMP, BasalType.RELATIVE_TEMP -> {
aapsLogger.debug(LTag.PUMPCOMM, "GetRecordPacket HandleResponse: BASAL_RECORD: Absolute temp basal")
var duration = (basalEndTime - basalStartTime)
// Work around for pumpSync not accepting 0 duration.
// sometimes we get 0 duration for very short basal because the pump only reports time in seconds
if (duration < 250) duration = 250 // 250ms to make sure AAPS accepts it
val newRecord = pumpSync.syncTemporaryBasalWithPumpId(
timestamp = basalStartTime,
rate = if (basalType == BasalType.ABSOLUTE_TEMP) basalRate else basalPercent.toDouble(),
duration = duration,
isAbsolute = (basalType == BasalType.ABSOLUTE_TEMP),
type = PumpSync.TemporaryBasalType.NORMAL,
pumpId = basalStartTime,
pumpType = medtrumPump.pumpType(),
pumpSerial = medtrumPump.pumpSN.toString(radix = 16)
)
aapsLogger.debug(
LTag.PUMPCOMM,
"handleBasalStatusUpdate from record: ${if (newRecord) "**NEW** " else ""}EVENT TEMP_SYNC: ($basalType) ${dateUtil.dateAndTimeString(basalStartTime)} ($basalStartTime) " +
"Rate: $basalRate Duration: ${duration}"
)
}
in BasalType.SUSPEND_LOW_GLUCOSE..BasalType.STOP -> {
aapsLogger.debug(LTag.PUMPCOMM, "GetRecordPacket HandleResponse: BASAL_RECORD: Suspend basal")
val duration = (basalEndTime - basalStartTime)
val newRecord = pumpSync.syncTemporaryBasalWithPumpId(
timestamp = basalEndTime,
rate = 0.0,
duration = duration,
isAbsolute = true,
type = PumpSync.TemporaryBasalType.PUMP_SUSPEND,
pumpId = basalStartTime,
pumpType = medtrumPump.pumpType(),
pumpSerial = medtrumPump.pumpSN.toString(radix = 16)
)
aapsLogger.debug(
LTag.PUMPCOMM,
"handleBasalStatusUpdate from record: ${if (newRecord) "**NEW** " else ""}EVENT SUSPEND: ($basalType) ${dateUtil.dateAndTimeString(basalStartTime)} ($basalStartTime) " +
"Rate: $basalRate Duration: ${duration}"
)
}
else -> {
aapsLogger.error(LTag.PUMPCOMM, "GetRecordPacket HandleResponse: BASAL_RECORD: Unknown basal type: $basalType")
}
}
handleBasalRecord(data)
}
ALARM_RECORD -> {
@ -304,59 +119,272 @@ class GetRecordPacket(injector: HasAndroidInjector, private val recordIndex: Int
}
TDD_RECORD -> {
aapsLogger.debug(LTag.PUMPCOMM, "GetRecordPacket HandleResponse: TDD_RECORD")
val timestamp = MedtrumTimeUtil().convertPumpTimeToSystemTimeMillis(data.copyOfRange(RESP_RECORD_DATA_START, RESP_RECORD_DATA_START + 4).toLong())
val timeZoneOffset = data.copyOfRange(RESP_RECORD_DATA_START + 4, RESP_RECORD_DATA_START + 6).toInt()
val tddMinutes = data.copyOfRange(RESP_RECORD_DATA_START + 6, RESP_RECORD_DATA_START + 8).toInt()
val glucoseRecordTime = data.copyOfRange(RESP_RECORD_DATA_START + 8, RESP_RECORD_DATA_START + 12).toLong()
val tdd = data.copyOfRange(RESP_RECORD_DATA_START + 12, RESP_RECORD_DATA_START + 16).toFloat()
val basalTdd = data.copyOfRange(RESP_RECORD_DATA_START + 16, RESP_RECORD_DATA_START + 20).toFloat()
val glucose = data.copyOfRange(RESP_RECORD_DATA_START + 20, RESP_RECORD_DATA_START + 24).toFloat()
val unknown = data.copyOfRange(RESP_RECORD_DATA_START + 24, RESP_RECORD_DATA_START + 28).toFloat()
val meanSomething = data.copyOfRange(RESP_RECORD_DATA_START + 28, RESP_RECORD_DATA_START + 32).toFloat()
val usedTdd = data.copyOfRange(RESP_RECORD_DATA_START + 32, RESP_RECORD_DATA_START + 36).toFloat()
val usedIBasal = data.copyOfRange(RESP_RECORD_DATA_START + 36, RESP_RECORD_DATA_START + 40).toFloat()
val usedSgBasal = data.copyOfRange(RESP_RECORD_DATA_START + 40, RESP_RECORD_DATA_START + 44).toFloat()
val usedUMax = data.copyOfRange(RESP_RECORD_DATA_START + 44, RESP_RECORD_DATA_START + 48).toFloat()
val newTdd = data.copyOfRange(RESP_RECORD_DATA_START + 48, RESP_RECORD_DATA_START + 52).toFloat()
val newIBasal = data.copyOfRange(RESP_RECORD_DATA_START + 52, RESP_RECORD_DATA_START + 56).toFloat()
val newSgBasal = data.copyOfRange(RESP_RECORD_DATA_START + 56, RESP_RECORD_DATA_START + 60).toFloat()
val newUMax = data.copyOfRange(RESP_RECORD_DATA_START + 60, RESP_RECORD_DATA_START + 64).toFloat()
aapsLogger.debug(
LTag.PUMPCOMM, "TDD_RECORD: timestamp: $timestamp, timeZoneOffset: $timeZoneOffset, tddMinutes: $tddMinutes, glucoseRecordTime: $glucoseRecordTime, tdd: " +
"$tdd, basalTdd: $basalTdd, glucose: $glucose, unknown: $unknown, meanSomething: $meanSomething, usedTdd: $usedTdd, usedIBasal: $usedIBasal, usedSgBasal: " +
"$usedSgBasal, usedUMax: $usedUMax, newTdd: $newTdd, newIBasal: $newIBasal, newSgBasal: $newSgBasal, newUMax: $newUMax"
)
val newRecord = pumpSync.createOrUpdateTotalDailyDose(
timestamp = timestamp,
bolusAmount = (tdd - basalTdd).toDouble(),
basalAmount = basalTdd.toDouble(),
totalAmount = tdd.toDouble(),
pumpId = timestamp,
pumpType = medtrumPump.pumpType(),
pumpSerial = medtrumPump.pumpSN.toString(radix = 16)
)
aapsLogger.debug(
LTag.PUMPCOMM,
"handleBasalStatusUpdate from record: ${if (newRecord) "**NEW** " else ""}EVENT TDD: ${dateUtil.dateAndTimeString(timestamp)} ($timestamp) " +
"TDD: $tdd, BasalTDD: $basalTdd, BolusTDD: ${tdd - basalTdd}"
)
handleTddRecord(data)
}
else -> {
aapsLogger.debug(LTag.PUMPCOMM, "GetRecordPacket HandleResponse: Unknown record type: $recordType")
}
}
} else {
aapsLogger.error(LTag.PUMPCOMM, "GetRecordPacket HandleResponse: Invalid record header")
}
// Update sequence number
medtrumPump.syncedSequenceNumber = recordSequence // Assume sync upwards
}
return success
}
private fun handleBolusRecord(data: ByteArray) {
aapsLogger.debug(LTag.PUMPCOMM, "GetRecordPacket HandleResponse: BOLUS_RECORD")
val typeAndWizard = data.copyOfRange(RESP_RECORD_DATA_START, RESP_RECORD_DATA_START + 1).toInt()
val bolusCause = data.copyOfRange(RESP_RECORD_DATA_START + 1, RESP_RECORD_DATA_START + 2).toInt()
val unknown = data.copyOfRange(RESP_RECORD_DATA_START + 2, RESP_RECORD_DATA_START + 4).toInt()
val bolusStartTime = MedtrumTimeUtil().convertPumpTimeToSystemTimeMillis(data.copyOfRange(RESP_RECORD_DATA_START + 4, RESP_RECORD_DATA_START + 8).toLong())
val bolusNormalAmount = data.copyOfRange(RESP_RECORD_DATA_START + 8, RESP_RECORD_DATA_START + 10).toInt() * 0.05
val bolusNormalDelivered = data.copyOfRange(RESP_RECORD_DATA_START + 10, RESP_RECORD_DATA_START + 12).toInt() * 0.05
val bolusExtendedAmount = data.copyOfRange(RESP_RECORD_DATA_START + 12, RESP_RECORD_DATA_START + 14).toInt() * 0.05
val bolusExtendedDuration = T.mins(data.copyOfRange(RESP_RECORD_DATA_START + 14, RESP_RECORD_DATA_START + 16).toLong()).msecs()
val bolusExtendedDelivered = data.copyOfRange(RESP_RECORD_DATA_START + 16, RESP_RECORD_DATA_START + 18).toInt() * 0.05
val bolusCarb = data.copyOfRange(RESP_RECORD_DATA_START + 18, RESP_RECORD_DATA_START + 20).toInt()
val bolusGlucose = data.copyOfRange(RESP_RECORD_DATA_START + 20, RESP_RECORD_DATA_START + 22).toInt()
val bolusIOB = data.copyOfRange(RESP_RECORD_DATA_START + 22, RESP_RECORD_DATA_START + 24).toInt()
val unknown1 = data.copyOfRange(RESP_RECORD_DATA_START + 24, RESP_RECORD_DATA_START + 26).toInt()
val unknown2 = data.copyOfRange(RESP_RECORD_DATA_START + 26, RESP_RECORD_DATA_START + 28).toInt()
val bolusType = enumValues<BolusType>()[typeAndWizard and 0x0F]
val bolusWizard = (typeAndWizard and 0xF0) != 0
aapsLogger.debug(
LTag.PUMPCOMM,
"GetRecordPacket HandleResponse: BOLUS_RECORD: typeAndWizard: $typeAndWizard, bolusCause: $bolusCause, unknown: $unknown, bolusStartTime: $bolusStartTime, " +
"bolusNormalAmount: $bolusNormalAmount, bolusNormalDelivered: $bolusNormalDelivered, bolusExtendedAmount: $bolusExtendedAmount, bolusExtendedDuration: " +
"$bolusExtendedDuration, " + "bolusExtendedDelivered: $bolusExtendedDelivered, bolusCarb: $bolusCarb, bolusGlucose: $bolusGlucose, bolusIOB: $bolusIOB, unknown1: $unknown1, unknown2: $unknown2, " + "bolusType: $bolusType, bolusWizard: $bolusWizard"
)
when (bolusType) {
BolusType.NORMAL -> {
val detailedBolusInfo = detailedBolusInfoStorage.findDetailedBolusInfo(bolusStartTime, bolusNormalDelivered)
var newRecord = false
if (detailedBolusInfo != null) {
val syncOk = pumpSync.syncBolusWithTempId(
timestamp = bolusStartTime,
amount = bolusNormalDelivered,
temporaryId = detailedBolusInfo.timestamp,
type = detailedBolusInfo.bolusType,
pumpId = bolusStartTime,
pumpType = medtrumPump.pumpType(),
pumpSerial = medtrumPump.pumpSN.toString(radix = 16)
)
if (!syncOk) {
aapsLogger.warn(LTag.PUMPCOMM, "GetRecordPacket HandleResponse: BOLUS_RECORD: Failed to sync bolus with tempId: ${detailedBolusInfo.timestamp}")
// detailedInfo can be from another similar record. Reinsert
detailedBolusInfoStorage.add(detailedBolusInfo)
}
} else {
newRecord = pumpSync.syncBolusWithPumpId(
timestamp = bolusStartTime,
amount = bolusNormalDelivered,
type = null,
pumpId = bolusStartTime,
pumpType = medtrumPump.pumpType(),
pumpSerial = medtrumPump.pumpSN.toString(radix = 16)
)
}
aapsLogger.debug(
LTag.PUMPCOMM,
"from record: ${newRecordInfo(newRecord)}EVENT BOLUS ${dateUtil.dateAndTimeString(bolusStartTime)} ($bolusStartTime) Bolus: ${bolusNormalDelivered}U "
)
if (bolusStartTime > medtrumPump.lastBolusTime) {
medtrumPump.lastBolusTime = bolusStartTime
medtrumPump.lastBolusAmount = bolusNormalDelivered
}
}
BolusType.EXTENDED -> {
val newRecord = pumpSync.syncExtendedBolusWithPumpId(
timestamp = bolusStartTime,
amount = bolusExtendedDelivered,
duration = bolusExtendedDuration,
isEmulatingTB = false,
pumpId = bolusStartTime,
pumpType = medtrumPump.pumpType(),
pumpSerial = medtrumPump.pumpSN.toString(radix = 16)
)
aapsLogger.debug(
LTag.PUMPCOMM,
"from record: ${newRecordInfo(newRecord)}EVENT EXTENDED BOLUS ${dateUtil.dateAndTimeString(bolusStartTime)} ($bolusStartTime) Bolus: ${bolusNormalDelivered}U "
)
}
BolusType.COMBI -> {
// Note, this should never happen, as we don't use combo bolus
val detailedBolusInfo = detailedBolusInfoStorage.findDetailedBolusInfo(bolusStartTime, bolusNormalDelivered)
val newRecord = pumpSync.syncBolusWithPumpId(
timestamp = bolusStartTime,
amount = bolusNormalDelivered,
type = detailedBolusInfo?.bolusType,
pumpId = bolusStartTime,
pumpType = medtrumPump.pumpType(),
pumpSerial = medtrumPump.pumpSN.toString(radix = 16)
)
pumpSync.syncExtendedBolusWithPumpId(
timestamp = bolusStartTime,
amount = bolusExtendedDelivered,
duration = bolusExtendedDuration,
isEmulatingTB = false,
pumpId = bolusStartTime,
pumpType = medtrumPump.pumpType(),
pumpSerial = medtrumPump.pumpSN.toString(radix = 16)
)
aapsLogger.error(
LTag.PUMPCOMM,
"from record: ${newRecordInfo(newRecord)}EVENT COMBI BOLUS ${dateUtil.dateAndTimeString(bolusStartTime)} ($bolusStartTime) Bolus: ${bolusNormalDelivered}U Extended: ${bolusExtendedDelivered} THIS SHOULD NOT HAPPEN!!!"
)
if (!newRecord && detailedBolusInfo != null) {
// detailedInfo can be from another similar record. Reinsert
detailedBolusInfoStorage.add(detailedBolusInfo)
}
if (bolusStartTime > medtrumPump.lastBolusTime) {
medtrumPump.lastBolusTime = bolusStartTime
medtrumPump.lastBolusAmount = bolusNormalDelivered
}
}
else -> {
aapsLogger.error(LTag.PUMPCOMM, "GetRecordPacket HandleResponse: BOLUS_RECORD: Unknown bolus type: $bolusType")
}
}
}
private fun handleBasalRecord(data: ByteArray) {
val medtrumTimeUtil = MedtrumTimeUtil()
val recordPatchId = data.copyOfRange(RESP_RECORD_PATCH_ID_START, RESP_RECORD_PATCH_ID_END).toLong()
val recordSequence = data.copyOfRange(RESP_RECORD_SEQUENCE_START, RESP_RECORD_SEQUENCE_END).toInt()
val basalStartTime = medtrumTimeUtil.convertPumpTimeToSystemTimeMillis(data.copyOfRange(RESP_RECORD_DATA_START, RESP_RECORD_DATA_START + 4).toLong())
val basalEndTime = medtrumTimeUtil.convertPumpTimeToSystemTimeMillis(data.copyOfRange(RESP_RECORD_DATA_START + 4, RESP_RECORD_DATA_START + 8).toLong())
val basalType = enumValues<BasalType>()[data.copyOfRange(RESP_RECORD_DATA_START + 8, RESP_RECORD_DATA_START + 9).toInt()]
val basalEndReason = enumValues<BasalEndReason>()[data.copyOfRange(RESP_RECORD_DATA_START + 9, RESP_RECORD_DATA_START + 10).toInt()]
val basalRate = data.copyOfRange(RESP_RECORD_DATA_START + 10, RESP_RECORD_DATA_START + 12).toInt() * 0.05
val basalDelivered = data.copyOfRange(RESP_RECORD_DATA_START + 12, RESP_RECORD_DATA_START + 14).toInt() * 0.05
val basalPercent = data.copyOfRange(RESP_RECORD_DATA_START + 14, RESP_RECORD_DATA_START + 16).toInt()
aapsLogger.debug(
LTag.PUMPCOMM,
"GetRecordPacket HandleResponse: BASAL_RECORD: Start: $basalStartTime, End: $basalEndTime, Type: $basalType, EndReason: $basalEndReason, Rate: $basalRate, Delivered: $basalDelivered, Percent: $basalPercent"
)
when (basalType) {
BasalType.STANDARD -> {
aapsLogger.debug(LTag.PUMPCOMM, "GetRecordPacket HandleResponse: BASAL_RECORD: Standard basal")
// If we are here it means the basal has ended
}
BasalType.ABSOLUTE_TEMP, BasalType.RELATIVE_TEMP -> {
aapsLogger.debug(LTag.PUMPCOMM, "GetRecordPacket HandleResponse: BASAL_RECORD: temp basal")
var duration = (basalEndTime - basalStartTime)
// Work around for pumpSync not accepting 0 duration.
// sometimes we get 0 duration for very short basal because the pump only reports time in seconds
if (duration < 250) duration = 250 // 250ms to make sure AAPS accepts it
val newRecord = pumpSync.syncTemporaryBasalWithPumpId(
timestamp = basalStartTime,
rate = if (basalType == BasalType.ABSOLUTE_TEMP) basalRate else basalPercent.toDouble(),
duration = duration,
isAbsolute = (basalType == BasalType.ABSOLUTE_TEMP),
type = PumpSync.TemporaryBasalType.NORMAL,
pumpId = basalStartTime,
pumpType = medtrumPump.pumpType(),
pumpSerial = medtrumPump.pumpSN.toString(radix = 16)
)
aapsLogger.debug(
LTag.PUMPCOMM,
"handleBasalStatusUpdate from record: ${newRecordInfo(newRecord)}EVENT TEMP_SYNC: ($basalType) ${dateUtil.dateAndTimeString(basalStartTime)} ($basalStartTime) " +
"Rate: $basalRate Duration: ${duration}"
)
}
in BasalType.SUSPEND_LOW_GLUCOSE..BasalType.STOP -> {
aapsLogger.debug(LTag.PUMPCOMM, "GetRecordPacket HandleResponse: BASAL_RECORD: Suspend basal")
// Never seen a packet like this from a pump, even when suspended by app, but leave it in just in case
val duration = (basalEndTime - basalStartTime)
val newRecord = pumpSync.syncTemporaryBasalWithPumpId(
timestamp = basalStartTime,
rate = 0.0,
duration = duration,
isAbsolute = true,
type = PumpSync.TemporaryBasalType.PUMP_SUSPEND,
pumpId = basalStartTime,
pumpType = medtrumPump.pumpType(),
pumpSerial = medtrumPump.pumpSN.toString(radix = 16)
)
aapsLogger.debug(
LTag.PUMPCOMM,
"handleBasalStatusUpdate from record: ${newRecordInfo(newRecord)}EVENT SUSPEND: ($basalType) ${dateUtil.dateAndTimeString(basalStartTime)} ($basalStartTime) " +
"Rate: $basalRate Duration: ${duration}"
)
}
else -> {
aapsLogger.error(LTag.PUMPCOMM, "GetRecordPacket HandleResponse: BASAL_RECORD: Unknown basal type: $basalType")
}
}
if (basalEndReason.isSuspendedByPump()) {
// Pump doesn't seem to sync suspend explicitly, so we need to do it here
// Sync suspend using handleBasalStatusUpdate to make sure other variables are updated as well
aapsLogger.warn(LTag.PUMPCOMM, "GetRecordPacket HandleResponse: Got suspended end reason, syncing suspend")
medtrumPump.handleBasalStatusUpdate(BasalType.fromBasalEndReason(basalEndReason), 0.0, recordSequence, recordPatchId, basalEndTime)
}
}
private fun handleTddRecord(data: ByteArray) {
aapsLogger.debug(LTag.PUMPCOMM, "GetRecordPacket HandleResponse: TDD_RECORD")
val timestamp = MedtrumTimeUtil().convertPumpTimeToSystemTimeMillis(data.copyOfRange(RESP_RECORD_DATA_START, RESP_RECORD_DATA_START + 4).toLong())
val timeZoneOffset = data.copyOfRange(RESP_RECORD_DATA_START + 4, RESP_RECORD_DATA_START + 6).toInt()
val tddMinutes = data.copyOfRange(RESP_RECORD_DATA_START + 6, RESP_RECORD_DATA_START + 8).toInt()
val glucoseRecordTime = data.copyOfRange(RESP_RECORD_DATA_START + 8, RESP_RECORD_DATA_START + 12).toLong()
val tdd = data.copyOfRange(RESP_RECORD_DATA_START + 12, RESP_RECORD_DATA_START + 16).toFloat()
val basalTdd = data.copyOfRange(RESP_RECORD_DATA_START + 16, RESP_RECORD_DATA_START + 20).toFloat()
val glucose = data.copyOfRange(RESP_RECORD_DATA_START + 20, RESP_RECORD_DATA_START + 24).toFloat()
val unknown = data.copyOfRange(RESP_RECORD_DATA_START + 24, RESP_RECORD_DATA_START + 28).toFloat()
val meanSomething = data.copyOfRange(RESP_RECORD_DATA_START + 28, RESP_RECORD_DATA_START + 32).toFloat()
val usedTdd = data.copyOfRange(RESP_RECORD_DATA_START + 32, RESP_RECORD_DATA_START + 36).toFloat()
val usedIBasal = data.copyOfRange(RESP_RECORD_DATA_START + 36, RESP_RECORD_DATA_START + 40).toFloat()
val usedSgBasal = data.copyOfRange(RESP_RECORD_DATA_START + 40, RESP_RECORD_DATA_START + 44).toFloat()
val usedUMax = data.copyOfRange(RESP_RECORD_DATA_START + 44, RESP_RECORD_DATA_START + 48).toFloat()
val newTdd = data.copyOfRange(RESP_RECORD_DATA_START + 48, RESP_RECORD_DATA_START + 52).toFloat()
val newIBasal = data.copyOfRange(RESP_RECORD_DATA_START + 52, RESP_RECORD_DATA_START + 56).toFloat()
val newSgBasal = data.copyOfRange(RESP_RECORD_DATA_START + 56, RESP_RECORD_DATA_START + 60).toFloat()
val newUMax = data.copyOfRange(RESP_RECORD_DATA_START + 60, RESP_RECORD_DATA_START + 64).toFloat()
aapsLogger.debug(
LTag.PUMPCOMM, "TDD_RECORD: timestamp: $timestamp, timeZoneOffset: $timeZoneOffset, tddMinutes: $tddMinutes, glucoseRecordTime: $glucoseRecordTime, tdd: " +
"$tdd, basalTdd: $basalTdd, glucose: $glucose, unknown: $unknown, meanSomething: $meanSomething, usedTdd: $usedTdd, usedIBasal: $usedIBasal, usedSgBasal: " +
"$usedSgBasal, usedUMax: $usedUMax, newTdd: $newTdd, newIBasal: $newIBasal, newSgBasal: $newSgBasal, newUMax: $newUMax"
)
val newRecord = pumpSync.createOrUpdateTotalDailyDose(
timestamp = timestamp,
bolusAmount = (tdd - basalTdd).toDouble(),
basalAmount = basalTdd.toDouble(),
totalAmount = tdd.toDouble(),
pumpId = timestamp,
pumpType = medtrumPump.pumpType(),
pumpSerial = medtrumPump.pumpSN.toString(radix = 16)
)
aapsLogger.debug(
LTag.PUMPCOMM,
"handleBasalStatusUpdate from record: ${newRecordInfo(newRecord)}EVENT TDD: ${dateUtil.dateAndTimeString(timestamp)} ($timestamp) " +
"TDD: $tdd, BasalTDD: $basalTdd, BolusTDD: ${tdd - basalTdd}"
)
}
private fun newRecordInfo(newRecord: Boolean): String {
return "${if (newRecord) "**NEW** " else ""}"
}
}

View file

@ -109,7 +109,6 @@ class NotificationPacket(val injector: HasAndroidInjector) {
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
}

View file

@ -281,13 +281,8 @@ class MedtrumService : DaggerService(), BLECommCallback {
}
fun clearAlarms(): Boolean {
var result = true
if (medtrumPump.pumpState in listOf(
MedtrumPumpState.PAUSED,
MedtrumPumpState.HOURLY_MAX_SUSPENDED,
MedtrumPumpState.DAILY_MAX_SUSPENDED
)
) {
var result = loadEvents() // Make sure we have all events before clearing alarms
if (result && medtrumPump.pumpState.isSuspendedByPump()) {
when (medtrumPump.pumpState) {
MedtrumPumpState.HOURLY_MAX_SUSPENDED -> {
result = sendPacketAndGetResponse(ClearPumpAlarmPacket(injector, ALARM_HOURLY_MAX_CLEAR_CODE))

View file

@ -119,9 +119,7 @@ class MedtrumOverviewViewModel @Inject constructor(
medtrumPump.pumpStateFlow.collect { state ->
aapsLogger.debug(LTag.PUMP, "MedtrumViewModel pumpStateFlow: $state")
_canDoResetAlarms.postValue(
medtrumPump.pumpState in listOf(
MedtrumPumpState.PAUSED, MedtrumPumpState.HOURLY_MAX_SUSPENDED, MedtrumPumpState.DAILY_MAX_SUSPENDED
)
medtrumPump.pumpState.isSuspendedByPump()
)
updateGUI()

View file

@ -2,19 +2,31 @@ package info.nightscout.pump.medtrum.comm.packets
import dagger.android.AndroidInjector
import dagger.android.HasAndroidInjector
import info.nightscout.interfaces.pump.DetailedBolusInfo
import info.nightscout.interfaces.pump.DetailedBolusInfoStorage
import info.nightscout.interfaces.pump.PumpSync
import info.nightscout.pump.medtrum.MedtrumTestBase
import info.nightscout.shared.utils.T
import org.junit.jupiter.api.Assertions
import org.junit.jupiter.api.Test
import org.mockito.Mock
import org.mockito.Mockito
import org.mockito.Mockito.mock
class GetRecordPacketTest : MedtrumTestBase() {
/** Test packet specific behavior */
@Mock private lateinit var detailedBolusInfoStorage: DetailedBolusInfoStorage
private val packetInjector = HasAndroidInjector {
AndroidInjector {
if (it is GetRecordPacket) {
it.aapsLogger = aapsLogger
it.medtrumPump = medtrumPump
it.pumpSync = pumpSync
it.detailedBolusInfoStorage = detailedBolusInfoStorage
it.dateUtil = dateUtil
}
}
}
@ -58,4 +70,296 @@ class GetRecordPacketTest : MedtrumTestBase() {
Assertions.assertEquals(false, result)
Assertions.assertEquals(true, packet.failed)
}
@Test fun handleResponseGivenBolusRecordWhenAndDetailedBolusInfoPresentThenExpectPumpSyncWithTempId() {
val data = byteArrayOf(47, 99, 10, 1, 0, 0, -86, 40, 1, -1, 38, 105, -77, 57, 56, 0, 29, 0, 1, 0, 0, 0, -82, -85, 62, 18, 22, 0, 22, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 76)
val timestamp = 1694631470000L
val bolusType = DetailedBolusInfo.BolusType.SMB
val amount = 1.1
// Mocks
val detailedBolusInfo: DetailedBolusInfo = mock(DetailedBolusInfo::class.java)
detailedBolusInfo.timestamp = timestamp // Wierd way to mock but this is a @JvmField
Mockito.`when`(detailedBolusInfo.bolusType).thenReturn(bolusType)
Mockito.`when`(detailedBolusInfoStorage.findDetailedBolusInfo(timestamp, amount)).thenReturn(detailedBolusInfo)
// Call
val packet = GetRecordPacket(packetInjector, 0)
val result = packet.handleResponse(data)
// Expected values
Mockito.verify(pumpSync).syncBolusWithTempId(
timestamp = timestamp,
amount = amount,
temporaryId = timestamp,
type = bolusType,
pumpId = timestamp,
pumpType = medtrumPump.pumpType(),
pumpSerial = medtrumPump.pumpSN.toString(radix = 16)
)
Assertions.assertEquals(true, result)
Assertions.assertEquals(false, packet.failed)
Assertions.assertEquals(timestamp, medtrumPump.lastBolusTime)
Assertions.assertEquals(amount, medtrumPump.lastBolusAmount, 0.01)
}
@Test fun handleResponseGivenBolusRecordWhenAndNoDetailedBolusInfoPresentThenExpectPumpSyncWithPumpId() {
val data = byteArrayOf(47, 99, 10, 1, 0, 0, -86, 40, 1, -1, 38, 105, -77, 57, 56, 0, 29, 0, 1, 0, 0, 0, -82, -85, 62, 18, 22, 0, 22, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 76)
val timestamp = 1694631470000L
val amount = 1.1
// Mocks
Mockito.`when`(detailedBolusInfoStorage.findDetailedBolusInfo(timestamp, amount)).thenReturn(null)
// Call
val packet = GetRecordPacket(packetInjector, 0)
val result = packet.handleResponse(data)
// Expected values
Mockito.verify(pumpSync).syncBolusWithPumpId(
timestamp = timestamp,
amount = amount,
type = null,
pumpId = timestamp,
pumpType = medtrumPump.pumpType(),
pumpSerial = medtrumPump.pumpSN.toString(radix = 16)
)
Assertions.assertEquals(true, result)
Assertions.assertEquals(false, packet.failed)
Assertions.assertEquals(timestamp, medtrumPump.lastBolusTime)
Assertions.assertEquals(amount, medtrumPump.lastBolusAmount, 0.01)
}
@Test fun handleResponseGivenExtendedBolusRecordThenExpectPumpSyncWithPumpId() {
val data = byteArrayOf(47, 99, 5, 1, 0, 0, -86, 40, 1, -1, 38, 105, -77, 57, 63, 0, 6, 0, 2, 0, 0, 0, -22, -123, 67, 18, 0, 0, 0, 0, 25, 0, 30, 0, 25, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -124)
val timestamp = 1694949482000
val amount = 1.25
val duration = T.mins(30).msecs()
// Call
val packet = GetRecordPacket(packetInjector, 0)
val result = packet.handleResponse(data)
// Expected values
Mockito.verify(pumpSync).syncExtendedBolusWithPumpId(
timestamp = timestamp,
amount = amount,
duration = duration,
isEmulatingTB = false,
pumpId = timestamp,
pumpType = medtrumPump.pumpType(),
pumpSerial = medtrumPump.pumpSN.toString(radix = 16)
)
Assertions.assertEquals(true, result)
Assertions.assertEquals(false, packet.failed)
}
@Test fun handleResponseGivenComboBolusRecordWhenAndNoDetailedBolusInfoPresentThenExpectPumpSyncWithPumpId() {
val data = byteArrayOf(47, 99, 5, 1, 0, 0, -86, 40, 1, -1, 38, 105, -77, 57, 63, 0, 8, 0, 3, 0, 0, 0, 111, -110, 67, 18, 40, 0, 40, 0, 20, 0, 30, 0, 20, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -122)
val timestamp = 1694952687000L
val amountDirect = 2.0
val amountExtended = 1.0
val duration = T.mins(30).msecs()
// Mocks
Mockito.`when`(detailedBolusInfoStorage.findDetailedBolusInfo(timestamp, amountDirect)).thenReturn(null)
// Call
val packet = GetRecordPacket(packetInjector, 0)
val result = packet.handleResponse(data)
// Expected values
Mockito.verify(pumpSync).syncBolusWithPumpId(
timestamp = timestamp,
amount = amountDirect,
type = null,
pumpId = timestamp,
pumpType = medtrumPump.pumpType(),
pumpSerial = medtrumPump.pumpSN.toString(radix = 16)
)
Mockito.verify(pumpSync).syncExtendedBolusWithPumpId(
timestamp = timestamp,
amount = amountExtended,
duration = duration,
isEmulatingTB = false,
pumpId = timestamp,
pumpType = medtrumPump.pumpType(),
pumpSerial = medtrumPump.pumpSN.toString(radix = 16)
)
Assertions.assertEquals(true, result)
Assertions.assertEquals(false, packet.failed)
}
@Test fun handleResponseGivenBasalRecordWhenAbsoluteTempThenExpectPumpSync() {
val data = byteArrayOf(35, 99, 7, 1, 0, 0, -86, 28, 2, -1, 38, 105, -77, 57, 56, 0, 30, 0, -85, -85, 62, 18, -34, -84, 62, 18, 6, 0, 69, 0, 6, 0, 69, 0, -125)
val startTime = 1694631467000
val endTime = 1694631774000
val rate = 3.45
val duration = endTime - startTime
// Call
val packet = GetRecordPacket(packetInjector, 0)
val result = packet.handleResponse(data)
// Expected values
Mockito.verify(pumpSync).syncTemporaryBasalWithPumpId(
timestamp = startTime,
rate = rate,
duration = duration,
isAbsolute = true,
type = PumpSync.TemporaryBasalType.NORMAL,
pumpId = startTime,
pumpType = medtrumPump.pumpType(),
pumpSerial = medtrumPump.pumpSN.toString(radix = 16)
)
Assertions.assertEquals(true, result)
Assertions.assertEquals(false, packet.failed)
}
@Test fun handleResponseGivenBasalRecordWhenRelativeTempThenExpectPumpSync() {
val data = byteArrayOf(35, 99, 7, 1, 0, 0, -86, 28, 2, -1, 38, 105, -77, 57, 63, 0, 4, 0, -116, -123, 67, 18, 81, -119, 67, 18, 7, 0, 4, 0, 1, 0, -56, 0, 1)
val startTime = 1694949388000
val endTime = 1694950353000
val rate = 200.0
val duration = endTime - startTime
// Call
val packet = GetRecordPacket(packetInjector, 0)
val result = packet.handleResponse(data)
// Expected values
Mockito.verify(pumpSync).syncTemporaryBasalWithPumpId(
timestamp = startTime,
rate = rate,
duration = duration,
isAbsolute = false,
type = PumpSync.TemporaryBasalType.NORMAL,
pumpId = startTime,
pumpType = medtrumPump.pumpType(),
pumpSerial = medtrumPump.pumpSN.toString(radix = 16)
)
Assertions.assertEquals(true, result)
Assertions.assertEquals(false, packet.failed)
}
@Test fun handleResponseGivenBasalRecordWhenSuspendThenExpectPumpSync() {
// Note: This is not a real response as I was unable to get this response from any of my pumpbases, but it can theoretically happen
val data = byteArrayOf(35, 99, 7, 1, 0, 0, -86, 28, 2, -1, -39, -7, 118, -86, -85, 1, 8, 0, -4, 116, -16, 17, 21, 125, -16, 17, 18, 0, 0, 0, 0, 0, 0, 0, 125)
val startTime = 1689505660000
val endTime = 1689507733000
val rate = 0.0
val duration = endTime - startTime
// Call
val packet = GetRecordPacket(packetInjector, 0)
val result = packet.handleResponse(data)
// Expected values
Mockito.verify(pumpSync).syncTemporaryBasalWithPumpId(
timestamp = startTime,
rate = rate,
duration = duration,
isAbsolute = true,
type = PumpSync.TemporaryBasalType.PUMP_SUSPEND,
pumpId = startTime,
pumpType = medtrumPump.pumpType(),
pumpSerial = medtrumPump.pumpSN.toString(radix = 16)
)
Assertions.assertEquals(true, result)
Assertions.assertEquals(false, packet.failed)
}
@Test fun handleResponseGivenBasalRecordWhenStandardAndSuspendEndReasonThenExpectPumpSync() {
val data = byteArrayOf(35, 99, 8, 1, 0, 0, -86, 28, 2, -1, -39, -7, 118, -86, -85, 1, 4, 0, -117, 113, -16, 17, 9, 116, -16, 17, 1, 4, 10, 0, 2, 0, 0, 0, 57)
val endTime = 1689505417000
// Call
val packet = GetRecordPacket(packetInjector, 0)
val result = packet.handleResponse(data)
// Just check the pumpSync here, rest of the behavoir of medtrumPump is tested in MedtrumPumpTest
// Expected values
Mockito.verify(pumpSync).syncTemporaryBasalWithPumpId(
timestamp = endTime,
rate = 0.0,
duration = T.mins(4800L).msecs(),
isAbsolute = true,
type = PumpSync.TemporaryBasalType.PUMP_SUSPEND,
pumpId = endTime,
pumpType = medtrumPump.pumpType(),
pumpSerial = medtrumPump.pumpSN.toString(radix = 16)
)
Assertions.assertEquals(true, result)
Assertions.assertEquals(false, packet.failed)
}
@Test fun handleResponseGivenBasalRecordWhenTempAndSuspendEndReasonThenExpectPumpSync() {
val data = byteArrayOf(35, 99, 8, 1, 0, 0, -86, 28, 2, -1, -39, -7, 118, -86, -82, 1, 5, 0, 75, 24, -14, 17, 44, 27, -14, 17, 6, 4, 16, 0, 3, 0, 16, 0, -73)
val endTime = 1689613740000
// Call
val packet = GetRecordPacket(packetInjector, 0)
val result = packet.handleResponse(data)
// Just check the pumpSync here, rest of the behavoir of medtrumPump is tested in MedtrumPumpTest
// Expected values
Mockito.verify(pumpSync).syncTemporaryBasalWithPumpId(
timestamp = endTime,
rate = 0.0,
duration = T.mins(4800L).msecs(),
isAbsolute = true,
type = PumpSync.TemporaryBasalType.PUMP_SUSPEND,
pumpId = endTime,
pumpType = medtrumPump.pumpType(),
pumpSerial = medtrumPump.pumpSN.toString(radix = 16)
)
Assertions.assertEquals(true, result)
Assertions.assertEquals(false, packet.failed)
}
@Test fun handleResponseGivenTDDRecordThenExpectPumpSync() {
val data = byteArrayOf(
87, 99, 8, 1, 0, 0, -86, 80, 9, -1, 38, 105, -77, 57, 56, 0, 82, 0, -32, -124, 61, 18, 120, 0, -120, 5, 0, 0, 0, 0, -102, -103, 84, 66, 0, 0,
-120, 65, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 102, -26, -128, 66, 58, -52, -75, 63, 0, 0, -16, 66, -49, -9, -13, 63, -103, -103, 121, 66, 55,
-75, -84, 63, 0, 0, -16, 66, -49, -9, -13, 63, 0, 0, 0, 0, -128
)
val timestamp = 1694556000000L
val tdd = 53.150001525878906
val basalTdd = 17.0
val bolusTdd = tdd - basalTdd
// Call
val packet = GetRecordPacket(packetInjector, 0)
val result = packet.handleResponse(data)
// Expected values
Mockito.verify(pumpSync).createOrUpdateTotalDailyDose(
timestamp = timestamp,
bolusAmount = bolusTdd,
basalAmount = basalTdd,
totalAmount = tdd,
pumpId = timestamp,
pumpType = medtrumPump.pumpType(),
pumpSerial = medtrumPump.pumpSN.toString(radix = 16)
)
Assertions.assertEquals(true, result)
Assertions.assertEquals(false, packet.failed)
}
}