Packet classes

This commit is contained in:
jbr7rr 2023-03-18 17:58:19 +01:00
parent 7afa0beb0a
commit 90392b2452
53 changed files with 1771 additions and 133 deletions

View file

@ -73,7 +73,7 @@
android:gravity="center_horizontal"
android:paddingStart="2dp"
android:paddingEnd="2dp"
android:text="@string/colon"
android:text=":"
android:textSize="16sp" />
<com.joanzapata.iconify.widget.IconTextView

View file

@ -26,5 +26,8 @@ dependencies {
implementation project(':core:interfaces')
implementation project(':core:main')
implementation project(':core:ui')
implementation project(':core:validators')
implementation project(':core:utils')
testImplementation project(':core:main')
}

View file

@ -1,61 +0,0 @@
package info.nightscout.pump.medtrum.comm
import dagger.android.HasAndroidInjector
import info.nightscout.pump.medtrum.encryption.Crypt
import info.nightscout.pump.medtrum.extension.toByteArray
import info.nightscout.pump.medtrum.util.MedtrumTimeUtil
import info.nightscout.shared.utils.DateUtil
import javax.inject.Inject
import javax.inject.Singleton
// TODO object would be better? Or split this class up in an entirely different way
@Singleton
class WriteCommand @Inject internal constructor(
private val dateUtil: DateUtil
) {
val COMMAND_SYNCHRONIZE: Byte = 3
val COMMAND_SUBSCRIBE: Byte = 4
val COMMAND_AUTH_REQ: Byte = 5
val COMMAND_GET_DEVICE_TYPE: Byte = 6
val COMMAND_SET_TIME: Byte = 10
val COMMAND_GET_TIME: Byte = 11
val COMMAND_SET_TIME_ZONE: Byte = 12
private val mCrypt = Crypt()
private val timeUtil = MedtrumTimeUtil()
fun authorize(deviceSerial: Long): ByteArray {
val role = 2 // Fixed to 2 for pump
val key = mCrypt.keyGen(deviceSerial)
return byteArrayOf(COMMAND_AUTH_REQ) + byteArrayOf(role.toByte()) + 0.toByteArray(4) + key.toByteArray(4)
}
fun getDeviceType(): ByteArray {
return byteArrayOf(COMMAND_GET_DEVICE_TYPE)
}
fun getTime(): ByteArray {
return byteArrayOf(COMMAND_GET_TIME)
}
fun setTime(): ByteArray {
val time = timeUtil.getCurrentTimePumpSeconds()
return byteArrayOf(COMMAND_SET_TIME) + 2.toByte() + time.toByteArray(4)
}
fun setTimeZone(): ByteArray {
val time = timeUtil.getCurrentTimePumpSeconds()
var offsetMins = dateUtil.getTimeZoneOffsetMinutes(dateUtil.now())
if (offsetMins < 0) offsetMins += 65536
return byteArrayOf(COMMAND_SET_TIME_ZONE) + offsetMins.toByteArray(2) + time.toByteArray(4)
}
fun synchronize(): ByteArray {
return byteArrayOf(COMMAND_SYNCHRONIZE)
}
fun subscribe(): ByteArray {
return byteArrayOf(COMMAND_SUBSCRIBE) + 4095.toByteArray(2)
}
}

View file

@ -0,0 +1,25 @@
package info.nightscout.pump.medtrum.comm.packets
import dagger.android.HasAndroidInjector
import info.nightscout.pump.medtrum.comm.packets.CommandType.ACTIVATE
class ActivatePacket(injector: HasAndroidInjector) : MedtrumPacket(injector) {
init {
opCode = ACTIVATE.code
}
override fun getRequest(): ByteArray {
// TODO get activation commands
return byteArrayOf(opCode)
}
override fun handleResponse(data: ByteArray): Boolean {
val success = super.handleResponse(data)
if (success) {
// TODO
}
return success
}
}

View file

@ -0,0 +1,47 @@
package info.nightscout.pump.medtrum.comm.packets
import dagger.android.HasAndroidInjector
import info.nightscout.pump.medtrum.comm.packets.CommandType.AUTH_REQ
import info.nightscout.pump.medtrum.encryption.Crypt
import info.nightscout.pump.medtrum.extension.toByteArray
import info.nightscout.pump.medtrum.extension.toInt
class AuthorizePacket(injector: HasAndroidInjector, private val deviceSerial: Long) : MedtrumPacket(injector) {
var deviceType: Int = 0
var swVersion: String = ""
companion object {
private const val RESP_DEVICE_TYPE_START = 7
private const val RESP_DEVICE_TYPE_END = RESP_DEVICE_TYPE_START + 1
private const val RESP_VERSION_X_START = 8
private const val RESP_VERSION_X_END = RESP_VERSION_X_START + 1
private const val RESP_VERSION_Y_START = 9
private const val RESP_VERSION_Y_END = RESP_VERSION_Y_START + 1
private const val RESP_VERSION_Z_START = 10
private const val RESP_VERSION_Z_END = RESP_VERSION_Z_START + 1
}
init {
opCode = AUTH_REQ.code
expectedMinRespLength = RESP_VERSION_Z_END
}
override fun getRequest(): ByteArray {
val role = 2 // Fixed to 2 for pump
val key = Crypt().keyGen(deviceSerial)
return byteArrayOf(opCode) + byteArrayOf(role.toByte()) + 0.toByteArray(4) + key.toByteArray(4)
}
override fun handleResponse(data: ByteArray): Boolean {
val success = super.handleResponse(data)
if (success) {
deviceType = data.copyOfRange(RESP_DEVICE_TYPE_START, RESP_DEVICE_TYPE_END).toInt()
swVersion = "" + data.copyOfRange(RESP_VERSION_X_START, RESP_VERSION_X_END).toInt() + "." + data.copyOfRange(RESP_VERSION_Y_START, RESP_VERSION_Y_END).toInt() + "." + data.copyOfRange(
RESP_VERSION_Z_START, RESP_VERSION_Z_END
).toInt()
}
return success
}
}

View file

@ -0,0 +1,16 @@
package info.nightscout.pump.medtrum.comm.packets
import dagger.android.HasAndroidInjector
import info.nightscout.pump.medtrum.comm.packets.CommandType.CANCEL_BOLUS
class CancelBolusPacket(injector: HasAndroidInjector) : MedtrumPacket(injector) {
init {
opCode = CANCEL_BOLUS.code
}
override fun getRequest(): ByteArray {
// TODO: Get bolus type
return byteArrayOf(opCode)
}
}

View file

@ -0,0 +1,19 @@
package info.nightscout.pump.medtrum.comm.packets
import dagger.android.HasAndroidInjector
import info.nightscout.pump.medtrum.comm.packets.CommandType.CANCEL_TEMP_BASAL
class CancelTempBasalPacket(injector: HasAndroidInjector) : MedtrumPacket(injector) {
init {
opCode = CANCEL_TEMP_BASAL.code
}
override fun handleResponse(data: ByteArray): Boolean {
val success = super.handleResponse(data)
if (success) {
// TODO Save basal
}
return success
}
}

View file

@ -0,0 +1,23 @@
package info.nightscout.pump.medtrum.comm.packets
enum class CommandType(val code: Byte) {
SYNCHRONIZE(3),
SUBSCRIBE(4),
AUTH_REQ(5),
GET_DEVICE_TYPE(6),
SET_TIME(10),
GET_TIME(11),
SET_TIME_ZONE(12),
PRIME(16),
ACTIVATE(18),
SET_BOLUS(19),
CANCEL_BOLUS(20),
SET_BASAL_PROFILE(21),
SET_TEMP_BASAL(24),
CANCEL_TEMP_BASAL(25),
POLL_PATCH(30),
STOP_PATCH(31),
READ_BOLUS_STATE(34),
SET_PATCH(35),
SET_BOLUS_MOTOR(36)
}

View file

@ -0,0 +1,35 @@
package info.nightscout.pump.medtrum.comm.packets
import dagger.android.HasAndroidInjector
import info.nightscout.pump.medtrum.comm.packets.CommandType.GET_DEVICE_TYPE
import info.nightscout.pump.medtrum.extension.toInt
import info.nightscout.pump.medtrum.extension.toLong
class GetDeviceTypePacket(injector: HasAndroidInjector) : MedtrumPacket(injector) {
var deviceType: Int = 0
var deviceSN: Long = 0
companion object {
private const val RESP_DEVICE_TYPE_START = 6
private const val RESP_DEVICE_TYPE_END = RESP_DEVICE_TYPE_START + 1
private const val RESP_DEVICE_SN_START = 7
private const val RESP_DEVICE_SN_END = RESP_DEVICE_SN_START + 4
}
init {
opCode = GET_DEVICE_TYPE.code
expectedMinRespLength = RESP_DEVICE_SN_END
}
override fun handleResponse(data: ByteArray): Boolean {
val success = super.handleResponse(data)
if (success) {
deviceType = data.copyOfRange(RESP_DEVICE_TYPE_START, RESP_DEVICE_TYPE_END).toInt()
deviceSN = data.copyOfRange(RESP_DEVICE_SN_START, RESP_DEVICE_SN_END).toLong()
}
return success
}
}

View file

