Packet implementation

This commit is contained in:
jbr7rr 2023-03-24 19:36:58 +01:00
parent 90392b2452
commit db409f5b7f
40 changed files with 781 additions and 120 deletions

View file

@ -0,0 +1,109 @@
package info.nightscout.pump.medtrum
import info.nightscout.interfaces.Constants
import info.nightscout.interfaces.profile.Instantiator
import info.nightscout.interfaces.profile.Profile
import info.nightscout.interfaces.profile.ProfileStore
import info.nightscout.interfaces.pump.PumpSync
import info.nightscout.interfaces.pump.defs.PumpType
import info.nightscout.pump.medtrum.comm.enums.AlarmSetting
import info.nightscout.pump.medtrum.comm.enums.MedtrumPumpState
import info.nightscout.pump.medtrum.extension.toByteArray
import info.nightscout.rx.logging.AAPSLogger
import info.nightscout.rx.logging.LTag
import info.nightscout.shared.sharedPreferences.SP
import info.nightscout.shared.utils.DateUtil
import info.nightscout.shared.utils.T
import javax.inject.Inject
import javax.inject.Singleton
import kotlin.math.round
@Singleton
class MedtrumPump @Inject constructor(
private val aapsLogger: AAPSLogger,
private val sp: SP,
private val dateUtil: DateUtil,
private val instantiator: Instantiator
) {
enum class PatchActivationState(val state: Int) {
NONE(0),
IDLE(1),
ACTIVATING(2),
ACTIVATED(3),
DEACTIVATING(4),
DEACTIVATED(5),
ERROR(6)
}
// Pump state and parameters
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!
// Pump history
// Last basal status update
var lastBasalType = 0
var lastBasalRate = 0.0
var lastBasalSequence = 0
var lastBasalPatchId = 0
var lastBasalStartTime = 0L
// Last stop status update
var lastStopSequence = 0
var lastStopPatchId = 0
fun buildMedtrumProfileArray(nsProfile: Profile): ByteArray? {
val list = nsProfile.getBasalValues()
var basals = byteArrayOf()
for (item in list) {
val rate = round(item.value / 0.05).toInt()
val time = item.timeAsSeconds / 60
if (rate > 0xFFF || time > 0xFFF) {
aapsLogger.error(LTag.PUMP, "buildMedtrumProfileArray: rate or time too large: $rate, $time")
return null
}
basals += ((rate shl 12) + time).toByteArray(3)
aapsLogger.debug(LTag.PUMP, "buildMedtrumProfileArray: value: ${item.value} time: ${item.timeAsSeconds}")
}
return (list.size).toByteArray(1) + basals
}
fun handleBasalStatusUpdate(basalType: Int, basalValue: Double, basalSequence: Int, basalPatchId: Int, basalStartTime: Long) {
handleBasalStatusUpdate(basalType, basalValue, basalSequence, basalPatchId, basalStartTime, dateUtil.now())
}
fun handleBasalStatusUpdate(basalType: Int, basalRate: Double, basalSequence: Int, basalPatchId: Int, basalStartTime: Long, receivedTime: Long) {
aapsLogger.debug(
LTag.PUMP, "handleBasalStatusUpdate: basalType: $basalType basalValue: $basalRate basalSequence: $basalSequence basalPatchId: $basalPatchId basalStartTime: $basalStartTime " +
"receivedTime: $receivedTime"
)
lastBasalType = basalType
lastBasalRate = basalRate
lastBasalSequence = basalSequence
lastBasalPatchId = basalPatchId
lastBasalStartTime = basalStartTime
}
fun handleStopStatusUpdate(stopSequence: Int, stopPatchId: Int) {
aapsLogger.debug(LTag.PUMP, "handleStopStatusUpdate: stopSequence: $stopSequence stopPatchId: $stopPatchId")
lastStopSequence = stopSequence
lastStopPatchId = stopPatchId
}
}

View file

@ -0,0 +1,12 @@
package info.nightscout.pump.medtrum.comm.enums
enum class AlarmSetting(val code: Byte) {
LIGHT_VIBRATE_AND_BEEP(0),
LIGHT_AND_VIBRATE(1),
LIGHT_AND_BEEP(2),
LIGHT_ONLY(3),
VIBRATE_AND_BEEP(4),
VIBRATE_ONLY(5),
BEEP_ONLY(6),
NONE(7)
}

View file

@ -1,4 +1,4 @@
package info.nightscout.pump.medtrum.comm.packets package info.nightscout.pump.medtrum.comm.enums
enum class CommandType(val code: Byte) { enum class CommandType(val code: Byte) {
SYNCHRONIZE(3), SYNCHRONIZE(3),

View file

@ -0,0 +1,29 @@
package info.nightscout.pump.medtrum.comm.enums
enum class MedtrumPumpState(val state: Byte) {
NONE(0),
IDLE(1),
DELIVERING(2),
PRIMING(3),
PRIMED(4),
EJECTING(5),
EJECTED(6),
ACTIVE(32),
ACTIVE_ALT(33),
LOWBG_SUSPENDED(64),
LOWBG_SUSPENDED2(65),
AUTO_SUSPENDED(66),
HMAX_SUSPENDED(67),
DMAX_SUSPENDED(68),
SUSPENDED(69),
PAUSED(70),
OCCLUSION(96),
EXPIRED(97),
RESERVOIR_EMPTY(98),
PATCH_FAULT(99),
PATCH_FAULT2(100),
BASE_FAULT(101),
BATTERY_OUT(102),
NO_CALIBRATION(103),
STOPPED(128.toByte())
}

View file

@ -1,23 +1,88 @@
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.comm.packets.CommandType.ACTIVATE import info.nightscout.pump.medtrum.MedtrumPump
import info.nightscout.pump.medtrum.comm.enums.CommandType.ACTIVATE
import info.nightscout.pump.medtrum.extension.toByteArray
import info.nightscout.pump.medtrum.extension.toByte
import info.nightscout.interfaces.stats.TddCalculator
import info.nightscout.pump.medtrum.extension.toInt
import info.nightscout.pump.medtrum.extension.toLong
import info.nightscout.pump.medtrum.util.MedtrumTimeUtil
import javax.inject.Inject
import kotlin.math.round
class ActivatePacket(injector: HasAndroidInjector) : MedtrumPacket(injector) { class ActivatePacket(injector: HasAndroidInjector, private val basalProfile: ByteArray) : MedtrumPacket(injector) {
@Inject lateinit var medtrumPump: MedtrumPump
@Inject lateinit var tddCalculator: TddCalculator
companion object {
private const val RESP_PATCH_ID_START = 6
private const val RESP_PATCH_ID_END = RESP_PATCH_ID_START + 4
private const val RESP_TIME_START = 10
private const val RESP_TIME_END = RESP_TIME_START + 4
private const val RESP_BASAL_TYPE_START = 14
private const val RESP_BASAL_TYPE_END = RESP_BASAL_TYPE_START + 1
private const val RESP_BASAL_VALUE_START = 15
private const val RESP_BASAL_VALUE_END = RESP_BASAL_VALUE_START + 2
private const val RESP_BASAL_SEQUENCE_START = 17
private const val RESP_BASAL_SEQUENCE_END = RESP_BASAL_SEQUENCE_START + 2
private const val RESP_BASAL_PATCH_ID_START = 19
private const val RESP_BASAL_PATCH_ID_END = RESP_BASAL_PATCH_ID_START + 2
private const val RESP_BASAL_START_TIME_START = 21
private const val RESP_BASAL_START_TIME_END = RESP_BASAL_START_TIME_START + 4
}
init { init {
opCode = ACTIVATE.code opCode = ACTIVATE.code
expectedMinRespLength = RESP_BASAL_START_TIME_END
} }
override fun getRequest(): ByteArray { override fun getRequest(): ByteArray {
// TODO get activation commands /**
return byteArrayOf(opCode) * byte 0: opCode
* byte 1: autoSuspendEnable // Value for auto mode, not used for AAPS
* byte 2: autoSuspendTime // Value for auto mode, not used for AAPS
* byte 3: expirationTimer // Expiration timer, 0 = no expiration 1 = 12 hour reminder and expiration after 3 days
* byte 4: alarmSetting // See AlarmSetting
* 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 14: 1 // Always 1
* bytes 15 - end // Basal profile > see MedtrumPump
*/
val patchExpiration: Byte = medtrumPump.desiredPatchExpiration.toByte()
val alarmSetting: Byte = medtrumPump.desiredAlarmSetting
val hourlyMaxInsulin: Int = round(medtrumPump.desiredHourlyMaxInsulin / 0.05).toInt()
val dailyMaxInsulin: Int = round(medtrumPump.desiredDailyMaxInsulin / 0.05).toInt()
val currentTDD: Double = tddCalculator.calculateToday()?.totalAmount?.div(0.05) ?: 0.0
return byteArrayOf(opCode) + 0.toByteArray(2) + patchExpiration + alarmSetting + 0.toByteArray(3) + hourlyMaxInsulin.toByteArray(2) + dailyMaxInsulin.toByteArray(2) + currentTDD.toInt()
.toByteArray(2) + 1.toByte() + basalProfile
} }
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 val medtrumTimeUtil = MedtrumTimeUtil()
val patchId = data.copyOfRange(RESP_PATCH_ID_START, RESP_PATCH_ID_END).toLong()
val time = medtrumTimeUtil.convertPumpTimeToSystemTimeSeconds(data.copyOfRange(RESP_TIME_START, RESP_TIME_END).toLong())
val basalType = data.copyOfRange(RESP_BASAL_TYPE_START, RESP_BASAL_TYPE_END).toInt()
val basalValue = data.copyOfRange(RESP_BASAL_VALUE_START, RESP_BASAL_VALUE_END).toInt() * 0.05
val basalSequence = data.copyOfRange(RESP_BASAL_SEQUENCE_START, RESP_BASAL_SEQUENCE_END).toInt()
val basalPatchId = data.copyOfRange(RESP_BASAL_PATCH_ID_START, RESP_BASAL_PATCH_ID_END).toInt()
val basalStartTime = medtrumTimeUtil.convertPumpTimeToSystemTimeSeconds(data.copyOfRange(RESP_BASAL_START_TIME_START, RESP_BASAL_START_TIME_END).toLong())
medtrumPump.patchId = patchId
medtrumPump.lastTimeReceivedFromPump = time
medtrumPump.handleBasalStatusUpdate(basalType, basalValue, basalSequence, basalPatchId, basalStartTime, time)
} }
return success return success

View file

@ -1,7 +1,7 @@
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.comm.packets.CommandType.AUTH_REQ import info.nightscout.pump.medtrum.comm.enums.CommandType.AUTH_REQ
import info.nightscout.pump.medtrum.encryption.Crypt import info.nightscout.pump.medtrum.encryption.Crypt
import info.nightscout.pump.medtrum.extension.toByteArray import info.nightscout.pump.medtrum.extension.toByteArray
import info.nightscout.pump.medtrum.extension.toInt import info.nightscout.pump.medtrum.extension.toInt

View file

@ -1,7 +1,7 @@
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.comm.packets.CommandType.CANCEL_BOLUS import info.nightscout.pump.medtrum.comm.enums.CommandType.CANCEL_BOLUS
class CancelBolusPacket(injector: HasAndroidInjector) : MedtrumPacket(injector) { class CancelBolusPacket(injector: HasAndroidInjector) : MedtrumPacket(injector) {
@ -10,7 +10,11 @@ class CancelBolusPacket(injector: HasAndroidInjector) : MedtrumPacket(injector)
} }
override fun getRequest(): ByteArray { override fun getRequest(): ByteArray {
// TODO: Get bolus type // Bolus types:
return byteArrayOf(opCode) // 1 = normal
// 2 = Extended
// 3 = Combi
val bolusType: Byte = 1 // Only support for normal bolus for now
return byteArrayOf(opCode) + bolusType
} }
} }

View file

@ -1,18 +1,46 @@
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.comm.packets.CommandType.CANCEL_TEMP_BASAL import info.nightscout.pump.medtrum.MedtrumPump
import info.nightscout.pump.medtrum.comm.enums.CommandType.CANCEL_TEMP_BASAL
import info.nightscout.pump.medtrum.extension.toInt
import info.nightscout.pump.medtrum.extension.toLong
import info.nightscout.pump.medtrum.util.MedtrumTimeUtil
import javax.inject.Inject
class CancelTempBasalPacket(injector: HasAndroidInjector) : MedtrumPacket(injector) { class CancelTempBasalPacket(injector: HasAndroidInjector) : MedtrumPacket(injector) {
@Inject lateinit var medtrumPump: MedtrumPump
companion object {
private const val RESP_BASAL_TYPE_START = 6
private const val RESP_BASAL_TYPE_END = RESP_BASAL_TYPE_START + 1
private const val RESP_BASAL_RATE_START = RESP_BASAL_TYPE_END
private const val RESP_BASAL_RATE_END = RESP_BASAL_RATE_START + 2
private const val RESP_BASAL_SEQUENCE_START = RESP_BASAL_RATE_END
private const val RESP_BASAL_SEQUENCE_END = RESP_BASAL_SEQUENCE_START + 2
private const val RESP_BASAL_PATCH_ID_START = RESP_BASAL_SEQUENCE_END
private const val RESP_BASAL_PATCH_ID_END = RESP_BASAL_PATCH_ID_START + 2
private const val RESP_BASAL_START_TIME_START = RESP_BASAL_PATCH_ID_END
private const val RESP_BASAL_START_TIME_END = RESP_BASAL_START_TIME_START + 4
}
init { init {
opCode = CANCEL_TEMP_BASAL.code opCode = CANCEL_TEMP_BASAL.code
expectedMinRespLength = RESP_BASAL_START_TIME_END
} }
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 Save basal val basalType = data.copyOfRange(RESP_BASAL_TYPE_START, RESP_BASAL_TYPE_END).toInt()
val basalRate = data.copyOfRange(RESP_BASAL_RATE_START, RESP_BASAL_RATE_END).toInt() * 0.05
val basalSequence = data.copyOfRange(RESP_BASAL_SEQUENCE_START, RESP_BASAL_SEQUENCE_END).toInt()
val basalPatchId = data.copyOfRange(RESP_BASAL_PATCH_ID_START, RESP_BASAL_PATCH_ID_END).toInt()
val basalStartTime = MedtrumTimeUtil().convertPumpTimeToSystemTimeSeconds(data.copyOfRange(RESP_BASAL_START_TIME_START, RESP_BASAL_START_TIME_END).toLong())
medtrumPump.handleBasalStatusUpdate(basalType, basalRate, basalSequence, basalPatchId, basalStartTime)
} }
return success return success
} }

View file

@ -1,7 +1,7 @@
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.comm.packets.CommandType.GET_DEVICE_TYPE import info.nightscout.pump.medtrum.comm.enums.CommandType.GET_DEVICE_TYPE
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

View file