@ -0,0 +1,30 @@
package info.nightscout.pump.medtrum.comm.packets
import dagger.android.HasAndroidInjector
import info.nightscout.pump.medtrum.comm.packets.CommandType.GET_TIME
import info.nightscout.pump.medtrum.extension.toLong
class GetTimePacket(injector: HasAndroidInjector) : MedtrumPacket(injector) {
var time: Long = 0
companion object {
private const val RESP_TIME_START = 6
private const val RESP_TIME_END = RESP_TIME_START + 4
}
init {
opCode = GET_TIME.code
expectedMinRespLength = RESP_TIME_END
}
override fun handleResponse(data: ByteArray): Boolean {
val success = super.handleResponse(data)
if (success) {
time = data.copyOfRange(RESP_TIME_START, RESP_TIME_END).toLong()
}
return success
}
}

View file

@ -0,0 +1,73 @@
package info.nightscout.pump.medtrum.comm.packets
import dagger.android.HasAndroidInjector
import info.nightscout.pump.medtrum.extension.toInt
import info.nightscout.rx.logging.AAPSLogger
import info.nightscout.rx.logging.LTag
import javax.inject.Inject
open class MedtrumPacket(protected var injector: HasAndroidInjector) {
@Inject lateinit var aapsLogger: AAPSLogger
var opCode: Byte = 0
var failed = false
var expectedMinRespLength = RESP_RESULT_END
companion object {
private const val RESP_OPCODE_START = 1
private const val RESP_OPCODE_END = RESP_OPCODE_START + 1
private const val RESP_RESULT_START = 4
private const val RESP_RESULT_END = RESP_RESULT_START + 2
private const val RESP_WAITING = 16384
}
init {
// @Suppress("LeakingThis")
injector.androidInjector().inject(this)
}
open fun getRequest(): ByteArray {
aapsLogger.debug(LTag.PUMPCOMM, "Get REQUEST TEST")
return byteArrayOf(opCode)
}
/** handles a response from the Medtrum pump, returns true if command was successfull, returns false if command failed or waiting for response */
open fun handleResponse(data: ByteArray): Boolean {
if (expectedMinRespLength > data.size) {
failed = true
aapsLogger.debug(LTag.PUMPCOMM, "handleResponse: Unexpected response length, expected: $expectedMinRespLength got: ${data.size}")
return false
}
val incomingOpCode: Byte = data.copyOfRange(RESP_OPCODE_START, RESP_OPCODE_END).first()
val responseCode = data.copyOfRange(RESP_RESULT_START, RESP_RESULT_END).toInt()
return when {
incomingOpCode != opCode -> {
failed = true
aapsLogger.debug(LTag.PUMPCOMM, "handleResponse: Unexpected command, expected: $opCode got: $incomingOpCode")
false
}
responseCode == 0 -> {
aapsLogger.debug(LTag.PUMPCOMM, "handleResponse: Happy command: $opCode response: $responseCode")
true
}
responseCode == RESP_WAITING -> {
aapsLogger.debug(LTag.PUMPCOMM, "handleResponse: Waiting command: $opCode response: $responseCode")
// Waiting do nothing
false
}
else -> {
failed = true
aapsLogger.debug(LTag.PUMPCOMM, "handleResponse: Error in command: $opCode response: $responseCode")
false
}
}
}
}

View file

@ -0,0 +1,11 @@
package info.nightscout.pump.medtrum.comm.packets
import dagger.android.HasAndroidInjector
import info.nightscout.pump.medtrum.comm.packets.CommandType.POLL_PATCH
class PollPatchPacket(injector: HasAndroidInjector) : MedtrumPacket(injector) {
init {
opCode = POLL_PATCH.code
}
}

View file

@ -0,0 +1,11 @@
package info.nightscout.pump.medtrum.comm.packets
import dagger.android.HasAndroidInjector
import info.nightscout.pump.medtrum.comm.packets.CommandType.PRIME
class PrimePacket(injector: HasAndroidInjector) : MedtrumPacket(injector) {
init {
opCode = PRIME.code
}
}

View file

@ -0,0 +1,29 @@
package info.nightscout.pump.medtrum.comm.packets
import dagger.android.HasAndroidInjector
import info.nightscout.pump.medtrum.comm.packets.CommandType.READ_BOLUS_STATE
import info.nightscout.pump.medtrum.extension.toInt
class ReadBolusStatePacket(injector: HasAndroidInjector) : MedtrumPacket(injector) {
var bolusData: ByteArray = byteArrayOf()
companion object {
private const val RESP_BOLUS_DATA_START = 6 // TODO: check this
}
init {
opCode = READ_BOLUS_STATE.code
expectedMinRespLength = RESP_BOLUS_DATA_START + 1
}
override fun handleResponse(data: ByteArray): Boolean {
val success = super.handleResponse(data)
if (success) {
bolusData = data.copyOfRange(RESP_BOLUS_DATA_START, data.size)
}
return success
}
}

View file

@ -0,0 +1,16 @@
package info.nightscout.pump.medtrum.comm.packets
import dagger.android.HasAndroidInjector
import info.nightscout.pump.medtrum.comm.packets.CommandType.SET_BASAL_PROFILE
class SetBasalProfilePacket(injector: HasAndroidInjector) : MedtrumPacket(injector) {
init {
opCode = SET_BASAL_PROFILE.code
}
override fun getRequest(): ByteArray {
// TODO get basal profile settings
return byteArrayOf(opCode)
}
}

View file

@ -0,0 +1,17 @@
package info.nightscout.pump.medtrum.comm.packets
import dagger.android.HasAndroidInjector
import info.nightscout.pump.medtrum.comm.packets.CommandType.SET_BOLUS_MOTOR
import info.nightscout.pump.medtrum.extension.toByteArray
import info.nightscout.pump.medtrum.util.MedtrumTimeUtil
class SetBolusMotorPacket(injector: HasAndroidInjector) : MedtrumPacket(injector) {
init {
opCode = SET_BOLUS_MOTOR.code
}
override fun getRequest(): ByteArray {
return byteArrayOf(opCode) + 0.toByte()
}
}

View file

@ -0,0 +1,16 @@
package info.nightscout.pump.medtrum.comm.packets
import dagger.android.HasAndroidInjector
import info.nightscout.pump.medtrum.comm.packets.CommandType.SET_BOLUS
class SetBolusPacket(injector: HasAndroidInjector) : MedtrumPacket(injector) {
init {
opCode = SET_BOLUS.code
}
override fun getRequest(): ByteArray {
// TODO get bolus settings
return byteArrayOf(opCode) + 0.toByte()
}
}

View file

@ -0,0 +1,17 @@
package info.nightscout.pump.medtrum.comm.packets
import dagger.android.HasAndroidInjector
import info.nightscout.pump.medtrum.comm.packets.CommandType.SET_PATCH
import info.nightscout.pump.medtrum.extension.toByteArray
class SetPatchPacket(injector: HasAndroidInjector) : MedtrumPacket(injector) {
init {
opCode = SET_PATCH.code
}
override fun getRequest(): ByteArray {
// TODO get patch settings
return byteArrayOf(opCode)
}
}

View file

@ -0,0 +1,25 @@
package info.nightscout.pump.medtrum.comm.packets
import dagger.android.HasAndroidInjector
import info.nightscout.pump.medtrum.comm.packets.CommandType.SET_TEMP_BASAL
class SetTempBasalPacket(injector: HasAndroidInjector) : MedtrumPacket(injector) {
init {
opCode = SET_TEMP_BASAL.code
// TODO set expectedMinRespLength
}
override fun getRequest(): ByteArray {
// TODO get temp basal settings
return byteArrayOf(opCode)
}
override fun handleResponse(data: ByteArray): Boolean {
val success = super.handleResponse(data)
if (success) {
// TODO Save basal
}
return success
}
}

View file

@ -0,0 +1,18 @@
package info.nightscout.pump.medtrum.comm.packets
import dagger.android.HasAndroidInjector
import info.nightscout.pump.medtrum.comm.packets.CommandType.SET_TIME
import info.nightscout.pump.medtrum.extension.toByteArray
import info.nightscout.pump.medtrum.util.MedtrumTimeUtil
class SetTimePacket(injector: HasAndroidInjector) : MedtrumPacket(injector) {
init {
opCode = SET_TIME.code
}
override fun getRequest(): ByteArray {
val time = MedtrumTimeUtil().getCurrentTimePumpSeconds()
return byteArrayOf(opCode) + 2.toByte() + time.toByteArray(4)
}
}

View file

@ -0,0 +1,24 @@
package info.nightscout.pump.medtrum.comm.packets
import dagger.android.HasAndroidInjector
import info.nightscout.pump.medtrum.comm.packets.CommandType.SET_TIME_ZONE
import info.nightscout.pump.medtrum.extension.toByteArray
import info.nightscout.pump.medtrum.util.MedtrumTimeUtil
import info.nightscout.shared.utils.DateUtil
import javax.inject.Inject
class SetTimeZonePacket(injector: HasAndroidInjector) : MedtrumPacket(injector) {
@Inject lateinit var dateUtil: DateUtil
init {
opCode = SET_TIME_ZONE.code
}
override fun getRequest(): ByteArray {
val time = MedtrumTimeUtil().getCurrentTimePumpSeconds()
var offsetMins = dateUtil.getTimeZoneOffsetMinutes(dateUtil.now())
if (offsetMins < 0) offsetMins += 65536
return byteArrayOf(opCode) + offsetMins.toByteArray(2) + time.toByteArray(4)
}
}

View file