@ -1,7 +1,7 @@
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.comm.packets.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
class GetTimePacket(injector: HasAndroidInjector) : MedtrumPacket(injector) { class GetTimePacket(injector: HasAndroidInjector) : MedtrumPacket(injector) {

View file

@ -1,7 +1,7 @@
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.comm.packets.CommandType.POLL_PATCH import info.nightscout.pump.medtrum.comm.enums.CommandType.POLL_PATCH
class PollPatchPacket(injector: HasAndroidInjector) : MedtrumPacket(injector) { class PollPatchPacket(injector: HasAndroidInjector) : MedtrumPacket(injector) {

View file

@ -1,7 +1,7 @@
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.comm.packets.CommandType.PRIME import info.nightscout.pump.medtrum.comm.enums.CommandType.PRIME
class PrimePacket(injector: HasAndroidInjector) : MedtrumPacket(injector) { class PrimePacket(injector: HasAndroidInjector) : MedtrumPacket(injector) {

View file

@ -1,8 +1,7 @@
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.comm.packets.CommandType.READ_BOLUS_STATE import info.nightscout.pump.medtrum.comm.enums.CommandType.READ_BOLUS_STATE
import info.nightscout.pump.medtrum.extension.toInt
class ReadBolusStatePacket(injector: HasAndroidInjector) : MedtrumPacket(injector) { class ReadBolusStatePacket(injector: HasAndroidInjector) : MedtrumPacket(injector) {
@ -10,7 +9,7 @@ class ReadBolusStatePacket(injector: HasAndroidInjector) : MedtrumPacket(injecto
companion object { companion object {
private const val RESP_BOLUS_DATA_START = 6 // TODO: check this private const val RESP_BOLUS_DATA_START = 6
} }
init { init {

View file

@ -1,16 +1,20 @@
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.comm.packets.CommandType.SET_BASAL_PROFILE import info.nightscout.pump.medtrum.MedtrumPump
import info.nightscout.pump.medtrum.comm.enums.CommandType.SET_BASAL_PROFILE
import javax.inject.Inject
class SetBasalProfilePacket(injector: HasAndroidInjector) : MedtrumPacket(injector) { class SetBasalProfilePacket(injector: HasAndroidInjector, private val basalProfile: ByteArray) : MedtrumPacket(injector) {
@Inject lateinit var medtrumPump: MedtrumPump
init { init {
opCode = SET_BASAL_PROFILE.code opCode = SET_BASAL_PROFILE.code
} }
override fun getRequest(): ByteArray { override fun getRequest(): ByteArray {
// TODO get basal profile settings val basalType: Byte = 1 // Fixed to normal basal
return byteArrayOf(opCode) return byteArrayOf(opCode) + basalType + basalProfile
} }
} }

View file

@ -1,9 +1,7 @@
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.comm.packets.CommandType.SET_BOLUS_MOTOR import info.nightscout.pump.medtrum.comm.enums.CommandType.SET_BOLUS_MOTOR
import info.nightscout.pump.medtrum.extension.toByteArray
import info.nightscout.pump.medtrum.util.MedtrumTimeUtil
class SetBolusMotorPacket(injector: HasAndroidInjector) : MedtrumPacket(injector) { class SetBolusMotorPacket(injector: HasAndroidInjector) : MedtrumPacket(injector) {

View file

@ -1,16 +1,23 @@
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.comm.packets.CommandType.SET_BOLUS import info.nightscout.pump.medtrum.comm.enums.CommandType.SET_BOLUS
import info.nightscout.pump.medtrum.extension.toByteArray
import kotlin.math.round
class SetBolusPacket(injector: HasAndroidInjector) : MedtrumPacket(injector) { class SetBolusPacket(injector: HasAndroidInjector, private val insulin: Double) : MedtrumPacket(injector) {
init { init {
opCode = SET_BOLUS.code opCode = SET_BOLUS.code
} }
override fun getRequest(): ByteArray { override fun getRequest(): ByteArray {
// TODO get bolus settings // Bolus types:
return byteArrayOf(opCode) + 0.toByte() // 1 = normal
// 2 = Extended
// 3 = Combi
val bolusType: Byte = 1 // Only support for normal bolus for now
val bolusAmount: Int = round(insulin / 0.05).toInt()
return byteArrayOf(opCode) + bolusType + bolusAmount.toByteArray(2) + 0.toByte()
} }
} }

View file

@ -1,17 +1,40 @@
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.comm.packets.CommandType.SET_PATCH import info.nightscout.pump.medtrum.MedtrumPump
import info.nightscout.pump.medtrum.comm.enums.CommandType.SET_PATCH
import info.nightscout.pump.medtrum.extension.toByte
import info.nightscout.pump.medtrum.extension.toByteArray import info.nightscout.pump.medtrum.extension.toByteArray
import javax.inject.Inject
import kotlin.math.round
class SetPatchPacket(injector: HasAndroidInjector) : MedtrumPacket(injector) { class SetPatchPacket(injector: HasAndroidInjector) : MedtrumPacket(injector) {
@Inject lateinit var medtrumPump: MedtrumPump
init { init {
opCode = SET_PATCH.code opCode = SET_PATCH.code
} }
override fun getRequest(): ByteArray { override fun getRequest(): ByteArray {
// TODO get patch settings /**
return byteArrayOf(opCode) * byte 0: opCode
* byte 1: alarmSetting // See AlarmSetting
* byte 2-3: hourlyMaxInsulin // Max hourly dose of insulin not used for now, divided by 0.05
* byte 4-5: dailyMaxSet // Max daily dose of insulin not used for now, divided by 0.05
* byte 6: expirationTimer // Expiration timer, 0 = no expiration 1 = 12 hour reminder and expiration
* byte 7: autoSuspendEnable // Value for auto mode, not used for AAPS
* byte 8: autoSuspendTime // Value for auto mode, not used for AAPS
* byte 9: lowSuspend // Value for auto mode, not used for AAPS
* byte 10: predictiveLowSuspend // Value for auto mode, not used for AAPS
* byte 11: predictiveLowSuspendRange // Value for auto mode, not used for AAPS
*/
val alarmSetting: Byte = medtrumPump.desiredAlarmSetting
val hourlyMaxInsulin: Int = round(medtrumPump.desiredHourlyMaxInsulin / 0.05).toInt()
val dailyMaxInsulin: Int = round(medtrumPump.desiredDailyMaxInsulin / 0.05).toInt()
val patchExpiration: Byte = medtrumPump.desiredPatchExpiration.toByte()
return byteArrayOf(opCode) + alarmSetting + hourlyMaxInsulin.toByteArray(2) + dailyMaxInsulin.toByteArray(2) + patchExpiration + 0.toByteArray(5)
} }
} }

View file

@ -1,24 +1,61 @@
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.comm.packets.CommandType.SET_TEMP_BASAL import info.nightscout.pump.medtrum.MedtrumPump
import info.nightscout.pump.medtrum.comm.enums.CommandType.SET_TEMP_BASAL
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 javax.inject.Inject
import kotlin.math.round
class SetTempBasalPacket(injector: HasAndroidInjector) : MedtrumPacket(injector) { class SetTempBasalPacket(injector: HasAndroidInjector, private val absoluteRate: Double, private val durationInMinutes: Int) : MedtrumPacket(injector) {
@Inject lateinit var medtrumPump: MedtrumPump
companion object {
private const val RESP_BASAL_TYPE_START = 6
private const val RESP_BASAL_TYPE_END = RESP_BASAL_TYPE_START + 1
private const val RESP_BASAL_RATE_START = RESP_BASAL_TYPE_END
private const val RESP_BASAL_RATE_END = RESP_BASAL_RATE_START + 2
private const val RESP_BASAL_SEQUENCE_START = RESP_BASAL_RATE_END
private const val RESP_BASAL_SEQUENCE_END = RESP_BASAL_SEQUENCE_START + 2
private const val RESP_BASAL_PATCH_ID_START = RESP_BASAL_SEQUENCE_END
private const val RESP_BASAL_PATCH_ID_END = RESP_BASAL_PATCH_ID_START + 2
private const val RESP_BASAL_START_TIME_START = RESP_BASAL_PATCH_ID_END
private const val RESP_BASAL_START_TIME_END = RESP_BASAL_START_TIME_START + 4
}
init { init {
opCode = SET_TEMP_BASAL.code opCode = SET_TEMP_BASAL.code
// TODO set expectedMinRespLength expectedMinRespLength = RESP_BASAL_START_TIME_END
} }
override fun getRequest(): ByteArray { override fun getRequest(): ByteArray {
// TODO get temp basal settings /**
return byteArrayOf(opCode) * byte 0: opCode
* byte 1: tempBasalType
* byte 2-3: tempBasalRate
* byte 4-5: tempBasalDuration
*/
val tempBasalType: Byte = 6 // Fixed to temp basal value for now
val tempBasalRate: Int = round(absoluteRate / 0.05).toInt()
val tempBasalDuration: Int = durationInMinutes
return byteArrayOf(opCode) + tempBasalType + tempBasalRate.toByteArray(2) + tempBasalDuration.toByteArray(2)
} }
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 Save basal val basalType = data.copyOfRange(RESP_BASAL_TYPE_START, RESP_BASAL_TYPE_END).toInt()
val basalRate = data.copyOfRange(RESP_BASAL_RATE_START, RESP_BASAL_RATE_END).toInt() * 0.05
val basalSequence = data.copyOfRange(RESP_BASAL_SEQUENCE_START, RESP_BASAL_SEQUENCE_END).toInt()
val basalPatchId = data.copyOfRange(RESP_BASAL_PATCH_ID_START, RESP_BASAL_PATCH_ID_END).toInt()
val basalStartTime = MedtrumTimeUtil().convertPumpTimeToSystemTimeSeconds(data.copyOfRange(RESP_BASAL_START_TIME_START, RESP_BASAL_START_TIME_END).toLong())
medtrumPump.handleBasalStatusUpdate(basalType, basalRate, basalSequence, basalPatchId, basalStartTime)
} }
return success return success
} }

View file

@ -1,7 +1,7 @@
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.comm.packets.CommandType.SET_TIME import info.nightscout.pump.medtrum.comm.enums.CommandType.SET_TIME
import info.nightscout.pump.medtrum.extension.toByteArray import info.nightscout.pump.medtrum.extension.toByteArray
import info.nightscout.pump.medtrum.util.MedtrumTimeUtil import info.nightscout.pump.medtrum.util.MedtrumTimeUtil

View file

@ -1,7 +1,7 @@
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.comm.packets.CommandType.SET_TIME_ZONE import info.nightscout.pump.medtrum.comm.enums.CommandType.SET_TIME_ZONE
import info.nightscout.pump.medtrum.extension.toByteArray import info.nightscout.pump.medtrum.extension.toByteArray
import info.nightscout.pump.medtrum.util.MedtrumTimeUtil import info.nightscout.pump.medtrum.util.MedtrumTimeUtil
import info.nightscout.shared.utils.DateUtil import info.nightscout.shared.utils.DateUtil

View file

@ -1,18 +1,35 @@
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.comm.packets.CommandType.STOP_PATCH import info.nightscout.pump.medtrum.MedtrumPump
import info.nightscout.pump.medtrum.extension.toInt
import info.nightscout.pump.medtrum.comm.enums.CommandType.STOP_PATCH
import javax.inject.Inject
class StopPatchPacket(injector: HasAndroidInjector) : MedtrumPacket(injector) { class StopPatchPacket(injector: HasAndroidInjector) : MedtrumPacket(injector) {
@Inject lateinit var medtrumPump: MedtrumPump
companion object {
private const val RESP_STOP_SEQUENCE_START = 6
private const val RESP_STOP_SEQUENCE_END = RESP_STOP_SEQUENCE_START + 2
private const val RESP_STOP_PATCH_ID_START = RESP_STOP_SEQUENCE_END
private const val RESP_STOP_PATCH_ID_END = RESP_STOP_PATCH_ID_START + 2
}
init { init {
opCode = STOP_PATCH.code opCode = STOP_PATCH.code
expectedMinRespLength = RESP_STOP_PATCH_ID_END
} }
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 val stopSequence = data.copyOfRange(RESP_STOP_SEQUENCE_START, RESP_STOP_SEQUENCE_END).toInt()
val stopPatchId = data.copyOfRange(RESP_STOP_PATCH_ID_START, RESP_STOP_PATCH_ID_END).toInt()
medtrumPump.handleStopStatusUpdate(stopSequence, stopPatchId)
} }
return success return success
} }

View file

@ -1,9 +1,8 @@
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.comm.packets.CommandType.SUBSCRIBE import info.nightscout.pump.medtrum.comm.enums.CommandType.SUBSCRIBE
import info.nightscout.pump.medtrum.extension.toByteArray import info.nightscout.pump.medtrum.extension.toByteArray
import info.nightscout.pump.medtrum.util.MedtrumTimeUtil
class SubscribePacket(injector: HasAndroidInjector) : MedtrumPacket(injector) { class SubscribePacket(injector: HasAndroidInjector) : MedtrumPacket(injector) {

View file

@ -1,7 +1,7 @@
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.comm.packets.CommandType.SYNCHRONIZE import info.nightscout.pump.medtrum.comm.enums.CommandType.SYNCHRONIZE
import info.nightscout.pump.medtrum.extension.toInt import info.nightscout.pump.medtrum.extension.toInt
class SynchronizePacket(injector: HasAndroidInjector) : MedtrumPacket(injector) { class SynchronizePacket(injector: HasAndroidInjector) : MedtrumPacket(injector) {

View file

@ -0,0 +1,8 @@
package info.nightscout.pump.medtrum.extension
fun Boolean.toByte(): Byte {
return if (this == true)
0x1
else
0x0
}

View file

@ -6,15 +6,15 @@ import java.time.Instant
class MedtrumTimeUtil { class MedtrumTimeUtil {
fun getCurrentTimePumpSeconds() : Long { fun getCurrentTimePumpSeconds() : Long {
val startInstant = Instant.parse("2020-01-01T00:00:00Z") val startInstant = Instant.parse("2014-01-01T00:00:00Z")
val currentInstant = Instant.now() val currentInstant = Instant.now()
return Duration.between(startInstant, currentInstant).seconds return Duration.between(startInstant, currentInstant).seconds
} }
fun convertPumpTimeToSystemTimeSeconds(pumpTime: Long) : Long { fun convertPumpTimeToSystemTimeSeconds(pumpTime: Long) : Long {
val startInstant = Instant.parse("2020-01-01T00:00:00Z") val startInstant = Instant.parse("2014-01-01T00:00:00Z")
val pumpInstant = startInstant.plusSeconds(pumpTime) val pumpInstant = startInstant.plusSeconds(pumpTime)
val epochInstant = Instant.EPOCH val epochInstant = Instant.EPOCH
return Duration.between(epochInstant, pumpInstant).seconds return Duration.between(epochInstant, pumpInstant).seconds
} }
} }

View file

@ -1,14 +1,16 @@
package info.nightscout.androidaps package info.nightscout.androidaps
import android.content.Context
import dagger.android.AndroidInjector import dagger.android.AndroidInjector
import dagger.android.HasAndroidInjector import dagger.android.HasAndroidInjector
import info.nightscout.core.extensions.pureProfileFromJson import info.nightscout.core.extensions.pureProfileFromJson
import info.nightscout.core.profile.ProfileSealed import info.nightscout.core.profile.ProfileSealed
import info.nightscout.core.utils.fabric.FabricPrivacy import info.nightscout.core.utils.fabric.FabricPrivacy
import info.nightscout.database.entities.EffectiveProfileSwitch
import info.nightscout.database.entities.embedments.InsulinConfiguration
import info.nightscout.interfaces.Config import info.nightscout.interfaces.Config
import info.nightscout.interfaces.iob.IobCobCalculator
import info.nightscout.interfaces.plugin.ActivePlugin import info.nightscout.interfaces.plugin.ActivePlugin
import info.nightscout.interfaces.profile.DefaultValueHelper
import info.nightscout.interfaces.profile.Profile
import info.nightscout.interfaces.profile.ProfileFunction import info.nightscout.interfaces.profile.ProfileFunction
import info.nightscout.interfaces.profile.ProfileStore import info.nightscout.interfaces.profile.ProfileStore
import info.nightscout.rx.bus.RxBus import info.nightscout.rx.bus.RxBus
@ -16,28 +18,34 @@ import info.nightscout.shared.interfaces.ResourceHelper
import info.nightscout.shared.utils.DateUtil import info.nightscout.shared.utils.DateUtil
import org.json.JSONObject import org.json.JSONObject
import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.BeforeEach
import org.mockito.ArgumentMatchers.anyDouble
import org.mockito.ArgumentMatchers.anyInt
import org.mockito.ArgumentMatchers.anyString
import org.mockito.Mock import org.mockito.Mock
import org.mockito.Mockito
import org.mockito.Mockito.`when`
import org.mockito.invocation.InvocationOnMock
@Suppress("SpellCheckingInspection") @Suppress("SpellCheckingInspection")
open class TestBaseWithProfile : TestBase() { open class TestBaseWithProfile : TestBase() {
@Mock lateinit var activePluginProvider: ActivePlugin @Mock lateinit var activePluginProvider: ActivePlugin
@Mock lateinit var rh: ResourceHelper @Mock lateinit var rh: ResourceHelper
@Mock lateinit var iobCobCalculator: IobCobCalculator
@Mock lateinit var fabricPrivacy: FabricPrivacy @Mock lateinit var fabricPrivacy: FabricPrivacy
@Mock lateinit var profileFunction: ProfileFunction @Mock lateinit var profileFunction: ProfileFunction
@Mock lateinit var defaultValueHelper: DefaultValueHelper
@Mock lateinit var dateUtil: DateUtil
@Mock lateinit var config: Config @Mock lateinit var config: Config
@Mock lateinit var context: Context
lateinit var dateUtil: DateUtil
val rxBus = RxBus(aapsSchedulers, aapsLogger) val rxBus = RxBus(aapsSchedulers, aapsLogger)
val profileInjector = HasAndroidInjector { val profileInjector = HasAndroidInjector { AndroidInjector { } }
AndroidInjector {
}
}
private lateinit var validProfileJSON: String private lateinit var validProfileJSON: String
lateinit var validProfile: Profile lateinit var validProfile: ProfileSealed.Pure
lateinit var effectiveProfileSwitch: EffectiveProfileSwitch
@Suppress("PropertyName") val TESTPROFILENAME = "someProfile" @Suppress("PropertyName") val TESTPROFILENAME = "someProfile"
@BeforeEach @BeforeEach
@ -45,7 +53,117 @@ open class TestBaseWithProfile : TestBase() {
validProfileJSON = "{\"dia\":\"5\",\"carbratio\":[{\"time\":\"00:00\",\"value\":\"30\"}],\"carbs_hr\":\"20\",\"delay\":\"20\",\"sens\":[{\"time\":\"00:00\",\"value\":\"3\"}," + validProfileJSON = "{\"dia\":\"5\",\"carbratio\":[{\"time\":\"00:00\",\"value\":\"30\"}],\"carbs_hr\":\"20\",\"delay\":\"20\",\"sens\":[{\"time\":\"00:00\",\"value\":\"3\"}," +
"{\"time\":\"2:00\",\"value\":\"3.4\"}],\"timezone\":\"UTC\",\"basal\":[{\"time\":\"00:00\",\"value\":\"1\"}],\"target_low\":[{\"time\":\"00:00\",\"value\":\"4.5\"}]," + "{\"time\":\"2:00\",\"value\":\"3.4\"}],\"timezone\":\"UTC\",\"basal\":[{\"time\":\"00:00\",\"value\":\"1\"}],\"target_low\":[{\"time\":\"00:00\",\"value\":\"4.5\"}]," +
"\"target_high\":[{\"time\":\"00:00\",\"value\":\"7\"}],\"startDate\":\"1970-01-01T00:00:00.000Z\",\"units\":\"mmol\"}" "\"target_high\":[{\"time\":\"00:00\",\"value\":\"7\"}],\"startDate\":\"1970-01-01T00:00:00.000Z\",\"units\":\"mmol\"}"
dateUtil = Mockito.spy(DateUtil(context))
`when`(dateUtil.now()).thenReturn(1656358822000)
validProfile = ProfileSealed.Pure(pureProfileFromJson(JSONObject(validProfileJSON), dateUtil)!!) validProfile = ProfileSealed.Pure(pureProfileFromJson(JSONObject(validProfileJSON), dateUtil)!!)
effectiveProfileSwitch = EffectiveProfileSwitch(
timestamp = dateUtil.now(),
basalBlocks = validProfile.basalBlocks,
isfBlocks = validProfile.isfBlocks,
icBlocks = validProfile.icBlocks,
targetBlocks = validProfile.targetBlocks,
glucoseUnit = EffectiveProfileSwitch.GlucoseUnit.MMOL,
originalProfileName = "",
originalCustomizedName = "",
originalTimeshift = 0,
originalPercentage = 100,
originalDuration = 0,
originalEnd = 0,
insulinConfiguration = InsulinConfiguration("", 0, 0)
)
Mockito.doAnswer { invocation: InvocationOnMock ->
val string = invocation.getArgument<Int>(0)
val arg1 = invocation.getArgument<Int?>(1)
String.format(rh.gs(string), arg1)
}.`when`(rh).gs(anyInt(), anyInt())
Mockito.doAnswer { invocation: InvocationOnMock ->
val string = invocation.getArgument<Int>(0)
val arg1 = invocation.getArgument<Double?>(1)
String.format(rh.gs(string), arg1)
}.`when`(rh).gs(anyInt(), anyDouble())
Mockito.doAnswer { invocation: InvocationOnMock ->
val string = invocation.getArgument<Int>(0)
val arg1 = invocation.getArgument<String?>(1)
String.format(rh.gs(string), arg1)
}.`when`(rh).gs(anyInt(), anyString())
Mockito.doAnswer { invocation: InvocationOnMock ->
val string = invocation.getArgument<Int>(0)
val arg1 = invocation.getArgument<String?>(1)
val arg2 = invocation.getArgument<String?>(2)
String.format(rh.gs(string), arg1, arg2)
}.`when`(rh).gs(anyInt(), anyString(), anyString())
Mockito.doAnswer { invocation: InvocationOnMock ->
val string = invocation.getArgument<Int>(0)
val arg1 = invocation.getArgument<String?>(1)
val arg2 = invocation.getArgument<Int?>(2)
String.format(rh.gs(string), arg1, arg2)
}.`when`(rh).gs(anyInt(), anyString(), anyInt())
Mockito.doAnswer { invocation: InvocationOnMock ->
val string = invocation.getArgument<Int>(0)
val arg1 = invocation.getArgument<Double?>(1)
val arg2 = invocation.getArgument<String?>(2)
String.format(rh.gs(string), arg1, arg2)
}.`when`(rh).gs(anyInt(), anyDouble(), anyString())
Mockito.doAnswer { invocation: InvocationOnMock ->
val string = invocation.getArgument<Int>(0)
val arg1 = invocation.getArgument<Double?>(1)
val arg2 = invocation.getArgument<Int?>(2)
String.format(rh.gs(string), arg1, arg2)
}.`when`(rh).gs(anyInt(), anyDouble(), anyInt())
Mockito.doAnswer { invocation: InvocationOnMock ->
val string = invocation.getArgument<Int>(0)
val arg1 = invocation.getArgument<Int?>(1)
val arg2 = invocation.getArgument<Int?>(2)
String.format(rh.gs(string), arg1, arg2)
}.`when`(rh).gs(anyInt(), anyInt(), anyInt())
Mockito.doAnswer { invocation: InvocationOnMock ->
val string = invocation.getArgument<Int>(0)
val arg1 = invocation.getArgument<Int?>(1)
val arg2 = invocation.getArgument<String?>(2)
String.format(rh.gs(string), arg1, arg2)
}.`when`(rh).gs(anyInt(), anyInt(), anyString())
Mockito.doAnswer { invocation: InvocationOnMock ->
val string = invocation.getArgument<Int>(0)
val arg1 = invocation.getArgument<Int?>(1)
val arg2 = invocation.getArgument<Int?>(2)
val arg3 = invocation.getArgument<String?>(3)
String.format(rh.gs(string), arg1, arg2, arg3)
}.`when`(rh).gs(anyInt(), anyInt(), anyInt(), anyString())
Mockito.doAnswer { invocation: InvocationOnMock ->
val string = invocation.getArgument<Int>(0)
val arg1 = invocation.getArgument<Int?>(1)
val arg2 = invocation.getArgument<String?>(2)
val arg3 = invocation.getArgument<String?>(3)
String.format(rh.gs(string), arg1, arg2, arg3)
}.`when`(rh).gs(anyInt(), anyInt(), anyString(), anyString())
Mockito.doAnswer { invocation: InvocationOnMock ->
val string = invocation.getArgument<Int>(0)
val arg1 = invocation.getArgument<Double?>(1)
val arg2 = invocation.getArgument<Int?>(2)
val arg3 = invocation.getArgument<String?>(3)
String.format(rh.gs(string), arg1, arg2, arg3)
}.`when`(rh).gs(anyInt(), anyDouble(), anyInt(), anyString())
Mockito.doAnswer { invocation: InvocationOnMock ->
val string = invocation.getArgument<Int>(0)
val arg1 = invocation.getArgument<String?>(1)
val arg2 = invocation.getArgument<Int?>(2)
val arg3 = invocation.getArgument<String?>(3)
String.format(rh.gs(string), arg1, arg2, arg3)
}.`when`(rh).gs(anyInt(), anyString(), anyInt(), anyString())
} }
fun getValidProfileStore(): ProfileStore { fun getValidProfileStore(): ProfileStore {

View file

@ -0,0 +1,52 @@
package info.nightscout.pump.medtrum
import info.nightscout.core.extensions.pureProfileFromJson
import info.nightscout.core.profile.ProfileSealed
import org.json.JSONObject
import org.junit.jupiter.api.Test
import org.junit.Assert.*
class MedtrumPumpTest : MedtrumTestBase() {
@Test fun buildMedtrumProfileArrayGivenProfileWhenValuesSetThenReturnCorrectByteArray() {
// Inputs
// Basal profile with 7 elements:
// 00:00 : 2.1
// 04:00 : 1.9
// 06:00 : 1.7
// 08:00 : 1.5
// 16:00 : 1.6
// 21:00 : 1.7
// 23:00 : 2
val profileJSON = "{\"dia\":\"5\",\"carbratio\":[{\"time\":\"00:00\",\"value\":\"30\"}],\"carbs_hr\":\"20\",\"delay\":\"20\"," +
"\"sens\":[{\"time\":\"00:00\",\"value\":\"3\"},{\"time\":\"02:00\",\"value\":\"3.4\"}],\"timezone\":\"UTC\"," +
"\"basal\":[{\"time\":\"00:00\",\"value\":\"2.1\"},{\"time\":\"04:00\",\"value\":\"1.9\"},{\"time\":\"06:00\",\"value\":\"1.7\"}," +
"{\"time\":\"08:00\",\"value\":\"1.5\"},{\"time\":\"16:00\",\"value\":\"1.6\"},{\"time\":\"21:00\",\"value\":\"1.7\"},{\"time\":\"23:00\",\"value\":\"2\"}]," +
"\"target_low\":[{\"time\":\"00:00\",\"value\":\"4.5\"}],\"target_high\":[{\"time\":\"00:00\",\"value\":\"7\"}],\"startDate\":\"1970-01-01T00:00:00.000Z\",\"units\":\"mmol\"}"
val profile = ProfileSealed.Pure(pureProfileFromJson(JSONObject(profileJSON), dateUtil)!!)
// Call
val result = medtrumPump.buildMedtrumProfileArray(profile)
// Expected values
val expectedByteArray = byteArrayOf(7, 0, -96, 2, -16, 96, 2, 104, 33, 2, -32, -31, 1, -64, 3, 2, -20, 36, 2, 100, -123, 2)
assertEquals(expectedByteArray.contentToString(), result?.contentToString())
}
@Test fun buildMedtrumProfileArrayGiveProfileWhenValuesTooHighThenReturnNull() {
// Inputs
// Basal profile with 1 element:
// 00:00 : 600
val profileJSON = "{\"dia\":\"5\",\"carbratio\":[{\"time\":\"00:00\",\"value\":\"30\"}],\"carbs_hr\":\"20\",\"delay\":\"20\"," +
"\"sens\":[{\"time\":\"00:00\",\"value\":\"3\"},{\"time\":\"02:00\",\"value\":\"3.4\"}],\"timezone\":\"UTC\"," +
"\"basal\":[{\"time\":\"00:00\",\"value\":\"600\"}]," +
"\"target_low\":[{\"time\":\"00:00\",\"value\":\"4.5\"}],\"target_high\":[{\"time\":\"00:00\",\"value\":\"7\"}],\"startDate\":\"1970-01-01T00:00:00.000Z\",\"units\":\"mmol\"}"
val profile = ProfileSealed.Pure(pureProfileFromJson(JSONObject(profileJSON), dateUtil)!!)
// Call
val result = medtrumPump.buildMedtrumProfileArray(profile)
// Expected values
assertNull(result)
}
}

View file

@ -1,7 +1,22 @@
package info.nightscout.pump.medtrum package info.nightscout.pump.medtrum
import info.nightscout.androidaps.TestBaseWithProfile import info.nightscout.androidaps.TestBaseWithProfile
import info.nightscout.shared.sharedPreferences.SP
import info.nightscout.interfaces.profile.Instantiator
import info.nightscout.interfaces.stats.TddCalculator
import org.junit.jupiter.api.BeforeEach
import org.mockito.Mock
open class MedtrumTestBase: TestBaseWithProfile() { open class MedtrumTestBase: TestBaseWithProfile() {
} @Mock lateinit var sp: SP
@Mock lateinit var instantiator: Instantiator
@Mock lateinit var tddCalculator: TddCalculator
lateinit var medtrumPump: MedtrumPump
@BeforeEach
fun setup() {
medtrumPump = MedtrumPump(aapsLogger, sp, dateUtil, instantiator)
}
}

View file

@ -3,6 +3,7 @@ package info.nightscout.pump.medtrum.comm.packets
import dagger.android.AndroidInjector 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.comm.enums.AlarmSetting
import org.junit.jupiter.api.Test import org.junit.jupiter.api.Test
import org.junit.Assert.* import org.junit.Assert.*
@ -12,22 +13,74 @@ class ActivatePacketTest : MedtrumTestBase() {
private val packetInjector = HasAndroidInjector { private val packetInjector = HasAndroidInjector {
AndroidInjector { AndroidInjector {
if (it is MedtrumPacket) { if (it is ActivatePacket) {
it.aapsLogger = aapsLogger it.aapsLogger = aapsLogger
it.medtrumPump = medtrumPump
it.tddCalculator = tddCalculator
} }
} }
} }
@Test fun getRequestGivenPacketWhenCalledThenReturnOpCode() { @Test fun getRequestGivenPacketWhenValuesSetThenReturnCorrectByteArray() {
// Inputs // Inputs
val opCode = 18 medtrumPump.desiredPatchExpiration = true
medtrumPump.desiredAlarmSetting = AlarmSetting.BEEP_ONLY.code
medtrumPump.desiredDailyMaxInsulin = 40
medtrumPump.desiredDailyMaxInsulin = 180
val basalProfile = byteArrayOf(3, 16, 14, 0, 0, 1, 2, 12, 12, 12)
// Call // Call
val packet = ActivatePacket(packetInjector) val packet = ActivatePacket(packetInjector, basalProfile)
val result = packet.getRequest() val result = packet.getRequest()
// Expected values // Expected values
assertEquals(1, result.size) val expectedByteArray = byteArrayOf(18, 0, 0, 1, 6, 0, 0, 0, 32, 3, 16, 14, 0, 0, 1, 3, 16, 14, 0, 0, 1, 2, 12, 12, 12)
assertEquals(opCode.toByte(), result[0]) assertEquals(expectedByteArray.contentToString(), result.contentToString())
}
@Test fun handleResponseGivenPacketWhenValuesSetThenReturnCorrectValues() {
// Inputs
medtrumPump.desiredPatchExpiration = true
medtrumPump.desiredAlarmSetting = AlarmSetting.BEEP_ONLY.code
medtrumPump.desiredDailyMaxInsulin = 40
medtrumPump.desiredDailyMaxInsulin = 180
val basalProfile = byteArrayOf(3, 16, 14, 0, 0, 1, 2, 12, 12, 12)
val response = byteArrayOf(26, 18, 19, 1, 0, 0, 41, 0, 0, 0, -104, 91, 28, 17, 1, 30, 0, 1, 0, 41, 0, -104, 91, 28, 17)
// Call
val packet = ActivatePacket(packetInjector, basalProfile)
val result = packet.handleResponse(response)
// Expected values
val expectedPatchId = 41L
val expectedTime = 1675605528L
val exptectedBasalType = 1
val expectedBasalRate = 1.5
val expectedBasalSequence = 1
val expectedBasalPatchId = 41
val expectedBasalStart = 1675605528L
assertEquals(true, result)
assertEquals(expectedPatchId, medtrumPump.patchId)
assertEquals(expectedTime, medtrumPump.lastTimeReceivedFromPump)
assertEquals(exptectedBasalType, medtrumPump.lastBasalType)
assertEquals(expectedBasalRate, medtrumPump.lastBasalRate, 0.01)
assertEquals(expectedBasalSequence, medtrumPump.lastBasalSequence)
assertEquals(expectedBasalPatchId, medtrumPump.lastBasalPatchId)
assertEquals(expectedBasalStart, medtrumPump.lastBasalStartTime)
}
@Test fun handleResponseGivenResponseWhenMessageTooShortThenResultFalse() {
// Inputs
val response = byteArrayOf(26, 18, 19, 1, 0, 0, 41, 0, 0, 0, -104, 91, 28, 17, 1, 30, 0, 1, 0, 41, 0, -104, 91, 28)
// Call
val packet = ActivatePacket(packetInjector, byteArrayOf())
val result = packet.handleResponse(response)
// Expected values
assertFalse(result)
} }
} }

View file

@ -52,8 +52,8 @@ class AuthorizePacketTest : MedtrumTestBase() {
// Expected values // Expected values
val swString = "$swVerX.$swVerY.$swVerZ" val swString = "$swVerX.$swVerY.$swVerZ"
assertEquals(true, result) assertTrue(result)
assertEquals(false, packet.failed) assertFalse(packet.failed)
assertEquals(deviceType, packet.deviceType) assertEquals(deviceType, packet.deviceType)
assertEquals(swString, packet.swVersion) assertEquals(swString, packet.swVersion)
} }
@ -70,7 +70,7 @@ class AuthorizePacketTest : MedtrumTestBase() {
val result = packet.handleResponse(response) val result = packet.handleResponse(response)
// Expected values // Expected values
assertEquals(false, result) assertFalse(result)
assertEquals(true, packet.failed) assertTrue(packet.failed)
} }
} }

View file

@ -27,7 +27,8 @@ class CancelBolusPacketTest : MedtrumTestBase() {
val result = packet.getRequest() val result = packet.getRequest()
// Expected values // Expected values
assertEquals(1, result.size) val expectedByteArray = byteArrayOf(opCode.toByte()) + 1.toByte()
assertEquals(opCode.toByte(), result[0]) assertEquals(2, result.size)
assertEquals(expectedByteArray.contentToString(), result.contentToString())
} }
} }

View file

@ -12,8 +12,9 @@ class CancelTempBasalPacketTest : MedtrumTestBase() {
private val packetInjector = HasAndroidInjector { private val packetInjector = HasAndroidInjector {
AndroidInjector { AndroidInjector {
if (it is MedtrumPacket) { if (it is CancelTempBasalPacket) {
it.aapsLogger = aapsLogger it.aapsLogger = aapsLogger
it.medtrumPump = medtrumPump
} }
} }
} }
@ -30,4 +31,40 @@ class CancelTempBasalPacketTest : MedtrumTestBase() {
assertEquals(1, result.size) assertEquals(1, result.size)
assertEquals(opCode.toByte(), result[0]) assertEquals(opCode.toByte(), result[0])
} }
@Test fun handleResponseGivenPacketWhenValuesSetThenReturnCorrectValues() {
// Inputs
val repsonse = byteArrayOf(18, 25, 16, 0, 0, 0, 1, 22, 0, 3, 0, -110, 0, -32, -18, 88, 17)
// Call
val packet = CancelTempBasalPacket(packetInjector)
val result = packet.handleResponse(repsonse)
// Expected values
val expectedBasalType = 1
val expectedBasalRate = 1.1
val expectedBasalSequence = 3
val expectedStartTime = 1679575392L
val expectedPatchId = 146
assertTrue(result)
assertEquals(expectedBasalType, medtrumPump.lastBasalType)
assertEquals(expectedBasalRate, medtrumPump.lastBasalRate, 0.01)
assertEquals(expectedBasalSequence, medtrumPump.lastBasalSequence)
assertEquals(expectedStartTime, medtrumPump.lastBasalStartTime)
assertEquals(expectedPatchId, medtrumPump.lastBasalPatchId)
}
@Test fun handleResponseGivenResponseWhenMessageTooShortThenResultFalse() {
// Inputs
val response = byteArrayOf(18, 25, 16, 0, 0, 0, 1, 22, 0, 3, 0, -110, 0, -32, -18, 88)
// Call
val packet = CancelTempBasalPacket(packetInjector)
val result = packet.handleResponse(response)
// Expected values
assertFalse(result)
assertTrue(packet.failed)
}
} }

View file

@ -19,7 +19,7 @@ class ReadBolusStatePacketTest : MedtrumTestBase() {
} }
} }
@Test fun handleResponseGivenResponseWhenMessageIsCorrectLengthThenResultTrue() { @Test fun handleResponseGivenPacketWhenValuesSetThenReturnCorrectValues() {
// Inputs // Inputs
val opCode = 34 val opCode = 34
val responseCode = 0 val responseCode = 0
@ -31,8 +31,8 @@ class ReadBolusStatePacketTest : MedtrumTestBase() {
val result = packet.handleResponse(response) val result = packet.handleResponse(response)
// Expected values // Expected values
assertEquals(true, result) assertTrue(result)
assertEquals(false, packet.failed) assertFalse(packet.failed)
assertEquals(bolusData.contentToString(), packet.bolusData.contentToString()) assertEquals(bolusData.contentToString(), packet.bolusData.contentToString())
} }

View file

@ -12,8 +12,9 @@ class SetBasalProfilePacketTest : MedtrumTestBase() {
private val packetInjector = HasAndroidInjector { private val packetInjector = HasAndroidInjector {
AndroidInjector { AndroidInjector {
if (it is MedtrumPacket) { if (it is SetBasalProfilePacket) {
it.aapsLogger = aapsLogger it.aapsLogger = aapsLogger
it.medtrumPump = medtrumPump
} }
} }
} }
@ -21,13 +22,14 @@ class SetBasalProfilePacketTest : MedtrumTestBase() {
@Test fun getRequestGivenPacketWhenCalledThenReturnOpCode() { @Test fun getRequestGivenPacketWhenCalledThenReturnOpCode() {
// Inputs // Inputs
val opCode = 21 val opCode = 21
val basalProfile = byteArrayOf(8, 2, 3, 4, -1, 0, 0, 0, 0)
// Call // Call
val packet = SetBasalProfilePacket(packetInjector) val packet = SetBasalProfilePacket(packetInjector, basalProfile)
val result = packet.getRequest() val result = packet.getRequest()
// Expected values // Expected values
assertEquals(1, result.size) val expected = byteArrayOf(opCode.toByte()) + 1.toByte() + basalProfile
assertEquals(opCode.toByte(), result[0]) assertEquals(expected.contentToString(), result.contentToString())
} }
} }

View file

@ -20,15 +20,14 @@ class SetBolusPacketTest : MedtrumTestBase() {
@Test fun getRequestGivenPacketWhenCalledThenReturnOpCode() { @Test fun getRequestGivenPacketWhenCalledThenReturnOpCode() {
// Inputs // Inputs
val opCode = 19 val insulin = 2.35
// Call // Call
val packet = SetBolusPacket(packetInjector) val packet = SetBolusPacket(packetInjector, insulin)
val result = packet.getRequest() val result = packet.getRequest()
// Expected values // Expected values
// TODO correct value's val expected = byteArrayOf(19, 1, 47, 0, 0)
assertEquals(1, result.size) assertEquals(expected.contentToString(), result.contentToString())
assertEquals(opCode.toByte(), result[0])
} }
} }

View file

@ -3,32 +3,36 @@ package info.nightscout.pump.medtrum.comm.packets
import dagger.android.AndroidInjector 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.comm.enums.AlarmSetting
import org.junit.jupiter.api.Test import org.junit.jupiter.api.Test
import org.junit.Assert.* import org.junit.Assert.*
class SetPatchPacket : MedtrumTestBase() { class SetPatchPacketTest : MedtrumTestBase() {
/** Test packet specific behavoir */ /** Test packet specific behavoir */
private val packetInjector = HasAndroidInjector { private val packetInjector = HasAndroidInjector {
AndroidInjector { AndroidInjector {
if (it is MedtrumPacket) { if (it is SetPatchPacket) {
it.aapsLogger = aapsLogger it.aapsLogger = aapsLogger
it.medtrumPump = medtrumPump
} }
} }
} }
@Test fun getRequestGivenPacketWhenCalledThenReturnOpCode() { @Test fun getRequestGivenValuesWhenCalledThenReturnValidArray() {
// Inputs // Inputs
val opCode = 35 medtrumPump.desiredPatchExpiration = false
medtrumPump.desiredAlarmSetting = AlarmSetting.LIGHT_AND_VIBRATE.code
medtrumPump.desiredDailyMaxInsulin = 40
medtrumPump.desiredDailyMaxInsulin = 180
// Call // Call
val packet = SetBolusPacket(packetInjector) val packet = SetPatchPacket(packetInjector)
val result = packet.getRequest() val result = packet.getRequest()
// Expected values // Expected values
// TODO correct value's val expected = byteArrayOf(35, 1, 32, 3, 16, 14, 0, 0, 0, 0, 0, 0)
assertEquals(1, result.size) assertEquals(expected.contentToString(), result.contentToString())
assertEquals(opCode.toByte(), result[0])
} }
} }

View file

@ -3,7 +3,6 @@ package info.nightscout.pump.medtrum.comm.packets
import dagger.android.AndroidInjector 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 org.junit.jupiter.api.Test import org.junit.jupiter.api.Test
import org.junit.Assert.* import org.junit.Assert.*
@ -13,50 +12,65 @@ class SetTempBasalPacketTest : MedtrumTestBase() {
private val packetInjector = HasAndroidInjector { private val packetInjector = HasAndroidInjector {
AndroidInjector { AndroidInjector {
if (it is MedtrumPacket) { if (it is SetTempBasalPacket) {
it.aapsLogger = aapsLogger it.aapsLogger = aapsLogger
it.medtrumPump = medtrumPump
} }
} }
} }
@Test fun getRequestGivenPacketWhenCalledThenReturnOpCode() { @Test fun getRequestGivenPacketWhenCalledThenReturnOpCode() {
// Inputs // Inputs
val opCode = 24 val absoluteRate = 1.25
val duration = 60
// Call // Call
val packet = SetTempBasalPacket(packetInjector) val packet = SetTempBasalPacket(packetInjector, absoluteRate, duration)
val result = packet.getRequest() val result = packet.getRequest()
// Expected values // Expected values
assertEquals(1, result.size) val expected = byteArrayOf(24, 6, 25, 0, 60, 0)
assertEquals(opCode.toByte(), result[0]) assertEquals(expected.contentToString(), result.contentToString())
} }
@Test fun getRequestGivenPacketWhenCalledThenReturnOpCodeAndDuration() { @Test fun handleResponseGivenPacketWhenValuesSetThenReturnCorrectValues() {
// TODO // Inputs
} val absoluteRate = 1.25
val duration = 60
@Test fun getRequestGivenPacketWhenCalledThenReturnOpCodeAndDurationAndRate() { val response = byteArrayOf(18, 24, 12, 0, 0, 0, 6, 25, 0, 2, 0, -110, 0, -56, -19, 88, 17, -89, 0)
// TODO
}
@Test fun handleResponseGivenResponseWhenMessageIsCorrectLengthThenResultTrue() { // Call
// TODO val packet = SetTempBasalPacket(packetInjector, absoluteRate, duration)
val result = packet.handleResponse(response)
// Expected values
val expectedBasalType = 6
val expectedBasalRate = 1.25
val expectedBasalSequence = 2
val expectedStartTime = 1679575112L
val expectedPatchId = 146
assertTrue(result)
assertEquals(expectedBasalType, medtrumPump.lastBasalType)
assertEquals(expectedBasalRate, medtrumPump.lastBasalRate, 0.01)
assertEquals(expectedBasalSequence, medtrumPump.lastBasalSequence)
assertEquals(expectedStartTime, medtrumPump.lastBasalStartTime)
assertEquals(expectedPatchId, medtrumPump.lastBasalPatchId)
} }
@Test fun handleResponseGivenResponseWhenMessageTooShortThenResultFalse() { @Test fun handleResponseGivenResponseWhenMessageTooShortThenResultFalse() {
// Inputs // Inputs
val opCode = 24 val absoluteRate = 1.25
val responseCode = 0 val duration = 60
val response = byteArrayOf(0) + opCode.toByte() + 0.toByteArray(2) + responseCode.toByteArray(2)
// Call val response = byteArrayOf(18, 24, 12, 0, 0, 0, 6, 25, 0, 2, 0, -110, 0, -56, -19, 88)
val packet = ReadBolusStatePacket(packetInjector)
// Call
val packet = SetTempBasalPacket(packetInjector, absoluteRate, duration)
val result = packet.handleResponse(response) val result = packet.handleResponse(response)
// Expected values // Expected values
assertEquals(false, result) assertFalse(result)
assertEquals(true, packet.failed)
// assertEquals(byteArrayOf().contentToString(), packet.bolusData.contentToString()) TODO
} }
} }

View file

@ -23,7 +23,7 @@ class SetTimePacketTest : MedtrumTestBase() {
@Test fun getRequestGivenPacketWhenCalledThenReturnOpCode() { @Test fun getRequestGivenPacketWhenCalledThenReturnOpCode() {
// Inputs // Inputs
val opCode = 10 val opCode = 10
val time = MedtrumTimeUtil().getCurrentTimePumpSeconds() // TODO: Mock time proper? val time = MedtrumTimeUtil().getCurrentTimePumpSeconds()
// Call // Call
val packet = SetTimePacket(packetInjector) val packet = SetTimePacket(packetInjector)

View file

@ -26,8 +26,8 @@ class SetTimeZonePacketTest : MedtrumTestBase() {
@Test fun getRequestGivenPacketWhenCalledThenReturnOpCode() { @Test fun getRequestGivenPacketWhenCalledThenReturnOpCode() {
// Inputs // Inputs
val opCode = 12 val opCode = 12
val time = MedtrumTimeUtil().getCurrentTimePumpSeconds() // TODO: Mock time proper? val time = MedtrumTimeUtil().getCurrentTimePumpSeconds()
val offsetMins = dateUtil.getTimeZoneOffsetMinutes(dateUtil.now()) // TODO: Mock time proper? val offsetMins = dateUtil.getTimeZoneOffsetMinutes(dateUtil.now())
// Call // Call
val packet = SetTimeZonePacket(packetInjector) val packet = SetTimeZonePacket(packetInjector)

View file

@ -12,8 +12,9 @@ class StopPatchPacketTest : MedtrumTestBase() {
private val packetInjector = HasAndroidInjector { private val packetInjector = HasAndroidInjector {
AndroidInjector { AndroidInjector {
if (it is MedtrumPacket) { if (it is StopPatchPacket) {
it.aapsLogger = aapsLogger it.aapsLogger = aapsLogger
it.medtrumPump = medtrumPump
} }
} }
} }
@ -31,5 +32,31 @@ class StopPatchPacketTest : MedtrumTestBase() {
assertEquals(opCode.toByte(), result[0]) assertEquals(opCode.toByte(), result[0])
} }
// TODO: Add tests for the response @Test fun handleResponseGivenPacketWhenValuesSetThenReturnCorrectValues() {
// Inputs
val response = byteArrayOf(11, 31, 10, 0, 0, 0, 23, 0, -110, 0, -5, 0)
// Call
val packet = StopPatchPacket(packetInjector)
val result = packet.handleResponse(response)
// Expected values
val expectedPatchId = 146
val expectedStopSequence = 23
assertEquals(expectedPatchId, medtrumPump.lastStopPatchId)
assertEquals(expectedStopSequence, medtrumPump.lastStopSequence)
}
@Test fun handleResponseGivenResponseWhenMessageTooShortThenResultFalse() {
// Inputs
val response = byteArrayOf(11, 31, 10, 0, 0, 0, 23, 0, -110)
// Call
val packet = StopPatchPacket(packetInjector)
val result = packet.handleResponse(response)
// Expected values
assertFalse(result)
assertTrue(packet.failed)
}
} }