@ -0,0 +1,19 @@
package info.nightscout.pump.medtrum.comm.packets
import dagger.android.HasAndroidInjector
import info.nightscout.pump.medtrum.comm.packets.CommandType.STOP_PATCH
class StopPatchPacket(injector: HasAndroidInjector) : MedtrumPacket(injector) {
init {
opCode = STOP_PATCH.code
}
override fun handleResponse(data: ByteArray): Boolean {
val success = super.handleResponse(data)
if (success) {
// TODO
}
return success
}
}

View file

@ -0,0 +1,17 @@
package info.nightscout.pump.medtrum.comm.packets
import dagger.android.HasAndroidInjector
import info.nightscout.pump.medtrum.comm.packets.CommandType.SUBSCRIBE
import info.nightscout.pump.medtrum.extension.toByteArray
import info.nightscout.pump.medtrum.util.MedtrumTimeUtil
class SubscribePacket(injector: HasAndroidInjector) : MedtrumPacket(injector) {
init {
opCode = SUBSCRIBE.code
}
override fun getRequest(): ByteArray {
return byteArrayOf(opCode) + 4095.toByteArray(2)
}
}

View file

@ -0,0 +1,37 @@
package info.nightscout.pump.medtrum.comm.packets
import dagger.android.HasAndroidInjector
import info.nightscout.pump.medtrum.comm.packets.CommandType.SYNCHRONIZE
import info.nightscout.pump.medtrum.extension.toInt
class SynchronizePacket(injector: HasAndroidInjector) : MedtrumPacket(injector) {
var state: Int = 0
var dataFieldsPresent: Int = 0
var syncData: ByteArray = byteArrayOf()
companion object {
private const val RESP_STATE_START = 6
private const val RESP_STATE_END = RESP_STATE_START + 1
private const val RESP_FIELDS_START = 7
private const val RESP_FIELDS_END = RESP_FIELDS_START + 2
private const val RESP_SYNC_DATA_START = 9
}
init {
opCode = SYNCHRONIZE.code
expectedMinRespLength = RESP_SYNC_DATA_START + 1
}
override fun handleResponse(data: ByteArray): Boolean {
val success = super.handleResponse(data)
if (success) {
state = data.copyOfRange(RESP_STATE_START, RESP_STATE_END).toInt()
dataFieldsPresent = data.copyOfRange(RESP_FIELDS_START, RESP_FIELDS_END).toInt()
syncData = data.copyOfRange(RESP_SYNC_DATA_START, data.size)
}
return success
}
}

View file

@ -0,0 +1,49 @@
package info.nightscout.pump.medtrum.di
import dagger.Module
import dagger.android.ContributesAndroidInjector
import info.nightscout.pump.medtrum.comm.packets.ActivatePacket
import info.nightscout.pump.medtrum.comm.packets.AuthorizePacket
import info.nightscout.pump.medtrum.comm.packets.CancelBolusPacket
import info.nightscout.pump.medtrum.comm.packets.CancelTempBasalPacket
import info.nightscout.pump.medtrum.comm.packets.GetDeviceTypePacket
import info.nightscout.pump.medtrum.comm.packets.GetTimePacket
import info.nightscout.pump.medtrum.comm.packets.MedtrumPacket
import info.nightscout.pump.medtrum.comm.packets.PollPatchPacket
import info.nightscout.pump.medtrum.comm.packets.PrimePacket
import info.nightscout.pump.medtrum.comm.packets.ReadBolusStatePacket
import info.nightscout.pump.medtrum.comm.packets.SetBasalProfilePacket
import info.nightscout.pump.medtrum.comm.packets.SetBolusMotorPacket
import info.nightscout.pump.medtrum.comm.packets.SetBolusPacket
import info.nightscout.pump.medtrum.comm.packets.SetPatchPacket
import info.nightscout.pump.medtrum.comm.packets.SetTempBasalPacket
import info.nightscout.pump.medtrum.comm.packets.SetTimePacket
import info.nightscout.pump.medtrum.comm.packets.SetTimeZonePacket
import info.nightscout.pump.medtrum.comm.packets.StopPatchPacket
import info.nightscout.pump.medtrum.comm.packets.SubscribePacket
import info.nightscout.pump.medtrum.comm.packets.SynchronizePacket
@Module
abstract class MedtrumCommModule {
@ContributesAndroidInjector abstract fun contributesActivatePacket(): ActivatePacket
@ContributesAndroidInjector abstract fun contributesAuthorizePacket(): AuthorizePacket
@ContributesAndroidInjector abstract fun contributesCancelBolusPacket(): CancelBolusPacket
@ContributesAndroidInjector abstract fun contributesCancelTempBasalPacket(): CancelTempBasalPacket
@ContributesAndroidInjector abstract fun contributesGetDeviceTypePacket(): GetDeviceTypePacket
@ContributesAndroidInjector abstract fun contributesGetTimePacket(): GetTimePacket
@ContributesAndroidInjector abstract fun contributesMedtrumPacket(): MedtrumPacket
@ContributesAndroidInjector abstract fun contributesPollPatchPacket(): PollPatchPacket
@ContributesAndroidInjector abstract fun contributesPrimePacket(): PrimePacket
@ContributesAndroidInjector abstract fun contributesReadBolusStatePacket(): ReadBolusStatePacket
@ContributesAndroidInjector abstract fun contributesSetBasalProfilePacket(): SetBasalProfilePacket
@ContributesAndroidInjector abstract fun contributesSetBolusMotorPacket(): SetBolusMotorPacket
@ContributesAndroidInjector abstract fun contributesSetBolusPacket(): SetBolusPacket
@ContributesAndroidInjector abstract fun contributesSetPatchPacket(): SetPatchPacket
@ContributesAndroidInjector abstract fun contributesSetTempBasalPacket(): SetTempBasalPacket
@ContributesAndroidInjector abstract fun contributesSetTimePacket(): SetTimePacket
@ContributesAndroidInjector abstract fun contributesSetTimeZonePacket(): SetTimeZonePacket
@ContributesAndroidInjector abstract fun contributesStopPatchPacket(): StopPatchPacket
@ContributesAndroidInjector abstract fun contributesSubscribePacket(): SubscribePacket
@ContributesAndroidInjector abstract fun contributesSynchronizePacket(): SynchronizePacket
}

View file

@ -18,7 +18,7 @@ import info.nightscout.pump.medtrum.ui.viewmodel.ViewModelFactory
import info.nightscout.pump.medtrum.ui.viewmodel.ViewModelKey
import javax.inject.Provider
@Module
@Module(includes = [MedtrumCommModule::class])
@Suppress("unused")
abstract class MedtrumModule {

View file

@ -180,6 +180,9 @@ class BLEComm @Inject internal constructor(
return
}
aapsLogger.debug(LTag.PUMPBTCOMM, "disconnect from: $from")
if (isConnecting) {
stopScan()
}
mBluetoothGatt?.disconnect()
}

View file

@ -5,52 +5,38 @@ import android.content.Context
import android.content.Intent
import android.os.Binder
import android.os.IBinder
import android.os.SystemClock
import dagger.android.DaggerService
import dagger.android.HasAndroidInjector
import info.nightscout.core.utils.fabric.FabricPrivacy
import info.nightscout.interfaces.Constants
import info.nightscout.interfaces.constraints.Constraints
import info.nightscout.interfaces.notifications.Notification
import info.nightscout.interfaces.plugin.ActivePlugin
import info.nightscout.interfaces.profile.Profile
import info.nightscout.interfaces.profile.ProfileFunction
import info.nightscout.interfaces.pump.BolusProgressData
import info.nightscout.interfaces.pump.PumpEnactResult
import info.nightscout.interfaces.pump.PumpSync
import info.nightscout.interfaces.queue.Callback
import info.nightscout.interfaces.queue.Command
import info.nightscout.interfaces.queue.CommandQueue
import info.nightscout.interfaces.ui.UiInteraction
import info.nightscout.pump.medtrum.MedtrumPlugin
import info.nightscout.pump.medtrum.comm.WriteCommand
import info.nightscout.pump.medtrum.comm.packets.*
import info.nightscout.pump.medtrum.extension.toInt
import info.nightscout.pump.medtrum.extension.toLong
import info.nightscout.pump.medtrum.util.MedtrumTimeUtil
import info.nightscout.rx.AapsSchedulers
import info.nightscout.rx.bus.RxBus
import info.nightscout.rx.events.EventAppExit
import info.nightscout.rx.events.EventInitializationChanged
import info.nightscout.rx.events.EventOverviewBolusProgress
import info.nightscout.rx.events.EventPreferenceChange
import info.nightscout.rx.events.EventProfileSwitchChanged
import info.nightscout.rx.events.EventPumpStatusChanged
import info.nightscout.rx.logging.AAPSLogger
import info.nightscout.rx.logging.LTag
import info.nightscout.shared.interfaces.ResourceHelper
import info.nightscout.shared.sharedPreferences.SP
import info.nightscout.shared.utils.DateUtil
import info.nightscout.shared.utils.T
import io.reactivex.rxjava3.core.ObservableEmitter
import io.reactivex.rxjava3.disposables.CompositeDisposable
import io.reactivex.rxjava3.kotlin.plusAssign
import org.joda.time.DateTime
import org.joda.time.DateTimeZone
import java.util.*
import java.util.concurrent.TimeUnit
import javax.inject.Inject
import kotlin.math.abs
import kotlin.math.min
class MedtrumService : DaggerService(), BLECommCallback {
@ -71,7 +57,6 @@ class MedtrumService : DaggerService(), BLECommCallback {
@Inject lateinit var fabricPrivacy: FabricPrivacy
@Inject lateinit var pumpSync: PumpSync
@Inject lateinit var dateUtil: DateUtil
@Inject lateinit var writeCommand: WriteCommand
val timeUtil = MedtrumTimeUtil()
@ -80,6 +65,7 @@ class MedtrumService : DaggerService(), BLECommCallback {
private var mDeviceSN: Long = 0
private var currentState: State = IdleState()
private var mPacket: MedtrumPacket? = null
var isConnected = false
var isConnecting = false
@ -118,7 +104,7 @@ class MedtrumService : DaggerService(), BLECommCallback {
rxBus.send(EventPumpStatusChanged(EventPumpStatusChanged.Status.CONNECTING))
return bleComm.connect(from, mDeviceSN)
} else {
aapsLogger.error(LTag.PUMPCOMM, "Connect attempt when in non Idle state from: " + from)
aapsLogger.error(LTag.PUMPCOMM, "Connect attempt when in non Idle state from: $from")
return false
}
}
@ -205,7 +191,7 @@ class MedtrumService : DaggerService(), BLECommCallback {
}
override fun onSendMessageError(reason: String) {
aapsLogger.debug(LTag.PUMPCOMM, "<<<<< error during send message " + reason)
aapsLogger.debug(LTag.PUMPCOMM, "<<<<< error during send message $reason")
// TODO
}
@ -237,7 +223,7 @@ class MedtrumService : DaggerService(), BLECommCallback {
open fun onEnter() {}
open fun onIndication(data: ByteArray) {
aapsLogger.debug(LTag.PUMPCOMM, "onIndicationr: " + this.toString() + "Should not be called here!")
aapsLogger.debug(LTag.PUMPCOMM, "onIndication: " + this.toString() + "Should not be called here!")
}
open fun onConnected() {
@ -274,18 +260,19 @@ class MedtrumService : DaggerService(), BLECommCallback {
override fun onEnter() {
aapsLogger.debug(LTag.PUMPCOMM, "Medtrum Service reached AuthState")
bleComm.sendMessage(writeCommand.authorize(mDeviceSN))
mPacket = AuthorizePacket(injector, mDeviceSN)
mPacket?.getRequest()?.let { bleComm.sendMessage(it) }
}
override fun onIndication(data: ByteArray) {
// TODO Create class for this? Maybe combine with authorize write command, something like danaRS packets? + Unit Tests
val commandCode: Byte = data.copyOfRange(1, 2).toInt().toByte()
val responseCode = data.copyOfRange(4, 6).toInt()
// TODO Get pump version info (do we care?)
if (responseCode == 0 && commandCode == writeCommand.COMMAND_AUTH_REQ) {
if (mPacket?.handleResponse(data) == true) {
// Succes!
// TODO Get pump version info
val deviceType = (mPacket as AuthorizePacket).deviceType
val swVersion = (mPacket as AuthorizePacket).swVersion
aapsLogger.debug(LTag.PUMPCOMM, "GetDeviceTypeState: deviceType: $deviceType swVersion: $swVersion") // TODO remove me later
toState(GetDeviceTypeState())
} else {
} else if (mPacket?.failed == true) {
// Failure
bleComm.disconnect("Failure")
toState(IdleState())
@ -297,18 +284,19 @@ class MedtrumService : DaggerService(), BLECommCallback {
override fun onEnter() {
aapsLogger.debug(LTag.PUMPCOMM, "Medtrum Service reached GetDeviceTypeState")
bleComm.sendMessage(writeCommand.getDeviceType())
mPacket = GetDeviceTypePacket(injector)
mPacket?.getRequest()?.let { bleComm.sendMessage(it) }
}
override fun onIndication(data: ByteArray) {
// TODO Create class for this? Maybe combine with authorize write command, something like danaRS packets? + Unit Tests
val commandCode: Byte = data.copyOfRange(1, 2).toInt().toByte()
val responseCode = data.copyOfRange(4, 6).toInt()
// TODO Get device type (do we care?)
if (responseCode == 0 && commandCode == writeCommand.COMMAND_GET_DEVICE_TYPE) {
if (mPacket?.handleResponse(data) == true) {
// Succes!
// TODO Get device type and SN
val deviceType = (mPacket as GetDeviceTypePacket).deviceType
val deviceSN = (mPacket as GetDeviceTypePacket).deviceSN
aapsLogger.debug(LTag.PUMPCOMM, "GetDeviceTypeState: deviceType: $deviceType deviceSN: $deviceSN") // TODO remove me later
toState(GetTimeState())
} else {
} else if (mPacket?.failed == true) {
// Failure
bleComm.disconnect("Failure")
toState(IdleState())
@ -320,30 +308,27 @@ class MedtrumService : DaggerService(), BLECommCallback {
override fun onEnter() {
aapsLogger.debug(LTag.PUMPCOMM, "Medtrum Service reached GetTimeState")
bleComm.sendMessage(writeCommand.getTime())
mPacket = GetTimePacket(injector)
mPacket?.getRequest()?.let { bleComm.sendMessage(it) }
}
override fun onIndication(data: ByteArray) {
// TODO Create class for this? Maybe combine with authorize write command, something like danaRS packets? + Unit Tests
val commandCode: Byte = data.copyOfRange(1, 2).toInt().toByte()
val responseCode = data.copyOfRange(4, 6).toInt()
val time = data.copyOfRange(6, 10).toLong()
if (responseCode == 0 && commandCode == writeCommand.COMMAND_GET_TIME) {
if (mPacket?.handleResponse(data) == true) {
// Succes!
mLastDeviceTime = time
mLastDeviceTime = (mPacket as GetTimePacket).time
val currTimeSec = dateUtil.nowWithoutMilliseconds() / 1000
if (abs(timeUtil.convertPumpTimeToSystemTimeSeconds(time) - currTimeSec) <= 5) { // Allow 5 sec deviation
if (abs(timeUtil.convertPumpTimeToSystemTimeSeconds(mLastDeviceTime) - currTimeSec) <= 5) { // Allow 5 sec deviation
toState(SynchronizeState())
} else {
aapsLogger.debug(
LTag.PUMPCOMM,
"GetTimeState.onIndication need to set time. systemTime: " + currTimeSec + " PumpTime: " + time + " Pump Time to system time: " + timeUtil.convertPumpTimeToSystemTimeSeconds(
time
"GetTimeState.onIndication need to set time. systemTime: $currTimeSec PumpTime: $mLastDeviceTime Pump Time to system time: " + timeUtil.convertPumpTimeToSystemTimeSeconds(
mLastDeviceTime
)
)
toState(SetTimeState())
}
} else {
} else if (mPacket?.failed == true) {
// Failure
bleComm.disconnect("Failure")
toState(IdleState())
@ -355,17 +340,15 @@ class MedtrumService : DaggerService(), BLECommCallback {
override fun onEnter() {
aapsLogger.debug(LTag.PUMPCOMM, "Medtrum Service reached SetTimeState")
bleComm.sendMessage(writeCommand.setTime())
mPacket = SetTimePacket(injector)
mPacket?.getRequest()?.let { bleComm.sendMessage(it) }
}
override fun onIndication(data: ByteArray) {
// TODO Create class for this? Maybe combine with authorize write command, something like danaRS packets? + Unit Tests
val commandCode: Byte = data.copyOfRange(1, 2).toInt().toByte()
val responseCode = data.copyOfRange(4, 6).toInt()
if (responseCode == 0 && commandCode == writeCommand.COMMAND_SET_TIME) {
if (mPacket?.handleResponse(data) == true) {
// Succes!
toState(SetTimeZoneState())
} else {
} else if (mPacket?.failed == true) {
// Failure
bleComm.disconnect("Failure")
toState(IdleState())
@ -377,17 +360,15 @@ class MedtrumService : DaggerService(), BLECommCallback {
override fun onEnter() {
aapsLogger.debug(LTag.PUMPCOMM, "Medtrum Service reached SetTimeZoneState")
bleComm.sendMessage(writeCommand.setTimeZone())
mPacket = SetTimeZonePacket(injector)
mPacket?.getRequest()?.let { bleComm.sendMessage(it) }
}
override fun onIndication(data: ByteArray) {
// TODO Create class for this? Maybe combine with authorize write command, something like danaRS packets? + Unit Tests
val commandCode: Byte = data.copyOfRange(1, 2).toInt().toByte()
val responseCode = data.copyOfRange(4, 6).toInt()
if (responseCode == 0 && commandCode == writeCommand.COMMAND_SET_TIME_ZONE) {
if (mPacket?.handleResponse(data) == true) {
// Succes!
toState(SynchronizeState())
} else {
} else if (mPacket?.failed == true) {
// Failure
bleComm.disconnect("Failure")
toState(IdleState())
@ -399,18 +380,16 @@ class MedtrumService : DaggerService(), BLECommCallback {
override fun onEnter() {
aapsLogger.debug(LTag.PUMPCOMM, "Medtrum Service reached SynchronizeState")
bleComm.sendMessage(writeCommand.synchronize())
mPacket = SynchronizePacket(injector)
mPacket?.getRequest()?.let { bleComm.sendMessage(it) }
}
override fun onIndication(data: ByteArray) {
// TODO Create class for this? Maybe combine with authorize write command, something like danaRS packets? + Unit Tests
val commandCode: Byte = data.copyOfRange(1, 2).toInt().toByte()
val responseCode = data.copyOfRange(4, 6).toInt()
if (responseCode == 0 && commandCode == writeCommand.COMMAND_SYNCHRONIZE) {
if (mPacket?.handleResponse(data) == true) {
// Succes!
// TODO: Handle pump state parameters
toState(SubscribeState())
} else {
} else if (mPacket?.failed == true) {
// Failure
bleComm.disconnect("Failure")
toState(IdleState())
@ -422,17 +401,15 @@ class MedtrumService : DaggerService(), BLECommCallback {
override fun onEnter() {
aapsLogger.debug(LTag.PUMPCOMM, "Medtrum Service reached SubscribeState")
bleComm.sendMessage(writeCommand.subscribe())
mPacket = SubscribePacket(injector)
mPacket?.getRequest()?.let { bleComm.sendMessage(it) }
}
override fun onIndication(data: ByteArray) {
// TODO Create class for this? Maybe combine with authorize write command, something like danaRS packets? + Unit Tests
val commandCode: Byte = data.copyOfRange(1, 2).toInt().toByte()
val responseCode = data.copyOfRange(4, 6).toInt()
if (responseCode == 0 && commandCode == writeCommand.COMMAND_SUBSCRIBE) {
if (mPacket?.handleResponse(data) == true) {
// Succes!
toState(ReadyState())
} else {
} else if (mPacket?.failed == true) {
// Failure
bleComm.disconnect("Failure")
toState(IdleState())

View file

@ -74,7 +74,7 @@
android:gravity="center_horizontal"
android:paddingStart="2dp"
android:paddingEnd="2dp"
android:text="@string/colon"
android:text=":"
android:textSize="16sp" />
<com.joanzapata.iconify.widget.IconTextView

View file

@ -0,0 +1,107 @@
package info.nightscout.androidaps
import androidx.collection.ArrayMap
import dagger.android.HasAndroidInjector
import info.nightscout.core.extensions.pureProfileFromJson
import info.nightscout.core.profile.ProfileSealed
import info.nightscout.interfaces.Config
import info.nightscout.interfaces.plugin.ActivePlugin
import info.nightscout.interfaces.profile.ProfileStore
import info.nightscout.interfaces.profile.PureProfile
import info.nightscout.interfaces.utils.HardLimits
import info.nightscout.interfaces.utils.JsonHelper
import info.nightscout.rx.bus.RxBus
import info.nightscout.rx.logging.AAPSLogger
import info.nightscout.shared.interfaces.ResourceHelper
import info.nightscout.shared.utils.DateUtil
import org.json.JSONException
import org.json.JSONObject
import javax.inject.Inject
class ProfileStoreObject(val injector: HasAndroidInjector, override val data: JSONObject, val dateUtil: DateUtil) : ProfileStore {
@Inject lateinit var aapsLogger: AAPSLogger
@Inject lateinit var activePlugin: ActivePlugin
@Inject lateinit var config: Config
@Inject lateinit var rh: ResourceHelper
@Inject lateinit var rxBus: RxBus
@Inject lateinit var hardLimits: HardLimits
init {
injector.androidInjector().inject(this)
}
private val cachedObjects = ArrayMap<String, PureProfile>()
private fun storeUnits(): String? = JsonHelper.safeGetStringAllowNull(data, "units", null)
private fun getStore(): JSONObject? {
try {
if (data.has("store")) return data.getJSONObject("store")
} catch (e: JSONException) {
aapsLogger.error("Unhandled exception", e)
}
return null
}
override fun getStartDate(): Long {
val iso = JsonHelper.safeGetString(data, "startDate") ?: return 0
return try {
dateUtil.fromISODateString(iso)
} catch (e: Exception) {
0
}
}
override fun getDefaultProfile(): PureProfile? = getDefaultProfileName()?.let { getSpecificProfile(it) }
override fun getDefaultProfileJson(): JSONObject? = getDefaultProfileName()?.let { getSpecificProfileJson(it) }
override fun getDefaultProfileName(): String? {
val defaultProfileName = data.optString("defaultProfile")
return if (defaultProfileName.isNotEmpty()) getStore()?.has(defaultProfileName)?.let { defaultProfileName } else null
}
override fun getProfileList(): ArrayList<CharSequence> {
val ret = ArrayList<CharSequence>()
getStore()?.keys()?.let { keys ->
while (keys.hasNext()) {
val profileName = keys.next() as String
ret.add(profileName)
}
}
return ret
}
@Synchronized
override fun getSpecificProfile(profileName: String): PureProfile? {
var profile: PureProfile? = null
val units = JsonHelper.safeGetStringAllowNull(data, "units", storeUnits())
getStore()?.let { store ->
if (store.has(profileName)) {
profile = cachedObjects[profileName]
if (profile == null) {
JsonHelper.safeGetJSONObject(store, profileName, null)?.let { profileObject ->
profile = pureProfileFromJson(profileObject, dateUtil, units)
profile?.let { cachedObjects[profileName] = profile }
}
}
}
}
return profile
}
private fun getSpecificProfileJson(profileName: String): JSONObject? {
getStore()?.let { store ->
if (store.has(profileName))
return JsonHelper.safeGetJSONObject(store, profileName, null)
}
return null
}
override val allProfilesValid: Boolean
get() = getProfileList()
.asSequence()
.map { profileName -> getSpecificProfile(profileName.toString()) }
.map { pureProfile -> pureProfile?.let { ProfileSealed.Pure(pureProfile).isValid("allProfilesValid", activePlugin.activePump, config, rh, rxBus, hardLimits, false) } }
.all { it?.isValid == true }
}

View file

@ -11,6 +11,7 @@ import org.mockito.junit.jupiter.MockitoSettings
import org.mockito.quality.Strictness
import java.util.Locale
@Suppress("SpellCheckingInspection")
@ExtendWith(MockitoExtension::class)
@MockitoSettings(strictness = Strictness.LENIENT)
open class TestBase {

View file

@ -0,0 +1,59 @@
package info.nightscout.androidaps
import dagger.android.AndroidInjector
import dagger.android.HasAndroidInjector
import info.nightscout.core.extensions.pureProfileFromJson
import info.nightscout.core.profile.ProfileSealed
import info.nightscout.core.utils.fabric.FabricPrivacy
import info.nightscout.interfaces.Config
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.ProfileStore
import info.nightscout.rx.bus.RxBus
import info.nightscout.shared.interfaces.ResourceHelper
import info.nightscout.shared.utils.DateUtil
import org.json.JSONObject
import org.junit.jupiter.api.BeforeEach
import org.mockito.Mock
@Suppress("SpellCheckingInspection")
open class TestBaseWithProfile : TestBase() {
@Mock lateinit var activePluginProvider: ActivePlugin
@Mock lateinit var rh: ResourceHelper
@Mock lateinit var fabricPrivacy: FabricPrivacy
@Mock lateinit var profileFunction: ProfileFunction
@Mock lateinit var defaultValueHelper: DefaultValueHelper
@Mock lateinit var dateUtil: DateUtil
@Mock lateinit var config: Config
val rxBus = RxBus(aapsSchedulers, aapsLogger)
val profileInjector = HasAndroidInjector {
AndroidInjector {
}
}
private lateinit var validProfileJSON: String
lateinit var validProfile: Profile
@Suppress("PropertyName") val TESTPROFILENAME = "someProfile"
@BeforeEach
fun prepareMock() {
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\"}]," +
"\"target_high\":[{\"time\":\"00:00\",\"value\":\"7\"}],\"startDate\":\"1970-01-01T00:00:00.000Z\",\"units\":\"mmol\"}"
validProfile = ProfileSealed.Pure(pureProfileFromJson(JSONObject(validProfileJSON), dateUtil)!!)
}
fun getValidProfileStore(): ProfileStore {
val json = JSONObject()
val store = JSONObject()
store.put(TESTPROFILENAME, JSONObject(validProfileJSON))
json.put("defaultProfile", TESTPROFILENAME)
json.put("store", store)
return ProfileStoreObject(profileInjector, json, dateUtil)
}
}

View file

@ -0,0 +1,7 @@
package info.nightscout.pump.medtrum
import info.nightscout.androidaps.TestBaseWithProfile
open class MedtrumTestBase: TestBaseWithProfile() {
}

View file

@ -0,0 +1,33 @@
package info.nightscout.pump.medtrum.comm.packets
import dagger.android.AndroidInjector
import dagger.android.HasAndroidInjector
import info.nightscout.pump.medtrum.MedtrumTestBase
import org.junit.jupiter.api.Test
import org.junit.Assert.*
class ActivatePacketTest : MedtrumTestBase() {
/** Test packet specific behavoir */
private val packetInjector = HasAndroidInjector {
AndroidInjector {
if (it is MedtrumPacket) {
it.aapsLogger = aapsLogger
}
}
}
@Test fun getRequestGivenPacketWhenCalledThenReturnOpCode() {
// Inputs
val opCode = 18
// Call
val packet = ActivatePacket(packetInjector)
val result = packet.getRequest()
// Expected values
assertEquals(1, result.size)
assertEquals(opCode.toByte(), result[0])
}
}

View file

@ -0,0 +1,76 @@
package info.nightscout.pump.medtrum.comm.packets
import dagger.android.AndroidInjector
import dagger.android.HasAndroidInjector
import info.nightscout.pump.medtrum.MedtrumTestBase
import info.nightscout.pump.medtrum.extension.toByteArray
import org.junit.jupiter.api.Test
import org.junit.Assert.*
class AuthorizePacketTest : MedtrumTestBase() {
/** Test packet specific behavoir */
private val packetInjector = HasAndroidInjector {
AndroidInjector {
if (it is MedtrumPacket) {
it.aapsLogger = aapsLogger
}
}
}
@Test fun getRequestGivenPacketAndSNWhenCalledThenReturnAuthorizePacket() {
// Inputs
val opCode = 5
val sn = 2859923929
// Call
val packet = AuthorizePacket(packetInjector, sn)
val result = packet.getRequest()
// Expected values
val key = 3364239851
val type = 2
val expectedByteArray = byteArrayOf(opCode.toByte()) + type.toByte() + 0.toByteArray(4) + key.toByteArray(4)
assertEquals(10, result.size)
assertEquals(expectedByteArray.contentToString(), result.contentToString())
}
@Test fun handleResponseGivenResponseWhenMessageIsCorrectLengthThenResultTrue() {
// Inputs
val opCode = 5
val responseCode = 0
val deviceType = 80
val swVerX = 12
val swVerY = 1
val swVerZ = 3
// Call
val response = byteArrayOf(0) + opCode.toByte() + 0.toByteArray(2) + responseCode.toByteArray(2) + 0.toByte() + deviceType.toByte() + swVerX.toByte() + swVerY.toByte() + swVerZ.toByte()
val packet = AuthorizePacket(packetInjector, 0)
val result = packet.handleResponse(response)
// Expected values
val swString = "$swVerX.$swVerY.$swVerZ"
assertEquals(true, result)
assertEquals(false, packet.failed)
assertEquals(deviceType, packet.deviceType)
assertEquals(swString, packet.swVersion)
}
@Test fun handleResponseGivenResponseWhenMessageTooShortThenResultFalse() {
// Inputs
val opCode = 5
val responseCode = 0
// Call
val response = byteArrayOf(0) + opCode.toByte() + 0.toByteArray(2) + responseCode.toByteArray(2)
val packet = AuthorizePacket(packetInjector, 0)
packet.opCode = opCode.toByte()
val result = packet.handleResponse(response)
// Expected values
assertEquals(false, result)
assertEquals(true, packet.failed)
}
}

View file

@ -0,0 +1,33 @@
package info.nightscout.pump.medtrum.comm.packets
import dagger.android.AndroidInjector
import dagger.android.HasAndroidInjector
import info.nightscout.pump.medtrum.MedtrumTestBase
import org.junit.jupiter.api.Test
import org.junit.Assert.*
class CancelBolusPacketTest : MedtrumTestBase() {
/** Test packet specific behavoir */
private val packetInjector = HasAndroidInjector {
AndroidInjector {
if (it is MedtrumPacket) {
it.aapsLogger = aapsLogger
}
}
}
@Test fun getRequestGivenPacketWhenCalledThenReturnOpCode() {
// Inputs
val opCode = 20
// Call
val packet = CancelBolusPacket(packetInjector)
val result = packet.getRequest()
// Expected values
assertEquals(1, result.size)
assertEquals(opCode.toByte(), result[0])
}
}

View file

@ -0,0 +1,33 @@
package info.nightscout.pump.medtrum.comm.packets
import dagger.android.AndroidInjector
import dagger.android.HasAndroidInjector
import info.nightscout.pump.medtrum.MedtrumTestBase
import org.junit.jupiter.api.Test
import org.junit.Assert.*
class CancelTempBasalPacketTest : MedtrumTestBase() {
/** Test packet specific behavoir */
private val packetInjector = HasAndroidInjector {
AndroidInjector {
if (it is MedtrumPacket) {
it.aapsLogger = aapsLogger
}
}
}
@Test fun getRequestGivenPacketWhenCalledThenReturnOpCode() {
// Inputs
val opCode = 25
// Call
val packet = CancelTempBasalPacket(packetInjector)
val result = packet.getRequest()
// Expected values
assertEquals(1, result.size)
assertEquals(opCode.toByte(), result[0])
}
}

View file

@ -0,0 +1,73 @@
package info.nightscout.pump.medtrum.comm.packets
import dagger.android.AndroidInjector
import dagger.android.HasAndroidInjector
import info.nightscout.pump.medtrum.MedtrumTestBase
import info.nightscout.pump.medtrum.extension.toByteArray
import org.junit.jupiter.api.Test
import org.junit.Assert.*
class GetDeviceTypePacketTest : MedtrumTestBase() {
/** Test packet specific behavoir */
private val packetInjector = HasAndroidInjector {
AndroidInjector {
if (it is MedtrumPacket) {
it.aapsLogger = aapsLogger
}
}
}
@Test fun getRequestGivenPacketWhenCalledThenReturnOpCode() {
// Inputs
val opCode = 6
// Call
val packet = GetDeviceTypePacket(packetInjector)
val result = packet.getRequest()
// Expected values
assertEquals(1, result.size)
assertEquals(opCode.toByte(), result[0])
}
@Test fun handleResponseGivenResponseWhenMessageIsCorrectLengthThenResultTrue() {
// Inputs
val opCode = 6
val responseCode = 0
val deviceType = 80
val deviceSN: Long = 12345678
val response = byteArrayOf(0) + opCode.toByte() + 0.toByteArray(2) + responseCode.toByteArray(2) + deviceType.toByte() + deviceSN.toByteArray(4)
// Call
val packet = GetDeviceTypePacket(packetInjector)
val result = packet.handleResponse(response)
// Expected values
assertEquals(true, result)
assertEquals(false, packet.failed)
assertEquals(deviceType, packet.deviceType)
assertEquals(deviceSN, packet.deviceSN)
}
@Test fun handleResponseGivenResponseWhenMessageTooShortThenResultFalse() {
// Inputs
val opCode = 6
val responseCode = 0
val deviceType = 80
val deviceSN: Long = 12345678
val response = byteArrayOf(0) + opCode.toByte() + 0.toByteArray(2) + responseCode.toByteArray(2) + deviceType.toByte() + deviceSN.toByteArray(4)
// Call
val packet = GetDeviceTypePacket(packetInjector)
val result = packet.handleResponse(response.sliceArray(0..response.size - 2))
// Expected values
assertEquals(false, result)
assertEquals(true, packet.failed)
assertEquals(0, packet.deviceType)
assertEquals(0, packet.deviceSN)
}
}

View file

@ -0,0 +1,68 @@
package info.nightscout.pump.medtrum.comm.packets
import dagger.android.AndroidInjector
import dagger.android.HasAndroidInjector
import info.nightscout.pump.medtrum.MedtrumTestBase
import info.nightscout.pump.medtrum.extension.toByteArray
import org.junit.jupiter.api.Test
import org.junit.Assert.*
class GetTimePacketTest : MedtrumTestBase() {
/** Test packet specific behavoir */
private val packetInjector = HasAndroidInjector {
AndroidInjector {
if (it is MedtrumPacket) {
it.aapsLogger = aapsLogger
}
}
}
@Test fun getRequestGivenPacketWhenCalledThenReturnOpCode() {
// Inputs
val opCode = 11
// Call
val packet = GetTimePacket(packetInjector)
val result = packet.getRequest()
// Expected values
assertEquals(1, result.size)
assertEquals(opCode.toByte(), result[0])
}
@Test fun handleResponseGivenResponseWhenMessageIsCorrectLengthThenResultTrue() {
// Inputs
val opCode = 11
val responseCode = 0
val time: Long = 1234567890
val response = byteArrayOf(0) + opCode.toByte() + 0.toByteArray(2) + responseCode.toByteArray(2) + time.toByteArray(4)
// Call
val packet = GetTimePacket(packetInjector)
val result = packet.handleResponse(response)
// Expected values
assertEquals(true, result)
assertEquals(false, packet.failed)
assertEquals(time, packet.time)
}
@Test fun handleResponseGivenResponseWhenMessageTooShortThenResultFalse() {
// Inputs
val opCode = 11
val responseCode = 0
val time: Long = 1234567890
val response = byteArrayOf(0) + opCode.toByte() + 0.toByteArray(2) + responseCode.toByteArray(2) + time.toByteArray(4)
// Call
val packet = GetTimePacket(packetInjector)
val result = packet.handleResponse(response.sliceArray(0..response.size - 2))
// Expected values
assertEquals(false, result)
assertEquals(true, packet.failed)
assertEquals(0, packet.time)
}
}

View file

@ -0,0 +1,115 @@
package info.nightscout.pump.medtrum.comm.packets
import dagger.android.AndroidInjector
import dagger.android.HasAndroidInjector
import info.nightscout.pump.medtrum.MedtrumTestBase
import info.nightscout.pump.medtrum.extension.toByteArray
import org.junit.jupiter.api.Test
import org.junit.Assert.*
class MedtrumPacketTest : MedtrumTestBase() {
/** Test base behavoir of the medtrum packet, thse */
private val packetInjector = HasAndroidInjector {
AndroidInjector {
if (it is MedtrumPacket) {
it.aapsLogger = aapsLogger
}
}
}
@Test fun getRequestGivenPacketWhenCalledThenReturnOpCode() {
// Inputs
val opCode = 1
// Call
val packet = MedtrumPacket(packetInjector)
packet.opCode = opCode.toByte()
val result = packet.getRequest()
// Expected values
assertEquals(result.size, 1)
assertEquals(result[0], opCode.toByte())
}
@Test fun handleResponseGivenResponseWhenOpcodeIsCorrectThenResultTrue() {
// Inputs
val opCode = 1
val responseCode = 0
val response = byteArrayOf(0) + opCode.toByte() + 0x0 + 0x0 + responseCode.toByteArray(2)
// Call
val packet = MedtrumPacket(packetInjector)
packet.opCode = opCode.toByte()
val result = packet.handleResponse(response)
// Expected values
assertEquals(result, true)
assertEquals(packet.failed, false)
}
@Test fun handleResponseGivenRepsonseWhenOpcodeIsIncorrectThenResultFalse() {
// Inputs
val opCode = 1
val responseCode = 0
val response = byteArrayOf(0) + (opCode + 1).toByte() + 0x0 + 0x0 + responseCode.toByteArray(2)
// Call
val packet = MedtrumPacket(packetInjector)
packet.opCode = opCode.toByte()
val result = packet.handleResponse(response)
// Expected values
assertEquals(result, false)
assertEquals(packet.failed, true)
}
@Test fun handleResponseGivenResponseWhenResponseCodeIsWaitingThenResultFalse() {
// Inputs
val opCode = 1
val responseCode = 16384
val response = byteArrayOf(0) + opCode.toByte() + 0x0 + 0x0 + responseCode.toByteArray(2)
// Call
val packet = MedtrumPacket(packetInjector)
packet.opCode = opCode.toByte()
val result = packet.handleResponse(response)
// Expected values
assertEquals(result, false)
assertEquals(packet.failed, false)
}
@Test fun handleResponseGivenResponseWhenRepsonseCodeIsErrorThenResultFalse() {
// Inputs
val opCode = 1
val responseCode = 1
val response = byteArrayOf(0) + opCode.toByte() + 0.toByteArray(2) + responseCode.toByteArray(2)
// Call
val packet = MedtrumPacket(packetInjector)
packet.opCode = opCode.toByte()
val result = packet.handleResponse(response)
// Expected values
assertEquals(false, result)
assertEquals(true, packet.failed)
}
@Test fun handleResponseGivenResponseWhenMessageTooShortThenResultFalse() {
// Inputs
val opCode = 1
val responseCode = 0
val response = byteArrayOf(0) + opCode.toByte() + 0x0 + 0x0
// Call
val packet = MedtrumPacket(packetInjector)
packet.opCode = opCode.toByte()
val result = packet.handleResponse(response)
// Expected values
assertEquals(false, result)
assertEquals(true, packet.failed)
}
}

View file

@ -0,0 +1,33 @@
package info.nightscout.pump.medtrum.comm.packets
import dagger.android.AndroidInjector
import dagger.android.HasAndroidInjector
import info.nightscout.pump.medtrum.MedtrumTestBase
import org.junit.jupiter.api.Test
import org.junit.Assert.*
class PollPatchPacketTest : MedtrumTestBase() {
/** Test packet specific behavoir */
private val packetInjector = HasAndroidInjector {
AndroidInjector {
if (it is MedtrumPacket) {
it.aapsLogger = aapsLogger
}
}
}
@Test fun getRequestGivenPacketWhenCalledThenReturnOpCode() {
// Inputs
val opCode = 30
// Call
val packet = PollPatchPacket(packetInjector)
val result = packet.getRequest()
// Expected values
assertEquals(1, result.size)
assertEquals(opCode.toByte(), result[0])
}
}

View file

@ -0,0 +1,33 @@
package info.nightscout.pump.medtrum.comm.packets
import dagger.android.AndroidInjector
import dagger.android.HasAndroidInjector
import info.nightscout.pump.medtrum.MedtrumTestBase
import org.junit.jupiter.api.Test
import org.junit.Assert.*
class PrimePacketTest : MedtrumTestBase() {
/** Test packet specific behavoir */
private val packetInjector = HasAndroidInjector {
AndroidInjector {
if (it is MedtrumPacket) {
it.aapsLogger = aapsLogger
}
}
}
@Test fun getRequestGivenPacketWhenCalledThenReturnOpCode() {
// Inputs
val opCode = 16
// Call
val packet = PrimePacket(packetInjector)
val result = packet.getRequest()
// Expected values
assertEquals(1, result.size)
assertEquals(opCode.toByte(), result[0])
}
}

View file

@ -0,0 +1,54 @@
package info.nightscout.pump.medtrum.comm.packets
import dagger.android.AndroidInjector
import dagger.android.HasAndroidInjector
import info.nightscout.pump.medtrum.MedtrumTestBase
import info.nightscout.pump.medtrum.extension.toByteArray
import org.junit.jupiter.api.Test
import org.junit.Assert.*
class ReadBolusStatePacketTest : MedtrumTestBase() {
/** Test packet specific behavoir */
private val packetInjector = HasAndroidInjector {
AndroidInjector {
if (it is MedtrumPacket) {
it.aapsLogger = aapsLogger
}
}
}
@Test fun handleResponseGivenResponseWhenMessageIsCorrectLengthThenResultTrue() {
// Inputs
val opCode = 34
val responseCode = 0
val bolusData = byteArrayOf(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16)
val response = byteArrayOf(0) + opCode.toByte() + 0.toByteArray(2) + responseCode.toByteArray(2) + bolusData
// Call
val packet = ReadBolusStatePacket(packetInjector)
val result = packet.handleResponse(response)
// Expected values
assertEquals(true, result)
assertEquals(false, packet.failed)
assertEquals(bolusData.contentToString(), packet.bolusData.contentToString())
}
@Test fun handleResponseGivenResponseWhenMessageTooShortThenResultFalse() {
// Inputs
val opCode = 34
val responseCode = 0
val response = byteArrayOf(0) + opCode.toByte() + 0.toByteArray(2) + responseCode.toByteArray(2)
// Call
val packet = ReadBolusStatePacket(packetInjector)
val result = packet.handleResponse(response)
// Expected values
assertEquals(false, result)
assertEquals(true, packet.failed)
assertEquals(byteArrayOf().contentToString(), packet.bolusData.contentToString())
}
}

View file

@ -0,0 +1,33 @@
package info.nightscout.pump.medtrum.comm.packets
import dagger.android.AndroidInjector
import dagger.android.HasAndroidInjector
import info.nightscout.pump.medtrum.MedtrumTestBase
import org.junit.jupiter.api.Test
import org.junit.Assert.*
class SetBasalProfilePacketTest : MedtrumTestBase() {
/** Test packet specific behavoir */
private val packetInjector = HasAndroidInjector {
AndroidInjector {
if (it is MedtrumPacket) {
it.aapsLogger = aapsLogger
}
}
}
@Test fun getRequestGivenPacketWhenCalledThenReturnOpCode() {
// Inputs
val opCode = 21
// Call
val packet = SetBasalProfilePacket(packetInjector)
val result = packet.getRequest()
// Expected values
assertEquals(1, result.size)
assertEquals(opCode.toByte(), result[0])
}
}

View file

@ -0,0 +1,34 @@
package info.nightscout.pump.medtrum.comm.packets
import dagger.android.AndroidInjector
import dagger.android.HasAndroidInjector
import info.nightscout.pump.medtrum.MedtrumTestBase
import org.junit.jupiter.api.Test
import org.junit.Assert.*
class SetBolusMotorPacketTest : MedtrumTestBase() {
/** Test packet specific behavoir */
private val packetInjector = HasAndroidInjector {
AndroidInjector {
if (it is MedtrumPacket) {
it.aapsLogger = aapsLogger
}
}
}
@Test fun getRequestGivenPacketWhenCalledThenReturnOpCode() {
// Inputs
val opCode = 36
// Call
val packet = SetBolusMotorPacket(packetInjector)
val result = packet.getRequest()
// Expected values
val expectedByteArray = byteArrayOf(opCode.toByte()) + 0.toByte()
assertEquals(2, result.size)
assertEquals(expectedByteArray.contentToString(), result.contentToString())
}
}

View file

@ -0,0 +1,34 @@
package info.nightscout.pump.medtrum.comm.packets
import dagger.android.AndroidInjector
import dagger.android.HasAndroidInjector
import info.nightscout.pump.medtrum.MedtrumTestBase
import org.junit.jupiter.api.Test
import org.junit.Assert.*
class SetBolusPacketTest : MedtrumTestBase() {
/** Test packet specific behavoir */
private val packetInjector = HasAndroidInjector {
AndroidInjector {
if (it is MedtrumPacket) {
it.aapsLogger = aapsLogger
}
}
}
@Test fun getRequestGivenPacketWhenCalledThenReturnOpCode() {
// Inputs
val opCode = 19
// Call
val packet = SetBolusPacket(packetInjector)
val result = packet.getRequest()
// Expected values
// TODO correct value's
assertEquals(1, result.size)
assertEquals(opCode.toByte(), result[0])
}
}

View file

@ -0,0 +1,34 @@
package info.nightscout.pump.medtrum.comm.packets
import dagger.android.AndroidInjector
import dagger.android.HasAndroidInjector
import info.nightscout.pump.medtrum.MedtrumTestBase
import org.junit.jupiter.api.Test
import org.junit.Assert.*
class SetPatchPacket : MedtrumTestBase() {
/** Test packet specific behavoir */
private val packetInjector = HasAndroidInjector {
AndroidInjector {
if (it is MedtrumPacket) {
it.aapsLogger = aapsLogger
}
}
}
@Test fun getRequestGivenPacketWhenCalledThenReturnOpCode() {
// Inputs
val opCode = 35
// Call
val packet = SetBolusPacket(packetInjector)
val result = packet.getRequest()
// Expected values
// TODO correct value's
assertEquals(1, result.size)
assertEquals(opCode.toByte(), result[0])
}
}

View file

@ -0,0 +1,62 @@
package info.nightscout.pump.medtrum.comm.packets
import dagger.android.AndroidInjector
import dagger.android.HasAndroidInjector
import info.nightscout.pump.medtrum.MedtrumTestBase
import info.nightscout.pump.medtrum.extension.toByteArray
import org.junit.jupiter.api.Test
import org.junit.Assert.*
class SetTempBasalPacketTest : MedtrumTestBase() {
/** Test packet specific behavoir */
private val packetInjector = HasAndroidInjector {
AndroidInjector {
if (it is MedtrumPacket) {
it.aapsLogger = aapsLogger
}
}
}
@Test fun getRequestGivenPacketWhenCalledThenReturnOpCode() {
// Inputs
val opCode = 24
// Call
val packet = SetTempBasalPacket(packetInjector)
val result = packet.getRequest()
// Expected values
assertEquals(1, result.size)
assertEquals(opCode.toByte(), result[0])
}
@Test fun getRequestGivenPacketWhenCalledThenReturnOpCodeAndDuration() {
// TODO
}
@Test fun getRequestGivenPacketWhenCalledThenReturnOpCodeAndDurationAndRate() {
// TODO
}
@Test fun handleResponseGivenResponseWhenMessageIsCorrectLengthThenResultTrue() {
// TODO
}
@Test fun handleResponseGivenResponseWhenMessageTooShortThenResultFalse() {
// Inputs
val opCode = 24
val responseCode = 0
val response = byteArrayOf(0) + opCode.toByte() + 0.toByteArray(2) + responseCode.toByteArray(2)
// Call
val packet = ReadBolusStatePacket(packetInjector)
val result = packet.handleResponse(response)
// Expected values
assertEquals(false, result)
assertEquals(true, packet.failed)
// assertEquals(byteArrayOf().contentToString(), packet.bolusData.contentToString()) TODO
}
}

View file

@ -0,0 +1,37 @@
package info.nightscout.pump.medtrum.comm.packets
import dagger.android.AndroidInjector
import dagger.android.HasAndroidInjector
import info.nightscout.pump.medtrum.MedtrumTestBase
import info.nightscout.pump.medtrum.extension.toByteArray
import info.nightscout.pump.medtrum.util.MedtrumTimeUtil
import org.junit.jupiter.api.Test
import org.junit.Assert.*
class SetTimePacketTest : MedtrumTestBase() {
/** Test packet specific behavoir */
private val packetInjector = HasAndroidInjector {
AndroidInjector {
if (it is MedtrumPacket) {
it.aapsLogger = aapsLogger
}
}
}
@Test fun getRequestGivenPacketWhenCalledThenReturnOpCode() {
// Inputs
val opCode = 10
val time = MedtrumTimeUtil().getCurrentTimePumpSeconds() // TODO: Mock time proper?
// Call
val packet = SetTimePacket(packetInjector)
val result = packet.getRequest()
// Expected values
val expectedByteArray = byteArrayOf(opCode.toByte()) + 2.toByte() + time.toByteArray(4)
assertEquals(6, result.size)
assertEquals(expectedByteArray.contentToString(), result.contentToString())
}
}

View file

@ -0,0 +1,41 @@
package info.nightscout.pump.medtrum.comm.packets
import dagger.android.AndroidInjector
import dagger.android.HasAndroidInjector
import info.nightscout.pump.medtrum.MedtrumTestBase
import info.nightscout.pump.medtrum.extension.toByteArray
import info.nightscout.pump.medtrum.util.MedtrumTimeUtil
import org.junit.jupiter.api.Test
import org.junit.Assert.*
class SetTimeZonePacketTest : MedtrumTestBase() {
/** Test packet specific behavoir */
private val packetInjector = HasAndroidInjector {
AndroidInjector {
if (it is MedtrumPacket) {
it.aapsLogger = aapsLogger
}
if (it is SetTimeZonePacket) {
it.dateUtil = dateUtil
}
}
}
@Test fun getRequestGivenPacketWhenCalledThenReturnOpCode() {
// Inputs
val opCode = 12
val time = MedtrumTimeUtil().getCurrentTimePumpSeconds() // TODO: Mock time proper?
val offsetMins = dateUtil.getTimeZoneOffsetMinutes(dateUtil.now()) // TODO: Mock time proper?
// Call
val packet = SetTimeZonePacket(packetInjector)
val result = packet.getRequest()
// Expected values
val expectedByteArray = byteArrayOf(opCode.toByte()) + offsetMins.toByteArray(2) + time.toByteArray(4)
assertEquals(7, result.size)
assertEquals(expectedByteArray.contentToString(), result.contentToString())
}
}

View file

@ -0,0 +1,35 @@
package info.nightscout.pump.medtrum.comm.packets
import dagger.android.AndroidInjector
import dagger.android.HasAndroidInjector
import info.nightscout.pump.medtrum.MedtrumTestBase
import org.junit.jupiter.api.Test
import org.junit.Assert.*
class StopPatchPacketTest : MedtrumTestBase() {
/** Test packet specific behavoir */
private val packetInjector = HasAndroidInjector {
AndroidInjector {
if (it is MedtrumPacket) {
it.aapsLogger = aapsLogger
}
}
}
@Test fun getRequestGivenPacketWhenCalledThenReturnOpCode() {
// Inputs
val opCode = 31
// Call
val packet = StopPatchPacket(packetInjector)
val result = packet.getRequest()
// Expected values
assertEquals(1, result.size)
assertEquals(opCode.toByte(), result[0])
}
// TODO: Add tests for the response
}

View file

@ -0,0 +1,35 @@
package info.nightscout.pump.medtrum.comm.packets
import dagger.android.AndroidInjector
import dagger.android.HasAndroidInjector
import info.nightscout.pump.medtrum.MedtrumTestBase
import info.nightscout.pump.medtrum.extension.toByteArray
import org.junit.jupiter.api.Test
import org.junit.Assert.*
class SubscribePacketTest : MedtrumTestBase() {
/** Test packet specific behavoir */
private val packetInjector = HasAndroidInjector {
AndroidInjector {
if (it is MedtrumPacket) {
it.aapsLogger = aapsLogger
}
}
}
@Test fun getRequestGivenPacketWhenCalledThenReturnOpCode() {
// Inputs
val opCode = 4
// Call
val packet = SubscribePacket(packetInjector)
val result = packet.getRequest()
// Expected values
val expectedByteArray = byteArrayOf(opCode.toByte()) + 4095.toByteArray(2)
assertEquals(3, result.size)
assertEquals(expectedByteArray.contentToString(), result.contentToString())
}
}

View file

@ -0,0 +1,72 @@
package info.nightscout.pump.medtrum.comm.packets
import dagger.android.AndroidInjector
import dagger.android.HasAndroidInjector
import info.nightscout.pump.medtrum.MedtrumTestBase
import info.nightscout.pump.medtrum.extension.toByteArray
import org.junit.jupiter.api.Test
import org.junit.Assert.*
class SynchronizePacketTest : MedtrumTestBase() {
/** Test packet specific behavoir */
private val packetInjector = HasAndroidInjector {
AndroidInjector {
if (it is MedtrumPacket) {
it.aapsLogger = aapsLogger
}
}
}
@Test fun getRequestGivenPacketWhenCalledThenReturnOpCode() {
// Inputs
val opCode = 3
// Call
val packet = SynchronizePacket(packetInjector)
val result = packet.getRequest()
// Expected values
assertEquals(1, result.size)
assertEquals(opCode.toByte(), result[0])
}
@Test fun handleResponseGivenResponseWhenMessageIsCorrectLengthThenResultTrue() {
// Inputs
val opCode = 3
val responseCode = 0
val state = 1
val dataFieldsPresent = 4046
val syncData = byteArrayOf(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15)
val response = byteArrayOf(0) + opCode.toByte() + 0.toByteArray(2) + responseCode.toByteArray(2) + state.toByteArray(1) + dataFieldsPresent.toByteArray(2) + syncData
// Call
val packet = SynchronizePacket(packetInjector)
val result = packet.handleResponse(response)
// Expected values
assertEquals(true, result)
assertEquals(false, packet.failed)
assertEquals(state, packet.state)
assertEquals(dataFieldsPresent, packet.dataFieldsPresent)
assertEquals(syncData.contentToString(), packet.syncData.contentToString())
}
@Test fun handleResponseGivenResponseWhenMessageTooShortThenResultFalse() {
// Inputs
val opCode = 3
val responseCode = 0
val state = 1
val dataFieldsPresent = 4046
val response = byteArrayOf(0) + opCode.toByte() + 0.toByteArray(2) + responseCode.toByteArray(2) + state.toByteArray(1) + dataFieldsPresent.toByteArray(2)
// Call
val packet = SynchronizePacket(packetInjector)
val result = packet.handleResponse(response)
// Expected values
assertEquals(false, result)
assertEquals(true, packet.failed)
}
}