diff --git a/jdrfandroidbleparser/src/main/java/org/ehealthinnovation/jdrfandroidbleparser/bgm/characteristic/compoundcharacteristic/GlucoseFeatureCharacteristic.kt b/jdrfandroidbleparser/src/main/java/org/ehealthinnovation/jdrfandroidbleparser/bgm/characteristic/compoundcharacteristic/GlucoseFeatureCharacteristic.kt index 5bfcdb7..c995cc2 100644 --- a/jdrfandroidbleparser/src/main/java/org/ehealthinnovation/jdrfandroidbleparser/bgm/characteristic/compoundcharacteristic/GlucoseFeatureCharacteristic.kt +++ b/jdrfandroidbleparser/src/main/java/org/ehealthinnovation/jdrfandroidbleparser/bgm/characteristic/compoundcharacteristic/GlucoseFeatureCharacteristic.kt @@ -1,7 +1,7 @@ package org.ehealthinnovation.jdrfandroidbleparser.bgm.characteristic.compoundcharacteristic import android.bluetooth.BluetoothGattCharacteristic -import org.ehealthinnovation.jdrfandroidbleparser.bgm.characteristic.common.BaseCharacteristic +import org.ehealthinnovation.jdrfandroidbleparser.common.BaseCharacteristic import org.ehealthinnovation.jdrfandroidbleparser.encodedvalue.GattCharacteristic import org.ehealthinnovation.jdrfandroidbleparser.encodedvalue.bgm.feature.Flags import java.util.* diff --git a/jdrfandroidbleparser/src/main/java/org/ehealthinnovation/jdrfandroidbleparser/bgm/characteristic/compoundcharacteristic/GlucoseMeasurementCharacteristic.kt b/jdrfandroidbleparser/src/main/java/org/ehealthinnovation/jdrfandroidbleparser/bgm/characteristic/compoundcharacteristic/GlucoseMeasurementCharacteristic.kt index d6c9c7a..b754ecb 100644 --- a/jdrfandroidbleparser/src/main/java/org/ehealthinnovation/jdrfandroidbleparser/bgm/characteristic/compoundcharacteristic/GlucoseMeasurementCharacteristic.kt +++ b/jdrfandroidbleparser/src/main/java/org/ehealthinnovation/jdrfandroidbleparser/bgm/characteristic/compoundcharacteristic/GlucoseMeasurementCharacteristic.kt @@ -1,15 +1,14 @@ package org.ehealthinnovation.jdrfandroidbleparser.bgm.characteristic.compoundcharacteristic import android.bluetooth.BluetoothGattCharacteristic -import android.util.Log -import org.ehealthinnovation.jdrfandroidbleparser.bgm.characteristic.common.BaseCharacteristic import org.ehealthinnovation.jdrfandroidbleparser.bgm.encodedvalue.bgmmeasurement.Flags import org.ehealthinnovation.jdrfandroidbleparser.bgm.encodedvalue.bgmmeasurement.SampleLocation import org.ehealthinnovation.jdrfandroidbleparser.bgm.encodedvalue.bgmmeasurement.SensorStatusAnnunciation import org.ehealthinnovation.jdrfandroidbleparser.bgm.encodedvalue.bgmmeasurement.Type -import org.ehealthinnovation.jdrfandroidbleparser.bgm.utility.BluetoothDateTime +import org.ehealthinnovation.jdrfandroidbleparser.common.BaseCharacteristic import org.ehealthinnovation.jdrfandroidbleparser.encodedvalue.GattCharacteristic import org.ehealthinnovation.jdrfandroidbleparser.encodedvalue.Units +import org.ehealthinnovation.jdrfandroidbleparser.utility.BluetoothDateTime import java.util.* /** @@ -81,8 +80,7 @@ class GlucoseMeasurementCharacteristic(characteristic: BluetoothGattCharacterist sampleLocation = SampleLocation.fromKey((tempIntHolder and 0xF0) shr 4) if (it.contains(Flags.GLUCOSE_CONCENTRATION_UNITS)) { unit = Units.MOLE_PER_LITRE - } - else { + } else { unit = Units.KILOGRAM_PER_LITRE } } diff --git a/jdrfandroidbleparser/src/main/java/org/ehealthinnovation/jdrfandroidbleparser/bgm/characteristic/compoundcharacteristic/GlucoseMeasurementContextCharacteristic.kt b/jdrfandroidbleparser/src/main/java/org/ehealthinnovation/jdrfandroidbleparser/bgm/characteristic/compoundcharacteristic/GlucoseMeasurementContextCharacteristic.kt index caa6208..6332153 100644 --- a/jdrfandroidbleparser/src/main/java/org/ehealthinnovation/jdrfandroidbleparser/bgm/characteristic/compoundcharacteristic/GlucoseMeasurementContextCharacteristic.kt +++ b/jdrfandroidbleparser/src/main/java/org/ehealthinnovation/jdrfandroidbleparser/bgm/characteristic/compoundcharacteristic/GlucoseMeasurementContextCharacteristic.kt @@ -1,7 +1,7 @@ package org.ehealthinnovation.jdrfandroidbleparser.bgm.characteristic.compoundcharacteristic import android.bluetooth.BluetoothGattCharacteristic -import org.ehealthinnovation.jdrfandroidbleparser.bgm.characteristic.common.BaseCharacteristic +import org.ehealthinnovation.jdrfandroidbleparser.common.BaseCharacteristic import org.ehealthinnovation.jdrfandroidbleparser.encodedvalue.GattCharacteristic import org.ehealthinnovation.jdrfandroidbleparser.encodedvalue.Units import org.ehealthinnovation.jdrfandroidbleparser.encodedvalue.bgm.contextmeasurement.* diff --git a/jdrfandroidbleparser/src/main/java/org/ehealthinnovation/jdrfandroidbleparser/bgm/characteristic/compoundcharacteristic/PnPIdCharacteristic.kt b/jdrfandroidbleparser/src/main/java/org/ehealthinnovation/jdrfandroidbleparser/bgm/characteristic/compoundcharacteristic/PnPIdCharacteristic.kt index df37fe7..dd9d526 100644 --- a/jdrfandroidbleparser/src/main/java/org/ehealthinnovation/jdrfandroidbleparser/bgm/characteristic/compoundcharacteristic/PnPIdCharacteristic.kt +++ b/jdrfandroidbleparser/src/main/java/org/ehealthinnovation/jdrfandroidbleparser/bgm/characteristic/compoundcharacteristic/PnPIdCharacteristic.kt @@ -1,11 +1,10 @@ package org.ehealthinnovation.jdrfandroidbleparser.bgm.characteristic.compoundcharacteristic import android.bluetooth.BluetoothGattCharacteristic -import org.ehealthinnovation.jdrfandroidbleparser.bgm.characteristic.common.BaseCharacteristic +import org.ehealthinnovation.jdrfandroidbleparser.common.BaseCharacteristic import org.ehealthinnovation.jdrfandroidbleparser.encodedvalue.GattCharacteristic import org.ehealthinnovation.jdrfandroidbleparser.encodedvalue.dis.pnpid.VendorId import org.ehealthinnovation.jdrfandroidbleparser.encodedvalue.dis.pnpid.VendorId.Companion.fromVendorId -import kotlin.jvm.java /** * The PnP_ID characteristic is a set of values that used to create a device ID value that is diff --git a/jdrfandroidbleparser/src/main/java/org/ehealthinnovation/jdrfandroidbleparser/bgm/characteristic/compoundcharacteristic/RACP.kt b/jdrfandroidbleparser/src/main/java/org/ehealthinnovation/jdrfandroidbleparser/bgm/characteristic/compoundcharacteristic/RACP.kt new file mode 100644 index 0000000..aee7074 --- /dev/null +++ b/jdrfandroidbleparser/src/main/java/org/ehealthinnovation/jdrfandroidbleparser/bgm/characteristic/compoundcharacteristic/RACP.kt @@ -0,0 +1,340 @@ +package org.ehealthinnovation.jdrfandroidbleparser.bgm.characteristic.compoundcharacteristic + +import android.bluetooth.BluetoothGattCharacteristic +import android.util.Log +import org.ehealthinnovation.jdrfandroidbleparser.common.BaseCharacteristic +import org.ehealthinnovation.jdrfandroidbleparser.common.Composable +import org.ehealthinnovation.jdrfandroidbleparser.encodedvalue.GattCharacteristic +import org.ehealthinnovation.jdrfandroidbleparser.encodedvalue.bgm.racp.Filter +import org.ehealthinnovation.jdrfandroidbleparser.encodedvalue.bgm.racp.Opcode +import org.ehealthinnovation.jdrfandroidbleparser.encodedvalue.bgm.racp.Operator +import org.ehealthinnovation.jdrfandroidbleparser.encodedvalue.bgm.racp.ResponseCode +import org.ehealthinnovation.jdrfandroidbleparser.utility.BluetoothDateTime + + +/** + * This control point is used with a service to provide basic management functionality for the + * Glucose Sensor patient record database. This enables functions including counting records, + * transmitting records and clearing records based on filter criterion. The filter criterion in + * the Operand field is defined by the service that references this characteristic as is the format + * of a record (which may be comprised of one or more characteristics) and the sequence of + * transferred records. + */ +class RACP : BaseCharacteristic, Composable { + override val tag: String = RACP::class.java.canonicalName + + constructor(c: BluetoothGattCharacteristic, hasCrc: Boolean = false, isComposing: Boolean = false) : + super(c, GattCharacteristic.RECORD_ACCESS_CONTROL_POINT.assigned, hasCrc, isComposing) { + this.hasCrc = hasCrc + } + + + /** + * Op Code specifying the operation + */ + var opcode: Opcode? = null + + /** + * [operator] is a modifier to the operand. For example, [Operator.LAST_RECORD], + * [Operator.LESS_THAN_OR_EQUAL_TO] + */ + var operator: Operator? = null + + /** Since the value of the Operand is defined per service, when the RACP is used with the + * Glucose Service, a Filter Type field is defined to enable the flexibility to filter based + * on different criteria (i.e., Sequence Number or optionally User Facing Time). + */ + var filterType: Filter? = null + + /** + * The following variables stores the possible fields which constitute operands. + */ + var minimumFilterValueSequenceNumber: Int? = null + var maximumFilterValueSequenceNumber: Int? = null + var minimumFilterValueUserFacingTime: BluetoothDateTime? = null + var maximumFilterValueUserFacingTime: BluetoothDateTime? = null + + /** + * When the Report Number of Stored Records Op Code is written to the Record Access Control + * Point, the Server shall calculate and respond with a record count in UINT16 format based on + * filter criteria, Operator and Operand values. + */ + var numberOfRecordResponse: Int? = null + var requestOpcode: Opcode? = null + var responseCode: ResponseCode? = null + + /** + * Indicate whether the packet requires CRC + */ + var hasCrc: Boolean + + /** + * Main entry point for parsing a [BluetoothGattCharacteristic] + */ + override fun parse(c: BluetoothGattCharacteristic): Boolean { + var errorFreeParsing = false + opcode = Opcode.fromKey(getNextIntValue(c, BluetoothGattCharacteristic.FORMAT_UINT8)) + //Extract operator + when (opcode) { + null -> { + Log.e(tag, "Opcode is null, packet might have been corrucpted") + return errorFreeParsing + } + Opcode.REPORT_NUMBER_OF_STORED_RECORDS, + Opcode.DELETE_STORED_RECORDS, + Opcode.REPORT_STORED_RECORDS -> { + operator = Operator.fromKey(getNextIntValue(c, BluetoothGattCharacteristic.FORMAT_UINT8)) + parseGenericOperand(c) + } + Opcode.ABORT_OPERATION -> { + operator = Operator.fromKey(getNextIntValue(c, BluetoothGattCharacteristic.FORMAT_UINT8)) + if (operator != Operator.NULL) { + Log.e(tag, "operator must be null, when opcode is Abort Operation") + return errorFreeParsing + } + } + Opcode.NUMBER_OF_STORED_RECORDS_RESPONSE -> { + operator = Operator.fromKey(getNextIntValue(c, BluetoothGattCharacteristic.FORMAT_UINT8)) + if (operator != Operator.NULL) { + Log.e(tag, "operator must be null, when opcode is Abort Operation") + return errorFreeParsing + } + numberOfRecordResponse = getNextIntValue(c, BluetoothGattCharacteristic.FORMAT_UINT16) + } + Opcode.RESPONSE_CODE -> { + operator = Operator.fromKey(getNextIntValue(c, BluetoothGattCharacteristic.FORMAT_UINT8)) + if (operator != Operator.NULL) { + Log.e(tag, "operator must be null, when opcode is Abort Operation") + return errorFreeParsing + } + requestOpcode = Opcode.fromKey(getNextIntValue(c, BluetoothGattCharacteristic.FORMAT_UINT8)) + responseCode = ResponseCode.fromKey(getNextIntValue(c, BluetoothGattCharacteristic.FORMAT_UINT8)) + } + else -> { + Log.e(tag, "unknown opcode $opcode , exiting parsing") + return errorFreeParsing + } + } + errorFreeParsing = true + return errorFreeParsing + + } + + /** + * Helper function to deserialize the generic operand portion of the packet + */ + private fun parseGenericOperand(c: BluetoothGattCharacteristic) { + when (operator) { + null -> throw NullPointerException("operator is null") + Operator.ALL_RECORDS, + Operator.FIRST_RECORD, + Operator.LAST_RECORD -> { + Log.i(tag, "${operator.toString()} does not need operand") + return + } + } + + filterType = Filter.fromKey(getNextIntValue(c, BluetoothGattCharacteristic.FORMAT_UINT8)) + + if (filterType == Filter.SEQUENCE_NUMBER) { + when (operator) { + Operator.LESS_THAN_OR_EQUAL_TO -> { + maximumFilterValueSequenceNumber = getNextIntValue(c, BluetoothGattCharacteristic.FORMAT_UINT16) + } + Operator.GREATER_THAN_OR_EQUAL_TO -> { + minimumFilterValueSequenceNumber = getNextIntValue(c, BluetoothGattCharacteristic.FORMAT_UINT16) + } + Operator.WITHIN_RANGE_OF_INCLUSIVE -> { + minimumFilterValueSequenceNumber = getNextIntValue(c, BluetoothGattCharacteristic.FORMAT_UINT16) + maximumFilterValueSequenceNumber = getNextIntValue(c, BluetoothGattCharacteristic.FORMAT_UINT16) + } + else -> { + Log.e(tag, "unknown operator $operator") + } + } + } else if (filterType == Filter.USER_FACING_TIME) { + when (operator) { + Operator.LESS_THAN_OR_EQUAL_TO -> { + maximumFilterValueUserFacingTime = parseBluetoothDateTime(c) + } + Operator.GREATER_THAN_OR_EQUAL_TO -> { + minimumFilterValueUserFacingTime = parseBluetoothDateTime(c) + } + Operator.WITHIN_RANGE_OF_INCLUSIVE -> { + minimumFilterValueUserFacingTime = parseBluetoothDateTime(c) + maximumFilterValueUserFacingTime = parseBluetoothDateTime(c) + + } + else -> { + Log.e(tag, "unknown operator $operator") + } + } + } else { + throw IllegalStateException("unable to handle filter type $filterType") + } + } + + + /** + * A helper function for deserializing [BluetoothDateTime] + */ + private fun parseBluetoothDateTime(c: BluetoothGattCharacteristic): BluetoothDateTime { + return BluetoothDateTime( + _year = getNextIntValue(c, getNextIntValue(c, BluetoothGattCharacteristic.FORMAT_UINT16)), + _month = getNextIntValue(c, getNextIntValue(c, BluetoothGattCharacteristic.FORMAT_UINT8)), + _day = getNextIntValue(c, getNextIntValue(c, BluetoothGattCharacteristic.FORMAT_UINT8)), + _hour = getNextIntValue(c, getNextIntValue(c, BluetoothGattCharacteristic.FORMAT_UINT8)), + _min = getNextIntValue(c, getNextIntValue(c, BluetoothGattCharacteristic.FORMAT_UINT8)), + _sec = getNextIntValue(c, getNextIntValue(c, BluetoothGattCharacteristic.FORMAT_UINT8)) + ) + } + + /** + * Serialize the current [RACP] into a [ByteArray], it can be loaded up onto a communication to + * write to remote device + * @param hasCrc if the characteristics need CRC + * @return the [ByteArray] of the resulting composition + */ + override fun composeCharacteristic(hasCrc: Boolean): ByteArray { + opcode?.run { + when (this) { + Opcode.REPORT_STORED_RECORDS, + Opcode.REPORT_NUMBER_OF_STORED_RECORDS, + Opcode.DELETE_STORED_RECORDS -> { + putIntValue(this.key, BluetoothGattCharacteristic.FORMAT_UINT8) + composeQueryParameters() + return rawData + } + Opcode.RESPONSE_CODE -> { + putIntValue(this.key, BluetoothGattCharacteristic.FORMAT_UINT8) + putIntValue(Operator.NULL.key, BluetoothGattCharacteristic.FORMAT_UINT8) + requestOpcode?.run { putIntValue(this.key, BluetoothGattCharacteristic.FORMAT_UINT8) } + ?: throw NullPointerException(" request opcode is null") + responseCode?.run { putIntValue(this.key, BluetoothGattCharacteristic.FORMAT_UINT8) } + ?: throw NullPointerException(" reponse code is null") + } + Opcode.ABORT_OPERATION -> { + putIntValue(this.key, BluetoothGattCharacteristic.FORMAT_UINT8) + putIntValue(Operator.NULL.key, BluetoothGattCharacteristic.FORMAT_UINT8) + } + Opcode.NUMBER_OF_STORED_RECORDS_RESPONSE -> { + putIntValue(this.key, BluetoothGattCharacteristic.FORMAT_UINT8) + putIntValue(Operator.NULL.key, BluetoothGattCharacteristic.FORMAT_UINT8) + numberOfRecordResponse?.run { putIntValue(this, BluetoothGattCharacteristic.FORMAT_UINT16) } + ?: throw NullPointerException(" nnumber of records is null") + } + else -> { + Log.e(tag, "unknown opcode") + } + } + if(hasCrc){ + attachCrc() + } + } + return rawData + } + + /** + * A helper function to compose the parameter portion of the packet + */ + private fun composeQueryParameters() { + operator?.run { + when (this) { + Operator.LAST_RECORD, + Operator.FIRST_RECORD, + Operator.ALL_RECORDS -> { + putIntValue(this.key, BluetoothGattCharacteristic.FORMAT_UINT8) + } + Operator.GREATER_THAN_OR_EQUAL_TO -> { + putIntValue(this.key, BluetoothGattCharacteristic.FORMAT_UINT8) + filterType?.run { + putIntValue(this.key, BluetoothGattCharacteristic.FORMAT_UINT8) + when (this) { + Filter.SEQUENCE_NUMBER -> { + minimumFilterValueSequenceNumber?.run { + putIntValue(this, BluetoothGattCharacteristic.FORMAT_UINT16) + } ?: throw NullPointerException("The minimum value is null") + } + Filter.USER_FACING_TIME -> { + minimumFilterValueUserFacingTime?.run { + putBluetoothDateTime(this) + } ?: throw NullPointerException("The minimum value is null") + } + else -> { + throw IllegalArgumentException("Unsupported filter type") + } + } + } + } + Operator.LESS_THAN_OR_EQUAL_TO -> { + putIntValue(this.key, BluetoothGattCharacteristic.FORMAT_UINT8) + filterType?.run { + putIntValue(this.key, BluetoothGattCharacteristic.FORMAT_UINT8) + when (this) { + Filter.SEQUENCE_NUMBER -> { + maximumFilterValueSequenceNumber?.run { + putIntValue(this, BluetoothGattCharacteristic.FORMAT_UINT16) + } ?: throw NullPointerException("The maximum value is null") + } + Filter.USER_FACING_TIME -> { + maximumFilterValueUserFacingTime?.run { + putBluetoothDateTime(this) + } ?: throw NullPointerException("The maximum value is null") + } + else -> { + throw IllegalArgumentException("Unsupported filter type") + } + } + } + } + Operator.WITHIN_RANGE_OF_INCLUSIVE -> { + putIntValue(this.key, BluetoothGattCharacteristic.FORMAT_UINT8) + filterType?.run { + putIntValue(this.key, BluetoothGattCharacteristic.FORMAT_UINT8) + when (this) { + Filter.SEQUENCE_NUMBER -> { + minimumFilterValueSequenceNumber?.run { + putIntValue(this, BluetoothGattCharacteristic.FORMAT_UINT16) + } ?: throw NullPointerException("The minimum value is null") + maximumFilterValueSequenceNumber?.run { + putIntValue(this, BluetoothGattCharacteristic.FORMAT_UINT16) + } ?: throw NullPointerException("The maximum value is null") + } + Filter.USER_FACING_TIME -> { + minimumFilterValueUserFacingTime?.run { + putBluetoothDateTime(this) + } ?: throw NullPointerException("The minimum value is null") + maximumFilterValueUserFacingTime?.run { + putBluetoothDateTime(this) + } ?: throw NullPointerException("The maximum value is null") + } + else -> { + throw IllegalArgumentException("Unsupported filter type") + } + } + } + } + else -> { + throw IllegalArgumentException("Unknown operator") + } + + } + + } + + + } + + /** + * A helper function to serialize [BluetoothDateTime] + */ + private fun putBluetoothDateTime(input: BluetoothDateTime) { + putIntValue(input._year, BluetoothGattCharacteristic.FORMAT_UINT16) + putIntValue(input._month, BluetoothGattCharacteristic.FORMAT_UINT8) + putIntValue(input._day, BluetoothGattCharacteristic.FORMAT_UINT8) + putIntValue(input._hour, BluetoothGattCharacteristic.FORMAT_UINT8) + putIntValue(input._min, BluetoothGattCharacteristic.FORMAT_UINT8) + putIntValue(input._sec, BluetoothGattCharacteristic.FORMAT_UINT8) + } + +} \ No newline at end of file diff --git a/jdrfandroidbleparser/src/main/java/org/ehealthinnovation/jdrfandroidbleparser/bgm/characteristic/compoundcharacteristic/SystemIdCharacteristic.kt b/jdrfandroidbleparser/src/main/java/org/ehealthinnovation/jdrfandroidbleparser/bgm/characteristic/compoundcharacteristic/SystemIdCharacteristic.kt index aa39c43..7cad83c 100644 --- a/jdrfandroidbleparser/src/main/java/org/ehealthinnovation/jdrfandroidbleparser/bgm/characteristic/compoundcharacteristic/SystemIdCharacteristic.kt +++ b/jdrfandroidbleparser/src/main/java/org/ehealthinnovation/jdrfandroidbleparser/bgm/characteristic/compoundcharacteristic/SystemIdCharacteristic.kt @@ -1,9 +1,8 @@ package org.ehealthinnovation.jdrfandroidbleparser.bgm.characteristic.compoundcharacteristic import android.bluetooth.BluetoothGattCharacteristic -import org.ehealthinnovation.jdrfandroidbleparser.bgm.characteristic.common.BaseCharacteristic +import org.ehealthinnovation.jdrfandroidbleparser.common.BaseCharacteristic import org.ehealthinnovation.jdrfandroidbleparser.encodedvalue.GattCharacteristic -import kotlin.jvm.java /** * The SYSTEM ID characteristic consists of a structure with two fields. The first field are the diff --git a/jdrfandroidbleparser/src/main/java/org/ehealthinnovation/jdrfandroidbleparser/bgm/characteristic/stringcharacteristics/StringCharacteristicDeclarations.kt b/jdrfandroidbleparser/src/main/java/org/ehealthinnovation/jdrfandroidbleparser/bgm/characteristic/stringcharacteristics/StringCharacteristicDeclarations.kt index 8cbaee0..b9f55c8 100644 --- a/jdrfandroidbleparser/src/main/java/org/ehealthinnovation/jdrfandroidbleparser/bgm/characteristic/stringcharacteristics/StringCharacteristicDeclarations.kt +++ b/jdrfandroidbleparser/src/main/java/org/ehealthinnovation/jdrfandroidbleparser/bgm/characteristic/stringcharacteristics/StringCharacteristicDeclarations.kt @@ -1,7 +1,7 @@ package org.ehealthinnovation.jdrfandroidbleparser.bgm.characteristic.stringcharacteristics import android.bluetooth.BluetoothGattCharacteristic -import org.ehealthinnovation.jdrfandroidbleparser.bgm.characteristic.common.StringCharacteristic +import org.ehealthinnovation.jdrfandroidbleparser.common.StringCharacteristic import org.ehealthinnovation.jdrfandroidbleparser.encodedvalue.GattCharacteristic.* import kotlin.jvm.java diff --git a/jdrfandroidbleparser/src/main/java/org/ehealthinnovation/jdrfandroidbleparser/bgm/characteristic/common/BaseCharacteristic.kt b/jdrfandroidbleparser/src/main/java/org/ehealthinnovation/jdrfandroidbleparser/common/BaseCharacteristic.kt similarity index 61% rename from jdrfandroidbleparser/src/main/java/org/ehealthinnovation/jdrfandroidbleparser/bgm/characteristic/common/BaseCharacteristic.kt rename to jdrfandroidbleparser/src/main/java/org/ehealthinnovation/jdrfandroidbleparser/common/BaseCharacteristic.kt index 93400f9..3958399 100644 --- a/jdrfandroidbleparser/src/main/java/org/ehealthinnovation/jdrfandroidbleparser/bgm/characteristic/common/BaseCharacteristic.kt +++ b/jdrfandroidbleparser/src/main/java/org/ehealthinnovation/jdrfandroidbleparser/common/BaseCharacteristic.kt @@ -1,8 +1,10 @@ -package org.ehealthinnovation.jdrfandroidbleparser.bgm.characteristic.common +package org.ehealthinnovation.jdrfandroidbleparser.common import android.bluetooth.BluetoothGattCharacteristic import android.util.Log import org.ehealthinnovation.jdrfandroidbleparser.encodedvalue.FormatType +import org.ehealthinnovation.jdrfandroidbleparser.utility.CrcHelper +import java.util.* import kotlin.jvm.Throws import kotlin.jvm.java @@ -17,12 +19,26 @@ abstract class BaseCharacteristic(val uuid: Int) { * If the characteristic is null, which is possible, we just return false so the requesting * process can handle the failed parse. This is done in the super class, so we don't have to * deal with it in every other sub class characteristic. + * + * The default value for CRC check is false. There are some characteristics that requires a + * first pass of parsing to know if CRC is present, and a second pass to parse the content with + * the [hasCrc] flag set correctly. + * + * */ - constructor(characteristic: BluetoothGattCharacteristic?, uuid: Int): this(uuid) { - this.characteristic = characteristic - characteristic?.let { - rawData = it.value ?: ByteArray(0) - this.successfulParsing = tryParse(it) + constructor(characteristic: BluetoothGattCharacteristic?, uuid: Int, hasCrc: Boolean = false, isComposing: Boolean = false): this(uuid) { + if(!isComposing) { + this.characteristic = characteristic + characteristic?.let { + rawData = it.value ?: ByteArray(0) + this.successfulParsing = tryParse(it, hasCrc) + } + }else{ + characteristic?.let { + this.composingcharacteristic = it } + if(characteristic == null){ + this.composingcharacteristic = BluetoothGattCharacteristic(UUID.randomUUID(), BluetoothGattCharacteristic.PROPERTY_WRITE, BluetoothGattCharacteristic.PERMISSION_WRITE) + } } } @@ -32,16 +48,27 @@ abstract class BaseCharacteristic(val uuid: Int) { var characteristic: BluetoothGattCharacteristic? = null var successfulParsing: Boolean = false var offset = 0 + /**A dummy characteristic for composing a new characteristic*/ + private var composingcharacteristic: BluetoothGattCharacteristic? = null /** * Swallowing the exception is one of two viable options, some users might want any error to * bubble up all the way immediately. * * https://github.com/markiantorno/JDRFAndroidBLEParser/issues/1 + * @param c The [BluetoothGattCharacteristic] to parse + * @param checkCrc if CRC check is needed. Default if false + * @return true if parsing is successful + * */ - fun tryParse(c: BluetoothGattCharacteristic): Boolean { + fun tryParse(c: BluetoothGattCharacteristic, checkCrc : Boolean = false): Boolean { var errorFreeParse = false try { + if(checkCrc){ + if(!testCrc(rawData)){ + throw Exception("CRC16 check failed"); + } + } errorFreeParse = parse(c) } catch (e: NullPointerException) { Log.e(tag, nullValueException) @@ -51,6 +78,7 @@ abstract class BaseCharacteristic(val uuid: Int) { return errorFreeParse } + /** * Each characteristic has it's own set of values which could be of differing types, so we leave * implementation of the parsing to the individual characteristic implementation. @@ -62,6 +90,34 @@ abstract class BaseCharacteristic(val uuid: Int) { */ protected abstract fun parse(c: BluetoothGattCharacteristic): Boolean + /** + * This function tests if the CRC attached at the packet is correct. This function + * assumes the CRC is attached at the end of the packet. If it is not the case, subclass + * needs to implements this method. + * @param data The raw data of the message byte sequence + * @return true if the CRC is passed. + */ + protected fun testCrc(data : ByteArray) : Boolean { + return CrcHelper.testCcittCrc16(data, data.size) + } + + /** + * This function append the CRC to the end of the packet. + * @param data The raw data of the message byte sequence + * @return the resulting [ByteArray] with the CRC attached. + */ + protected fun attachCrc(data: ByteArray? = null) : ByteArray { + data?.let { + return CrcHelper.attachCcittCrc16ToPacket(data, data.size) + } + val cRc = CrcHelper.calculateCcittCrc16(rawData, rawData.size).toInt() + if(putIntValue(cRc, BluetoothGattCharacteristic.FORMAT_UINT16)) { + return rawData + }else{ + throw IllegalStateException("Unable to append CRC") + } + } + /** * Returns the stored [String] value of this characteristic. * @@ -130,6 +186,31 @@ abstract class BaseCharacteristic(val uuid: Int) { } ?: throw NullPointerException("Format \"$formatType\" at offset \"$offset\" does " + "not relate to valid Float value...") + @Throws(NullPointerException::class) + + /** + * Put the input [value] of Bluetooth type [formatType] into a the internal [composingcharacteristic] + * object. The [offset] property will be incremented by the size of [formatType] + * + * @param value The integer value to be put into the serial buffer + * @param formatType Int indicating the format type of the input. An example is + * [BluetoothGattCharacteristic.FORMAT_UINT8] + * @return true if operation is successful; false otherwise + * @throws NullPointerException If the internal serialization buffer [composingcharacteristic] + * is null, this happens when [BaseCharacteristic] + */ + protected fun putIntValue(value :Int, formatType: Int) : Boolean{ + composingcharacteristic?.run { + val results = this.setValue(value, formatType, offset) + if(results) { + offset = getNextOffset(formatType, offset) + rawData = this.value ?: ByteArray(0) + } + return results + } ?: throw NullPointerException("composingcharacteristic is null, make sure the " + + "characteristic is initiated with the composing constructor") + } + /** * Increments the current read index by the appropriate amount for the format type passed in. * @@ -145,4 +226,6 @@ abstract class BaseCharacteristic(val uuid: Int) { return newIndex } + + } diff --git a/jdrfandroidbleparser/src/main/java/org/ehealthinnovation/jdrfandroidbleparser/bgm/characteristic/common/Composable.kt b/jdrfandroidbleparser/src/main/java/org/ehealthinnovation/jdrfandroidbleparser/common/Composable.kt similarity index 67% rename from jdrfandroidbleparser/src/main/java/org/ehealthinnovation/jdrfandroidbleparser/bgm/characteristic/common/Composable.kt rename to jdrfandroidbleparser/src/main/java/org/ehealthinnovation/jdrfandroidbleparser/common/Composable.kt index a65a2f4..bd21030 100644 --- a/jdrfandroidbleparser/src/main/java/org/ehealthinnovation/jdrfandroidbleparser/bgm/characteristic/common/Composable.kt +++ b/jdrfandroidbleparser/src/main/java/org/ehealthinnovation/jdrfandroidbleparser/common/Composable.kt @@ -1,7 +1,4 @@ -package org.ehealthinnovation.jdrfandroidbleparser.bgm.characteristic.common - -import android.bluetooth.BluetoothGatt -import android.bluetooth.BluetoothGattCharacteristic +package org.ehealthinnovation.jdrfandroidbleparser.common /** * Many base Bluetooth Characteristics not only have the requirement that they must decode a given @@ -9,7 +6,6 @@ import android.bluetooth.BluetoothGattCharacteristic * as well. These characteristics, must implement the [Composable] interface. * [BaseCharacteristic] classes can just extend this class and not worry about implementation. * - * Created by miantorno on 2018-06-19. */ interface Composable { @@ -17,6 +13,6 @@ interface Composable { * Compose a given characteristic into the resulting [ByteArray]. */ @Throws(IllegalStateException::class) - fun composeCharacteristic(): ByteArray + fun composeCharacteristic(hasCrc : Boolean): ByteArray -} \ No newline at end of file +} diff --git a/jdrfandroidbleparser/src/main/java/org/ehealthinnovation/jdrfandroidbleparser/bgm/characteristic/common/StringCharacteristic.kt b/jdrfandroidbleparser/src/main/java/org/ehealthinnovation/jdrfandroidbleparser/common/StringCharacteristic.kt similarity index 90% rename from jdrfandroidbleparser/src/main/java/org/ehealthinnovation/jdrfandroidbleparser/bgm/characteristic/common/StringCharacteristic.kt rename to jdrfandroidbleparser/src/main/java/org/ehealthinnovation/jdrfandroidbleparser/common/StringCharacteristic.kt index cfb8c40..6ead04e 100644 --- a/jdrfandroidbleparser/src/main/java/org/ehealthinnovation/jdrfandroidbleparser/bgm/characteristic/common/StringCharacteristic.kt +++ b/jdrfandroidbleparser/src/main/java/org/ehealthinnovation/jdrfandroidbleparser/common/StringCharacteristic.kt @@ -1,4 +1,4 @@ -package org.ehealthinnovation.jdrfandroidbleparser.bgm.characteristic.common +package org.ehealthinnovation.jdrfandroidbleparser.common import android.bluetooth.BluetoothGattCharacteristic import kotlin.jvm.java diff --git a/jdrfandroidbleparser/src/main/java/org/ehealthinnovation/jdrfandroidbleparser/encodedvalue/bgmmeasurement/Flags.kt b/jdrfandroidbleparser/src/main/java/org/ehealthinnovation/jdrfandroidbleparser/encodedvalue/bgm/bgmmeasurement/Flags.kt similarity index 100% rename from jdrfandroidbleparser/src/main/java/org/ehealthinnovation/jdrfandroidbleparser/encodedvalue/bgmmeasurement/Flags.kt rename to jdrfandroidbleparser/src/main/java/org/ehealthinnovation/jdrfandroidbleparser/encodedvalue/bgm/bgmmeasurement/Flags.kt diff --git a/jdrfandroidbleparser/src/main/java/org/ehealthinnovation/jdrfandroidbleparser/encodedvalue/bgmmeasurement/SampleLocation.kt b/jdrfandroidbleparser/src/main/java/org/ehealthinnovation/jdrfandroidbleparser/encodedvalue/bgm/bgmmeasurement/SampleLocation.kt similarity index 100% rename from jdrfandroidbleparser/src/main/java/org/ehealthinnovation/jdrfandroidbleparser/encodedvalue/bgmmeasurement/SampleLocation.kt rename to jdrfandroidbleparser/src/main/java/org/ehealthinnovation/jdrfandroidbleparser/encodedvalue/bgm/bgmmeasurement/SampleLocation.kt diff --git a/jdrfandroidbleparser/src/main/java/org/ehealthinnovation/jdrfandroidbleparser/encodedvalue/bgmmeasurement/SensorStatusAnnunciation.kt b/jdrfandroidbleparser/src/main/java/org/ehealthinnovation/jdrfandroidbleparser/encodedvalue/bgm/bgmmeasurement/SensorStatusAnnunciation.kt similarity index 100% rename from jdrfandroidbleparser/src/main/java/org/ehealthinnovation/jdrfandroidbleparser/encodedvalue/bgmmeasurement/SensorStatusAnnunciation.kt rename to jdrfandroidbleparser/src/main/java/org/ehealthinnovation/jdrfandroidbleparser/encodedvalue/bgm/bgmmeasurement/SensorStatusAnnunciation.kt diff --git a/jdrfandroidbleparser/src/main/java/org/ehealthinnovation/jdrfandroidbleparser/encodedvalue/bgmmeasurement/Type.kt b/jdrfandroidbleparser/src/main/java/org/ehealthinnovation/jdrfandroidbleparser/encodedvalue/bgm/bgmmeasurement/Type.kt similarity index 100% rename from jdrfandroidbleparser/src/main/java/org/ehealthinnovation/jdrfandroidbleparser/encodedvalue/bgmmeasurement/Type.kt rename to jdrfandroidbleparser/src/main/java/org/ehealthinnovation/jdrfandroidbleparser/encodedvalue/bgm/bgmmeasurement/Type.kt diff --git a/jdrfandroidbleparser/src/main/java/org/ehealthinnovation/jdrfandroidbleparser/bgm/utility/BluetoothDateTime.kt b/jdrfandroidbleparser/src/main/java/org/ehealthinnovation/jdrfandroidbleparser/utility/BluetoothDateTime.kt similarity index 82% rename from jdrfandroidbleparser/src/main/java/org/ehealthinnovation/jdrfandroidbleparser/bgm/utility/BluetoothDateTime.kt rename to jdrfandroidbleparser/src/main/java/org/ehealthinnovation/jdrfandroidbleparser/utility/BluetoothDateTime.kt index ad44b2d..160c16c 100644 --- a/jdrfandroidbleparser/src/main/java/org/ehealthinnovation/jdrfandroidbleparser/bgm/utility/BluetoothDateTime.kt +++ b/jdrfandroidbleparser/src/main/java/org/ehealthinnovation/jdrfandroidbleparser/utility/BluetoothDateTime.kt @@ -1,7 +1,4 @@ -package org.ehealthinnovation.jdrfandroidbleparser.bgm.utility - -import java.time.Year -import java.util.* +package org.ehealthinnovation.jdrfandroidbleparser.utility data class BluetoothDateTime(val _year: Int = 0, val _month: Int = 0, diff --git a/jdrfandroidbleparser/src/main/java/org/ehealthinnovation/jdrfandroidbleparser/utility/CrcHelper.kt b/jdrfandroidbleparser/src/main/java/org/ehealthinnovation/jdrfandroidbleparser/utility/CrcHelper.kt new file mode 100644 index 0000000..bca8e8a --- /dev/null +++ b/jdrfandroidbleparser/src/main/java/org/ehealthinnovation/jdrfandroidbleparser/utility/CrcHelper.kt @@ -0,0 +1,76 @@ +package org.ehealthinnovation.jdrfandroidbleparser.utility + +import unsigned.toUShort +import unsigned.toUshort +import kotlin.experimental.and + +object CrcHelper { + val tag = CrcHelper::class.java.canonicalName as String + private val lookUpTable = intArrayOf( + 0x0000, 0x1189, 0x2312, 0x329b, 0x4624, 0x57ad, 0x6536, 0x74bf, 0x8c48, 0x9dc1, 0xaf5a, + 0xbed3, 0xca6c, 0xdbe5, 0xe97e, 0xf8f7, 0x1081, 0x0108, 0x3393, 0x221a, 0x56a5, 0x472c, + 0x75b7, 0x643e, 0x9cc9, 0x8d40, 0xbfdb, 0xae52, 0xdaed, 0xcb64, 0xf9ff, 0xe876, 0x2102, + 0x308b, 0x0210, 0x1399, 0x6726, 0x76af, 0x4434, 0x55bd, 0xad4a, 0xbcc3, 0x8e58, 0x9fd1, + 0xeb6e, 0xfae7, 0xc87c, 0xd9f5, 0x3183, 0x200a, 0x1291, 0x0318, 0x77a7, 0x662e, 0x54b5, + 0x453c, 0xbdcb, 0xac42, 0x9ed9, 0x8f50, 0xfbef, 0xea66, 0xd8fd, 0xc974, 0x4204, 0x538d, + 0x6116, 0x709f, 0x0420, 0x15a9, 0x2732, 0x36bb, 0xce4c, 0xdfc5, 0xed5e, 0xfcd7, 0x8868, + 0x99e1, 0xab7a, 0xbaf3, 0x5285, 0x430c, 0x7197, 0x601e, 0x14a1, 0x0528, 0x37b3, 0x263a, + 0xdecd, 0xcf44, 0xfddf, 0xec56, 0x98e9, 0x8960, 0xbbfb, 0xaa72, 0x6306, 0x728f, 0x4014, + 0x519d, 0x2522, 0x34ab, 0x0630, 0x17b9, 0xef4e, 0xfec7, 0xcc5c, 0xddd5, 0xa96a, 0xb8e3, + 0x8a78, 0x9bf1, 0x7387, 0x620e, 0x5095, 0x411c, 0x35a3, 0x242a, 0x16b1, 0x0738, 0xffcf, + 0xee46, 0xdcdd, 0xcd54, 0xb9eb, 0xa862, 0x9af9, 0x8b70, 0x8408, 0x9581, 0xa71a, 0xb693, + 0xc22c, 0xd3a5, 0xe13e, 0xf0b7, 0x0840, 0x19c9, 0x2b52, 0x3adb, 0x4e64, 0x5fed, 0x6d76, + 0x7cff, 0x9489, 0x8500, 0xb79b, 0xa612, 0xd2ad, 0xc324, 0xf1bf, 0xe036, 0x18c1, 0x0948, + 0x3bd3, 0x2a5a, 0x5ee5, 0x4f6c, 0x7df7, 0x6c7e, 0xa50a, 0xb483, 0x8618, 0x9791, 0xe32e, + 0xf2a7, 0xc03c, 0xd1b5, 0x2942, 0x38cb, 0x0a50, 0x1bd9, 0x6f66, 0x7eef, 0x4c74, 0x5dfd, + 0xb58b, 0xa402, 0x9699, 0x8710, 0xf3af, 0xe226, 0xd0bd, 0xc134, 0x39c3, 0x284a, 0x1ad1, + 0x0b58, 0x7fe7, 0x6e6e, 0x5cf5, 0x4d7c, 0xc60c, 0xd785, 0xe51e, 0xf497, 0x8028, 0x91a1, + 0xa33a, 0xb2b3, 0x4a44, 0x5bcd, 0x6956, 0x78df, 0x0c60, 0x1de9, 0x2f72, 0x3efb, 0xd68d, + 0xc704, 0xf59f, 0xe416, 0x90a9, 0x8120, 0xb3bb, 0xa232, 0x5ac5, 0x4b4c, 0x79d7, 0x685e, + 0x1ce1, 0x0d68, 0x3ff3, 0x2e7a, 0xe70e, 0xf687, 0xc41c, 0xd595, 0xa12a, 0xb0a3, 0x8238, + 0x93b1, 0x6b46, 0x7acf, 0x4854, 0x59dd, 0x2d62, 0x3ceb, 0x0e70, 0x1ff9, 0xf78f, 0xe606, + 0xd49d, 0xc514, 0xb1ab, 0xa022, 0x92b9, 0x8330, 0x7bc7, 0x6a4e, 0x58d5, 0x495c, 0x3de3, + 0x2c6a, 0x1ef1, 0x0f78) + + /** + * Get the CRC check sum from a message + * @param message the byte message array + * @param length the length of the message array + * @return the CRC check sum from the message. + */ + fun calculateCcittCrc16(message: ByteArray, length: Int): Short { + var crc16 = 0xFFFF + var i: Int + i = 0 + while (i < length) { + crc16 = (crc16 shr 8 and 0xFF) xor lookUpTable[crc16 xor message[i].toInt() and 0xFF] + i++ + } + crc16 = crc16 and 0xffff + return crc16.toShort() + } + + /** + * Test a message if it passes the CRC check. + * @param message the byte message array + * @param length the length of the message array + * @return true is the message passes the test, false otherwise. + */ + fun testCcittCrc16(message: ByteArray, length: Int): Boolean { + return calculateCcittCrc16(message, length).toInt() == 0 + } + + /** + * Attach a 16 bit check sum to an input byte array + * @param message the byte message array + * @param length the length of the message array + * @return the resulting byte array with CRC code attached to the end in little endian format + */ + fun attachCcittCrc16ToPacket(message: ByteArray, length: Int): ByteArray { + + val crc = calculateCcittCrc16(message, length) + val output = message + crc.toByte() + (crc.toInt() and 0xFF00 shr 8).toByte() + + return output + } +} diff --git a/jdrfandroidbleparser/src/test/java/org/ehealthinnovation/jdrfandroidbleparser/BaseTest.kt b/jdrfandroidbleparser/src/test/java/org/ehealthinnovation/jdrfandroidbleparser/BaseTest.kt index 065830c..0e244ec 100644 --- a/jdrfandroidbleparser/src/test/java/org/ehealthinnovation/jdrfandroidbleparser/BaseTest.kt +++ b/jdrfandroidbleparser/src/test/java/org/ehealthinnovation/jdrfandroidbleparser/BaseTest.kt @@ -19,14 +19,22 @@ import kotlin.jvm.java open class BaseTest { - private lateinit var payload: ByteArray + private lateinit var testPayload: ByteArray + private var mockPayload: ByteArray = ByteArray(0) private lateinit var mockBTCharacteristic: BluetoothGattCharacteristic private lateinit var stringByteArray: ByteArray private lateinit var testString: String private fun any(type: Class): T = Mockito.any(type) - protected fun mockBTCharacteristic(payload: ByteArray): BluetoothGattCharacteristic { + + protected fun mockBTCharacteristic(payload2: ByteArray): BluetoothGattCharacteristic { + if(payload2.isNotEmpty()){ + mockPayload = payload2 + }else{ + mockPayload = ByteArray(0) + } + return mock { on { getIntValue(any(Int::class.java), any(Int::class.java)) } doAnswer { val formatType: Int = it.getArgument(0) @@ -37,10 +45,10 @@ open class BaseTest { FORMAT_SINT8, FORMAT_UINT8, FORMAT_SINT16, FORMAT_UINT16 -> { val offset: Int = it.getArgument(1) FormatType.fromType(formatType)?.let { - if( payload.size <= offset+it.length()-1){ + if( mockPayload.size <= offset+it.length()-1){ throw IllegalArgumentException("Trying to access non-existing byte") } - val reversedArray = payload.sliceArray(offset..(offset + (it.length() - 1))) + val reversedArray = mockPayload.sliceArray(offset..(offset + (it.length() - 1))) if (it.signed) { unsignedToSigned(unsignedBytesToInt(reversedArray), reversedArray.size * 8) } else { @@ -59,7 +67,7 @@ open class BaseTest { FORMAT_FLOAT, FORMAT_SFLOAT -> { val offset: Int = it.getArgument(1) FormatType.fromType(formatType)?.let { - val reversedArray = payload.sliceArray(offset..(offset + (it.length() - 1))) + val reversedArray = mockPayload.sliceArray(offset..(offset + (it.length() - 1))) when (it) { FORMAT_FLOAT -> bytesToFloat(reversedArray[0], reversedArray[1], reversedArray[2], reversedArray[3]) FORMAT_SFLOAT -> bytesToFloat(reversedArray[0], reversedArray[1]) @@ -74,34 +82,58 @@ open class BaseTest { } on { getStringValue(any(Int::class.java)) } doAnswer { val offset: Int = it.getArgument(0) - payload.sliceArray(offset..(offset + (payload.size - 1))).reversedArray().toString(Charset.defaultCharset()) + mockPayload.sliceArray(offset..(offset + (mockPayload.size - 1))).reversedArray().toString(Charset.defaultCharset()) + } + on { value } doAnswer {mockPayload} + on {setValue(any(Int::class.java), any(Int::class.java), any(Int::class.java))} doAnswer { + val value: Int = it.getArgument(0) + val formatType : Int = it.getArgument(1) + val offset: Int = it.getArgument(2) + + when (FormatType.fromType(formatType)) { + FORMAT_UINT8, FORMAT_UINT16-> { + setPayloadValueInt(value, formatType, offset) + } + else->{ + throw IllegalArgumentException("Unknown type") + } + } } } } @Before fun setUp() { + } + + @Test + fun testMockUINT8() { /* * Data is little endian formatted. So the LSO (least significant octet is loaded in first, * followed by the second least significant octet, and so on. * * In this case, we want the payload to be 0x0123456789ABCDEF */ - payload = byteArrayOf(0xef.toByte(), 0xcd.toByte(), 0xab.toByte(), + testPayload = byteArrayOf(0xef.toByte(), 0xcd.toByte(), 0xab.toByte(), 0x89.toByte(), 0x67.toByte(), 0x45.toByte(), 0x23.toByte(), 0x01.toByte()) - mockBTCharacteristic = mockBTCharacteristic(payload) - } - - @Test - fun testMockUINT8() { - for ((index, value) in payload.withIndex()) { + mockBTCharacteristic = mockBTCharacteristic(testPayload) + for ((index, value) in testPayload.withIndex()) { Assert.assertEquals(String.format("%02X", value).toInt(radix = 16), mockBTCharacteristic.getIntValue(BluetoothGattCharacteristic.FORMAT_UINT8, index)) } } @Test fun testMockSINT8() { - for ((index, value) in payload.withIndex()) { + /* + * Data is little endian formatted. So the LSO (least significant octet is loaded in first, + * followed by the second least significant octet, and so on. + * + * In this case, we want the payload to be 0x0123456789ABCDEF + */ + testPayload = byteArrayOf(0xef.toByte(), 0xcd.toByte(), 0xab.toByte(), + 0x89.toByte(), 0x67.toByte(), 0x45.toByte(), 0x23.toByte(), 0x01.toByte()) + mockBTCharacteristic = mockBTCharacteristic(testPayload) + for ((index, value) in testPayload.withIndex()) { Assert.assertEquals(unsignedToSigned(String.format("%02X", value).toInt(radix = 16), FORMAT_SINT8.bits()), mockBTCharacteristic.getIntValue(BluetoothGattCharacteristic.FORMAT_SINT8, index)) } @@ -109,9 +141,18 @@ open class BaseTest { @Test fun testMockUINT16() { + /* + * Data is little endian formatted. So the LSO (least significant octet is loaded in first, + * followed by the second least significant octet, and so on. + * + * In this case, we want the payload to be 0x0123456789ABCDEF + */ + testPayload = byteArrayOf(0xef.toByte(), 0xcd.toByte(), 0xab.toByte(), + 0x89.toByte(), 0x67.toByte(), 0x45.toByte(), 0x23.toByte(), 0x01.toByte()) + mockBTCharacteristic = mockBTCharacteristic(testPayload) var index = 0 - while (index < payload.size) { - Assert.assertEquals(payload.sliceArray(index..(index + FORMAT_UINT16.length() - 1)).reversedArray().toHex()!!.toInt(radix = 16), + while (index < testPayload.size) { + Assert.assertEquals(testPayload.sliceArray(index..(index + FORMAT_UINT16.length() - 1)).reversedArray().toHex()!!.toInt(radix = 16), mockBTCharacteristic.getIntValue(BluetoothGattCharacteristic.FORMAT_UINT16, index)) index += FORMAT_UINT16.length() } @@ -119,9 +160,18 @@ open class BaseTest { @Test fun testMockSINT16() { + /* + * Data is little endian formatted. So the LSO (least significant octet is loaded in first, + * followed by the second least significant octet, and so on. + * + * In this case, we want the payload to be 0x0123456789ABCDEF + */ + testPayload = byteArrayOf(0xef.toByte(), 0xcd.toByte(), 0xab.toByte(), + 0x89.toByte(), 0x67.toByte(), 0x45.toByte(), 0x23.toByte(), 0x01.toByte()) + mockBTCharacteristic = mockBTCharacteristic(testPayload) var index = 0 - while (index < payload.size) { - Assert.assertEquals(unsignedToSigned(payload.sliceArray(index..(index + FORMAT_SINT16.length() - 1)).reversedArray().toHex()!!.toInt(radix = 16), FORMAT_SINT16.bits()), + while (index < testPayload.size) { + Assert.assertEquals(unsignedToSigned(testPayload.sliceArray(index..(index + FORMAT_SINT16.length() - 1)).reversedArray().toHex()!!.toInt(radix = 16), FORMAT_SINT16.bits()), mockBTCharacteristic.getIntValue(BluetoothGattCharacteristic.FORMAT_SINT16, index)) index += FORMAT_UINT16.length() } @@ -129,9 +179,18 @@ open class BaseTest { @Test fun testMockFLOAT() { + /* + * Data is little endian formatted. So the LSO (least significant octet is loaded in first, + * followed by the second least significant octet, and so on. + * + * In this case, we want the payload to be 0x0123456789ABCDEF + */ + testPayload = byteArrayOf(0xef.toByte(), 0xcd.toByte(), 0xab.toByte(), + 0x89.toByte(), 0x67.toByte(), 0x45.toByte(), 0x23.toByte(), 0x01.toByte()) + mockBTCharacteristic = mockBTCharacteristic(testPayload) var index = 0 - while (index < payload.size) { - val sliced = payload.sliceArray(index..(index + FORMAT_FLOAT.length() - 1)) + while (index < testPayload.size) { + val sliced = testPayload.sliceArray(index..(index + FORMAT_FLOAT.length() - 1)) Assert.assertEquals(bytesToFloat(sliced[0], sliced[1], sliced[2], sliced[3]), mockBTCharacteristic.getFloatValue(BluetoothGattCharacteristic.FORMAT_FLOAT, index)) index += FORMAT_FLOAT.length() @@ -140,9 +199,18 @@ open class BaseTest { @Test fun testMockSFLOAT() { + /* + * Data is little endian formatted. So the LSO (least significant octet is loaded in first, + * followed by the second least significant octet, and so on. + * + * In this case, we want the payload to be 0x0123456789ABCDEF + */ + testPayload = byteArrayOf(0xef.toByte(), 0xcd.toByte(), 0xab.toByte(), + 0x89.toByte(), 0x67.toByte(), 0x45.toByte(), 0x23.toByte(), 0x01.toByte()) + mockBTCharacteristic = mockBTCharacteristic(testPayload) var index = 0 - while (index < payload.size) { - val sliced = payload.sliceArray(index..(index + FORMAT_SFLOAT.length() - 1)) + while (index < testPayload.size) { + val sliced = testPayload.sliceArray(index..(index + FORMAT_SFLOAT.length() - 1)) Assert.assertEquals(bytesToFloat(sliced[0], sliced[1]), mockBTCharacteristic.getFloatValue(BluetoothGattCharacteristic.FORMAT_SFLOAT, index)) index += FORMAT_SFLOAT.length() @@ -159,6 +227,26 @@ open class BaseTest { Assert.assertEquals(testString, mockBTCharacteristic.getStringValue(0)) } + @Test + fun testSettingUint8(){ + mockPayload = ByteArray(1) + val expectedPayload: ByteArray = byteArrayOf(0x69) + val valueToSet:Byte = 0x69 + mockBTCharacteristic = mockBTCharacteristic(ByteArray(0)) + mockBTCharacteristic.setValue(valueToSet.toInt(), FORMAT_UINT8.formatType, 0) + Assert.assertTrue(mockPayload.contentEquals(expectedPayload)) + } + + @Test + fun testSettingUint16(){ + mockPayload = ByteArray(2) + val expectedPayload: ByteArray = byteArrayOf(0x69,0x38) + val valueToSet:Int = 0x3869 + mockBTCharacteristic = mockBTCharacteristic(ByteArray(0)) + mockBTCharacteristic.setValue(valueToSet, FORMAT_UINT16.formatType, 0) + Assert.assertTrue(mockPayload.contentEquals(expectedPayload)) + } + companion object { /** @@ -238,4 +326,36 @@ open class BaseTest { fun ok(): String { return Base64.getDecoder().decode(OK).toString(Charsets.UTF_8) } + + fun setPayloadValueInt(value:Int, formatType:Int, offset:Int):Boolean{ + + val newArraySize = getFormatTypeSize(formatType)+offset + if(newArraySize>mockPayload.size){ + mockPayload = mockPayload.copyOf(newArraySize) + } + when(FormatType.fromType(formatType)){ + FORMAT_UINT8->{ + mockPayload.set(offset, value.toByte()) + return true + } + FORMAT_UINT16->{ + mockPayload.set(offset, value.toByte()) + mockPayload.set(offset+1, (value shr 8).toByte()) + return true + } + else ->{ + throw IllegalArgumentException("Currently we does not support this integer type") + } + } + return false + } + + /** + * Get the size of format type + */ + private fun getFormatTypeSize(formatType:Int):Int{ + return formatType and 0x0f + } + + } \ No newline at end of file diff --git a/jdrfandroidbleparser/src/test/java/org/ehealthinnovation/jdrfandroidbleparser/bgm/characteristic/StringCharacteristicTest.kt b/jdrfandroidbleparser/src/test/java/org/ehealthinnovation/jdrfandroidbleparser/bgm/characteristic/StringCharacteristicTest.kt index bd11d99..420138f 100644 --- a/jdrfandroidbleparser/src/test/java/org/ehealthinnovation/jdrfandroidbleparser/bgm/characteristic/StringCharacteristicTest.kt +++ b/jdrfandroidbleparser/src/test/java/org/ehealthinnovation/jdrfandroidbleparser/bgm/characteristic/StringCharacteristicTest.kt @@ -4,7 +4,7 @@ import android.bluetooth.BluetoothGattCharacteristic import com.nhaarman.mockito_kotlin.doReturn import com.nhaarman.mockito_kotlin.mock import org.ehealthinnovation.jdrfandroidbleparser.BaseTest -import org.ehealthinnovation.jdrfandroidbleparser.bgm.characteristic.common.StringCharacteristic +import org.ehealthinnovation.jdrfandroidbleparser.common.StringCharacteristic import org.junit.Assert import org.junit.Test import kotlin.jvm.java diff --git a/jdrfandroidbleparser/src/test/java/org/ehealthinnovation/jdrfandroidbleparser/bgm/characteristic/compoundcharacteristic/GlucoseMeasurementCharacteristicTest.kt b/jdrfandroidbleparser/src/test/java/org/ehealthinnovation/jdrfandroidbleparser/bgm/characteristic/compoundcharacteristic/GlucoseMeasurementCharacteristicTest.kt index 526924e..75c816f 100644 --- a/jdrfandroidbleparser/src/test/java/org/ehealthinnovation/jdrfandroidbleparser/bgm/characteristic/compoundcharacteristic/GlucoseMeasurementCharacteristicTest.kt +++ b/jdrfandroidbleparser/src/test/java/org/ehealthinnovation/jdrfandroidbleparser/bgm/characteristic/compoundcharacteristic/GlucoseMeasurementCharacteristicTest.kt @@ -1,12 +1,9 @@ package org.ehealthinnovation.jdrfandroidbleparser.bgm.characteristic.compoundcharacteristic -import android.bluetooth.BluetoothGattCharacteristic -import com.nhaarman.mockito_kotlin.mock import org.ehealthinnovation.jdrfandroidbleparser.BaseTest -import org.ehealthinnovation.jdrfandroidbleparser.bgm.encodedvalue.bgmmeasurement.Flags import org.ehealthinnovation.jdrfandroidbleparser.bgm.encodedvalue.bgmmeasurement.SampleLocation import org.ehealthinnovation.jdrfandroidbleparser.bgm.encodedvalue.bgmmeasurement.Type -import org.ehealthinnovation.jdrfandroidbleparser.bgm.utility.BluetoothDateTime +import org.ehealthinnovation.jdrfandroidbleparser.utility.BluetoothDateTime import org.ehealthinnovation.jdrfandroidbleparser.encodedvalue.GattCharacteristic import org.ehealthinnovation.jdrfandroidbleparser.encodedvalue.Units import org.junit.Assert @@ -15,7 +12,6 @@ import org.junit.Test import org.junit.Assert.* import org.junit.Before import java.util.* -import kotlin.reflect.jvm.internal.impl.types.checker.TypeIntersector class GlucoseMeasurementCharacteristicTest : BaseTest() { diff --git a/jdrfandroidbleparser/src/test/java/org/ehealthinnovation/jdrfandroidbleparser/bgm/characteristic/compoundcharacteristic/RACPTest.kt b/jdrfandroidbleparser/src/test/java/org/ehealthinnovation/jdrfandroidbleparser/bgm/characteristic/compoundcharacteristic/RACPTest.kt new file mode 100644 index 0000000..50b5613 --- /dev/null +++ b/jdrfandroidbleparser/src/test/java/org/ehealthinnovation/jdrfandroidbleparser/bgm/characteristic/compoundcharacteristic/RACPTest.kt @@ -0,0 +1,757 @@ +package org.ehealthinnovation.jdrfandroidbleparser.bgm.characteristic.compoundcharacteristic + +import org.ehealthinnovation.jdrfandroidbleparser.BaseTest +import org.ehealthinnovation.jdrfandroidbleparser.encodedvalue.bgm.racp.Filter +import org.ehealthinnovation.jdrfandroidbleparser.encodedvalue.bgm.racp.Opcode +import org.ehealthinnovation.jdrfandroidbleparser.encodedvalue.bgm.racp.Operator +import org.ehealthinnovation.jdrfandroidbleparser.encodedvalue.bgm.racp.ResponseCode +import org.ehealthinnovation.jdrfandroidbleparser.utility.BluetoothDateTime +import org.junit.Assert +import org.junit.Test + +class RACPTest : BaseTest() { + /** + * Data class for hosting test vector + */ + class RacpTestVector( + val testPacket: ByteArray, + val expectedParsingResult: Boolean, + val expectedOpcode: Opcode, + val expectedOperator: Operator?, + val expectedFilter: Filter? = null, + val expectedMinimumSequenceNumber: Int? = null, + val expectedMaximumSequenceNumber: Int? = null, + val expectedMinimumUserFacingTime: BluetoothDateTime? = null, + val expectedMaximumUserFacingTime: BluetoothDateTime? = null, + val expectedRequestOpcode: Opcode? = null, + val expectedResponseCode: ResponseCode? = null, + val expectedNumberOfRecord: Int? = null, + val hasCrc: Boolean = false + ) + + val racpTestVectors = mutableMapOf() + + init { + /** + * packet1 + * general response successful + */ + racpTestVectors[1] = RacpTestVector( + testPacket = byteArrayOf(0x06, 0x00, 0x01, 0x01), + expectedParsingResult = true, + expectedOpcode = Opcode.RESPONSE_CODE, + expectedOperator = Operator.NULL, + expectedFilter = null, + expectedMinimumSequenceNumber = null, + expectedMaximumSequenceNumber = null, + expectedMinimumUserFacingTime = null, + expectedMaximumUserFacingTime = null, + expectedNumberOfRecord = null, + expectedRequestOpcode = Opcode.REPORT_STORED_RECORDS, + expectedResponseCode = ResponseCode.SUCCESS + ) + /** + * Packet2 + * Opcode not supported + */ + racpTestVectors[2] = RacpTestVector( + testPacket = byteArrayOf(0x06, 0x00, 0x01, 0x02), + expectedParsingResult = true, + expectedOpcode = Opcode.RESPONSE_CODE, + expectedOperator = Operator.NULL, + expectedFilter = null, + expectedMinimumSequenceNumber = null, + expectedMaximumSequenceNumber = null, + expectedMinimumUserFacingTime = null, + expectedMaximumUserFacingTime = null, + expectedNumberOfRecord = null, + expectedRequestOpcode = Opcode.REPORT_STORED_RECORDS, + expectedResponseCode = ResponseCode.OP_CODE_NOT_SUPPORTED) + + /** + * Packet3 + * Invalid Operator + */ + racpTestVectors[3] = RacpTestVector( + testPacket = byteArrayOf(0x06, 0x00, 0x01, 0x03), + expectedParsingResult = true, + expectedOpcode = Opcode.RESPONSE_CODE, + expectedOperator = Operator.NULL, + expectedFilter = null, + expectedMinimumSequenceNumber = null, + expectedMaximumSequenceNumber = null, + expectedMinimumUserFacingTime = null, + expectedMaximumUserFacingTime = null, + expectedNumberOfRecord = null, + expectedRequestOpcode = Opcode.REPORT_STORED_RECORDS, + expectedResponseCode = ResponseCode.INVALID_OPERATOR + ) + /** + + * Packet4 + * Operator not supported + */ + racpTestVectors[4] = RacpTestVector( + + testPacket = byteArrayOf(0x06, 0x00, 0x01, 0x04), + expectedParsingResult = true, + expectedOpcode = Opcode.RESPONSE_CODE, + expectedOperator = Operator.NULL, + expectedFilter = null, + expectedMinimumSequenceNumber = null, + expectedMaximumSequenceNumber = null, + expectedMinimumUserFacingTime = null, + expectedMaximumUserFacingTime = null, + expectedNumberOfRecord = null, + expectedRequestOpcode = Opcode.REPORT_STORED_RECORDS, + expectedResponseCode = ResponseCode.OPERATOR_NOT_SUPPORTED + + ) + /** + + * Packet5 + * General response successful + */ + racpTestVectors[5] = RacpTestVector( + + testPacket = byteArrayOf(0x06, 0x00, 0x01, 0x05), + expectedParsingResult = true, + expectedOpcode = Opcode.RESPONSE_CODE, + expectedOperator = Operator.NULL, + expectedFilter = null, + expectedMinimumSequenceNumber = null, + expectedMaximumSequenceNumber = null, + expectedMinimumUserFacingTime = null, + expectedMaximumUserFacingTime = null, + expectedNumberOfRecord = null, + expectedRequestOpcode = Opcode.REPORT_STORED_RECORDS, + expectedResponseCode = ResponseCode.INVALID_OPERAND + + ) + /** + + * Packet6 + * No record found + */ + racpTestVectors[6] = RacpTestVector( + + testPacket = byteArrayOf(0x06, 0x00, 0x01, 0x06), + expectedParsingResult = true, + expectedOpcode = Opcode.RESPONSE_CODE, + expectedOperator = Operator.NULL, + expectedFilter = null, + expectedMinimumSequenceNumber = null, + expectedMaximumSequenceNumber = null, + expectedMinimumUserFacingTime = null, + expectedMaximumUserFacingTime = null, + expectedNumberOfRecord = null, + expectedRequestOpcode = Opcode.REPORT_STORED_RECORDS, + expectedResponseCode = ResponseCode.NO_RECORDS_FOUND + + ) + /** + + * Packet7 + * Abort unsuccessful + */ + racpTestVectors[7] = RacpTestVector( + + testPacket = byteArrayOf(0x06, 0x00, 0x01, 0x07), + expectedParsingResult = true, + expectedOpcode = Opcode.RESPONSE_CODE, + expectedOperator = Operator.NULL, + expectedFilter = null, + expectedMinimumSequenceNumber = null, + expectedMaximumSequenceNumber = null, + expectedMinimumUserFacingTime = null, + expectedMaximumUserFacingTime = null, + expectedNumberOfRecord = null, + expectedRequestOpcode = Opcode.REPORT_STORED_RECORDS, + expectedResponseCode = ResponseCode.ABORT_UNSUCCESSFUL + + ) + /** + + * Packet8 + * Procedure not completed + */ + racpTestVectors[8] = RacpTestVector( + + testPacket = byteArrayOf(0x06, 0x00, 0x01, 0x08), + expectedParsingResult = true, + expectedOpcode = Opcode.RESPONSE_CODE, + expectedOperator = Operator.NULL, + expectedFilter = null, + expectedMinimumSequenceNumber = null, + expectedMaximumSequenceNumber = null, + expectedMinimumUserFacingTime = null, + expectedMaximumUserFacingTime = null, + expectedNumberOfRecord = null, + expectedRequestOpcode = Opcode.REPORT_STORED_RECORDS, + expectedResponseCode = ResponseCode.PROCEDURE_NOT_COMPLETED + + ) + /** + + * Packet9 + * Operand not supported + */ + racpTestVectors[9] = RacpTestVector( + + testPacket = byteArrayOf(0x06, 0x00, 0x01, 0x09), + expectedParsingResult = true, + expectedOpcode = Opcode.RESPONSE_CODE, + expectedOperator = Operator.NULL, + expectedFilter = null, + expectedMinimumSequenceNumber = null, + expectedMaximumSequenceNumber = null, + expectedMinimumUserFacingTime = null, + expectedMaximumUserFacingTime = null, + expectedNumberOfRecord = null, + expectedRequestOpcode = Opcode.REPORT_STORED_RECORDS, + expectedResponseCode = ResponseCode.OPERAND_NOT_SUPPORTED + + ) + /** + + * Packet10 + * reponse parser, opcode - delete stored record + */ + racpTestVectors[10] = RacpTestVector( + + testPacket = byteArrayOf(0x06, 0x00, 0x02, 0x01), + expectedParsingResult = true, + expectedOpcode = Opcode.RESPONSE_CODE, + expectedOperator = Operator.NULL, + expectedFilter = null, + expectedMinimumSequenceNumber = null, + expectedMaximumSequenceNumber = null, + expectedMinimumUserFacingTime = null, + expectedMaximumUserFacingTime = null, + expectedNumberOfRecord = null, + expectedRequestOpcode = Opcode.DELETE_STORED_RECORDS, + expectedResponseCode = ResponseCode.SUCCESS + + ) + /** + + * Packet11 + * response parser, opcode - abort operation + */ + racpTestVectors[11] = RacpTestVector( + + testPacket = byteArrayOf(0x06, 0x00, 0x03, 0x01), + expectedParsingResult = true, + expectedOpcode = Opcode.RESPONSE_CODE, + expectedOperator = Operator.NULL, + expectedFilter = null, + expectedMinimumSequenceNumber = null, + expectedMaximumSequenceNumber = null, + expectedMinimumUserFacingTime = null, + expectedMaximumUserFacingTime = null, + expectedNumberOfRecord = null, + expectedRequestOpcode = Opcode.ABORT_OPERATION, + expectedResponseCode = ResponseCode.SUCCESS + + ) + /** + + * Packet12 + * report number of records + */ + racpTestVectors[12] = RacpTestVector( + + testPacket = byteArrayOf(0x05, 0x00, 0x03, 0x00), + expectedParsingResult = true, + expectedOpcode = Opcode.NUMBER_OF_STORED_RECORDS_RESPONSE, + expectedOperator = Operator.NULL, + expectedFilter = null, + expectedMinimumSequenceNumber = null, + expectedMaximumSequenceNumber = null, + expectedMinimumUserFacingTime = null, + expectedMaximumUserFacingTime = null, + expectedNumberOfRecord = 3, + expectedRequestOpcode = null, + expectedResponseCode = null + + ) + /** + + * Packet13 + * Composing packet - Report all records + */ + racpTestVectors[13] = RacpTestVector( + + testPacket = byteArrayOf(0x01, 0x01), + expectedParsingResult = true, + expectedOpcode = Opcode.REPORT_STORED_RECORDS, + expectedOperator = Operator.ALL_RECORDS, + expectedFilter = null, + expectedMinimumSequenceNumber = null, + expectedMaximumSequenceNumber = null, + expectedMinimumUserFacingTime = null, + expectedMaximumUserFacingTime = null, + expectedNumberOfRecord = null, + expectedRequestOpcode = null, + expectedResponseCode = null + + ) + /** + + * Packet14 + * Composing packet - report records - larger than or equal to + */ + racpTestVectors[14] = RacpTestVector( + + testPacket = byteArrayOf(0x01, 0x03, 0x01, 0x05, 0x00), + expectedParsingResult = true, + expectedOpcode = Opcode.REPORT_STORED_RECORDS, + expectedOperator = Operator.GREATER_THAN_OR_EQUAL_TO, + expectedFilter = Filter.SEQUENCE_NUMBER, + expectedMinimumSequenceNumber = 5, + expectedMaximumSequenceNumber = null, + expectedMinimumUserFacingTime = null, + expectedMaximumUserFacingTime = null, + expectedNumberOfRecord = null, + expectedRequestOpcode = null, + expectedResponseCode = null + + ) + /** + + * Packet15 + * Composing packet - report number of records - less than or equal to + * + */ + racpTestVectors[15] = RacpTestVector( + + testPacket = byteArrayOf(0x01, 0x02, 0x01, 0xff.toByte(), 0x00), + expectedParsingResult = true, + expectedOpcode = Opcode.REPORT_STORED_RECORDS, + expectedOperator = Operator.LESS_THAN_OR_EQUAL_TO, + expectedFilter = Filter.SEQUENCE_NUMBER, + expectedMinimumSequenceNumber = null, + expectedMaximumSequenceNumber = 255, + expectedMinimumUserFacingTime = null, + expectedMaximumUserFacingTime = null, + expectedNumberOfRecord = null, + expectedRequestOpcode = null, + expectedResponseCode = null + + ) + /** + + * Packet16 + * Composing packet - report records - between A and B + */ + racpTestVectors[16] = RacpTestVector( + + testPacket = byteArrayOf(0x01, 0x04, 0x01, 0x01, 0x00, 0xFE.toByte(), 0x00), + expectedParsingResult = true, + expectedOpcode = Opcode.REPORT_STORED_RECORDS, + expectedOperator = Operator.WITHIN_RANGE_OF_INCLUSIVE, + expectedFilter = Filter.SEQUENCE_NUMBER, + expectedMinimumSequenceNumber = 1, + expectedMaximumSequenceNumber = 254, + expectedMinimumUserFacingTime = null, + expectedMaximumUserFacingTime = null, + expectedNumberOfRecord = null, + expectedRequestOpcode = null, + expectedResponseCode = null + + ) + /** + + * Packet17 + * Composing packet - report records - first record + */ + racpTestVectors[17] = RacpTestVector( + + testPacket = byteArrayOf(0x01, 0x05), + expectedParsingResult = true, + expectedOpcode = Opcode.REPORT_STORED_RECORDS, + expectedOperator = Operator.FIRST_RECORD, + expectedFilter = null, + expectedMinimumSequenceNumber = null, + expectedMaximumSequenceNumber = null, + expectedMinimumUserFacingTime = null, + expectedMaximumUserFacingTime = null, + expectedNumberOfRecord = null, + expectedRequestOpcode = null, + expectedResponseCode = null + + ) + /** + + * Packet18 + * Composing packet - report records - last record + */ + racpTestVectors[18] = RacpTestVector( + + testPacket = byteArrayOf(0x01, 0x06), + expectedParsingResult = true, + expectedOpcode = Opcode.REPORT_STORED_RECORDS, + expectedOperator = Operator.LAST_RECORD, + expectedFilter = null, + expectedMinimumSequenceNumber = null, + expectedMaximumSequenceNumber = null, + expectedMinimumUserFacingTime = null, + expectedMaximumUserFacingTime = null, + expectedNumberOfRecord = null, + expectedRequestOpcode = null, + expectedResponseCode = null + + ) + /** + + * Packet19 + * Composing packet - report records - error case, unsupported operator + */ + racpTestVectors[19] = RacpTestVector( + + testPacket = byteArrayOf(0x01, 0x00), + expectedParsingResult = false, + expectedOpcode = Opcode.REPORT_STORED_RECORDS, + expectedOperator = Operator.NULL, + expectedFilter = null, + expectedMinimumSequenceNumber = null, + expectedMaximumSequenceNumber = null, + expectedMinimumUserFacingTime = null, + expectedMaximumUserFacingTime = null, + expectedNumberOfRecord = null, + expectedRequestOpcode = null, + expectedResponseCode = null + ) + + /** + * Packet20 + * Composing packet - report records - error case, unsupported opcode + */ + racpTestVectors[20] = RacpTestVector( + + testPacket = byteArrayOf(0x00, 0x00), + expectedParsingResult = false, + expectedOpcode = Opcode.RESERVED_FOR_FUTURE_USE, + expectedOperator = null, + expectedFilter = null, + expectedMinimumSequenceNumber = null, + expectedMaximumSequenceNumber = null, + expectedMinimumUserFacingTime = null, + expectedMaximumUserFacingTime = null, + expectedNumberOfRecord = null, + expectedRequestOpcode = null, + expectedResponseCode = null + ) + + /** + * packet21 + * general response successful with crc + */ + racpTestVectors[21] = RacpTestVector( + testPacket = byteArrayOf(0x06, 0x00, 0x01, 0x01, -22, 64), + expectedParsingResult = true, + expectedOpcode = Opcode.RESPONSE_CODE, + expectedOperator = Operator.NULL, + expectedFilter = null, + expectedMinimumSequenceNumber = null, + expectedMaximumSequenceNumber = null, + expectedMinimumUserFacingTime = null, + expectedMaximumUserFacingTime = null, + expectedNumberOfRecord = null, + expectedRequestOpcode = Opcode.REPORT_STORED_RECORDS, + expectedResponseCode = ResponseCode.SUCCESS, + hasCrc = true + ) + + /** + * Packet22 + * Opcode not supported has CRC + */ + racpTestVectors[2] = RacpTestVector( + testPacket = byteArrayOf(0x06, 0x00, 0x01, 0x02, 113, 114), + expectedParsingResult = true, + expectedOpcode = Opcode.RESPONSE_CODE, + expectedOperator = Operator.NULL, + expectedFilter = null, + expectedMinimumSequenceNumber = null, + expectedMaximumSequenceNumber = null, + expectedMinimumUserFacingTime = null, + expectedMaximumUserFacingTime = null, + expectedNumberOfRecord = null, + expectedRequestOpcode = Opcode.REPORT_STORED_RECORDS, + expectedResponseCode = ResponseCode.OP_CODE_NOT_SUPPORTED, + hasCrc = true) + + /** + * Packet23 + * Invalid Operator + */ + racpTestVectors[23] = RacpTestVector( + testPacket = byteArrayOf(0x06, 0x00, 0x01, 0x03, -8, 99), + expectedParsingResult = true, + expectedOpcode = Opcode.RESPONSE_CODE, + expectedOperator = Operator.NULL, + expectedFilter = null, + expectedMinimumSequenceNumber = null, + expectedMaximumSequenceNumber = null, + expectedMinimumUserFacingTime = null, + expectedMaximumUserFacingTime = null, + expectedNumberOfRecord = null, + expectedRequestOpcode = Opcode.REPORT_STORED_RECORDS, + expectedResponseCode = ResponseCode.INVALID_OPERATOR, + hasCrc = true + ) + /** + + * Packet24 + * Operator not supported has CRC + */ + racpTestVectors[24] = RacpTestVector( + + testPacket = byteArrayOf(0x06, 0x00, 0x01, 0x04, 71, 23), + expectedParsingResult = true, + expectedOpcode = Opcode.RESPONSE_CODE, + expectedOperator = Operator.NULL, + expectedFilter = null, + expectedMinimumSequenceNumber = null, + expectedMaximumSequenceNumber = null, + expectedMinimumUserFacingTime = null, + expectedMaximumUserFacingTime = null, + expectedNumberOfRecord = null, + expectedRequestOpcode = Opcode.REPORT_STORED_RECORDS, + expectedResponseCode = ResponseCode.OPERATOR_NOT_SUPPORTED, + hasCrc = true + ) + + /** + + * Packet25 + * General response successful has CRC + */ + racpTestVectors[25] = RacpTestVector( + + testPacket = byteArrayOf(0x06, 0x00, 0x01, 0x05, -50, 6), + expectedParsingResult = true, + expectedOpcode = Opcode.RESPONSE_CODE, + expectedOperator = Operator.NULL, + expectedFilter = null, + expectedMinimumSequenceNumber = null, + expectedMaximumSequenceNumber = null, + expectedMinimumUserFacingTime = null, + expectedMaximumUserFacingTime = null, + expectedNumberOfRecord = null, + expectedRequestOpcode = Opcode.REPORT_STORED_RECORDS, + expectedResponseCode = ResponseCode.INVALID_OPERAND, + hasCrc = true + ) + /** + + * Packet26 + * No record found has CRC + */ + racpTestVectors[26] = RacpTestVector( + + testPacket = byteArrayOf(0x06, 0x00, 0x01, 0x06, 85, 52), + expectedParsingResult = true, + expectedOpcode = Opcode.RESPONSE_CODE, + expectedOperator = Operator.NULL, + expectedFilter = null, + expectedMinimumSequenceNumber = null, + expectedMaximumSequenceNumber = null, + expectedMinimumUserFacingTime = null, + expectedMaximumUserFacingTime = null, + expectedNumberOfRecord = null, + expectedRequestOpcode = Opcode.REPORT_STORED_RECORDS, + expectedResponseCode = ResponseCode.NO_RECORDS_FOUND, + hasCrc = true + ) + /** + + * Packet27 + * Abort unsuccessful has CRC + */ + racpTestVectors[27] = RacpTestVector( + + testPacket = byteArrayOf(0x06, 0x00, 0x01, 0x07, -36, 37), + expectedParsingResult = true, + expectedOpcode = Opcode.RESPONSE_CODE, + expectedOperator = Operator.NULL, + expectedFilter = null, + expectedMinimumSequenceNumber = null, + expectedMaximumSequenceNumber = null, + expectedMinimumUserFacingTime = null, + expectedMaximumUserFacingTime = null, + expectedNumberOfRecord = null, + expectedRequestOpcode = Opcode.REPORT_STORED_RECORDS, + expectedResponseCode = ResponseCode.ABORT_UNSUCCESSFUL, + hasCrc = true + ) + } + + + @Test + fun testParsingSuccessIndicator() { + System.out.printf("testParsingSuccessIndicator\n") + for (testVector in racpTestVectors) { + System.out.printf("testing racpTestVector ${testVector.key}\n") + Assert.assertEquals(testVector.value.expectedParsingResult, RACP(mockBTCharacteristic(testVector.value.testPacket), testVector.value.hasCrc).successfulParsing) + } + } + + @Test + fun testParsingOpcode() { + System.out.printf("testParsingOpcode\n") + for (testVector in racpTestVectors) { + System.out.printf("testing racpTestVector ${testVector.key}\n") + Assert.assertEquals(testVector.value.expectedOpcode, RACP(mockBTCharacteristic(testVector.value.testPacket), testVector.value.hasCrc).opcode) + } + } + + @Test + fun testParsingOperator() { + System.out.printf("testParsingOperator\n") + for (testVector in racpTestVectors) { + System.out.printf("testing racpTestVector ${testVector.key}\n") + Assert.assertEquals(testVector.value.expectedOperator, RACP(mockBTCharacteristic(testVector.value.testPacket), testVector.value.hasCrc).operator) + } + } + + @Test + fun testParsingFilter() { + System.out.printf("testParsingFilter\n") + for (testVector in racpTestVectors) { + System.out.printf("testing racpTestVector ${testVector.key}\n") + Assert.assertEquals(testVector.value.expectedFilter, RACP(mockBTCharacteristic(testVector.value.testPacket), testVector.value.hasCrc).filterType) + } + } + + @Test + fun testParsingMinimumSequenceNumber() { + System.out.printf("testMinimumSequenceNumber\n") + for (testVector in racpTestVectors) { + System.out.printf("testing racpTestVector ${testVector.key}\n") + Assert.assertEquals(testVector.value.expectedMinimumSequenceNumber, RACP(mockBTCharacteristic(testVector.value.testPacket), testVector.value.hasCrc).minimumFilterValueSequenceNumber) + } + } + + @Test + fun testParsingMaximumSequenceNumber() { + System.out.printf("testMaximumSequenceNumber\n") + for (testVector in racpTestVectors) { + System.out.printf("testing racpTestVector ${testVector.key}\n") + Assert.assertEquals(testVector.value.expectedMaximumSequenceNumber, RACP(mockBTCharacteristic(testVector.value.testPacket), testVector.value.hasCrc).maximumFilterValueSequenceNumber) + } + } + + @Test + fun testParsingMinimumUserFacingTime() { + System.out.printf("testMinimumUserFacingTime\n") + for (testVector in racpTestVectors) { + System.out.printf("testing racpTestVector ${testVector.key}\n") + Assert.assertEquals(testVector.value.expectedMinimumUserFacingTime, RACP(mockBTCharacteristic(testVector.value.testPacket), testVector.value.hasCrc).minimumFilterValueUserFacingTime) + } + } + + @Test + fun testParsingMaximumUserFacingTime() { + System.out.printf("testMaximumUserFacingTime\n") + for (testVector in racpTestVectors) { + System.out.printf("testing racpTestVector ${testVector.key}\n") + Assert.assertEquals(testVector.value.expectedMaximumUserFacingTime, RACP(mockBTCharacteristic(testVector.value.testPacket), testVector.value.hasCrc).maximumFilterValueUserFacingTime) + } + } + + @Test + fun testParsingRequestedOpcode() { + System.out.printf("testRequestedOpcode\n") + for (testVector in racpTestVectors) { + System.out.printf("testing racpTestVector ${testVector.key}\n") + Assert.assertEquals(testVector.value.expectedRequestOpcode, RACP(mockBTCharacteristic(testVector.value.testPacket), testVector.value.hasCrc).requestOpcode) + } + } + + @Test + fun testParsingRequestedResponse() { + System.out.printf("testRequestedResponse\n") + for (testVector in racpTestVectors) { + System.out.printf("testing racpTestVector ${testVector.key}\n") + Assert.assertEquals(testVector.value.expectedResponseCode, RACP(mockBTCharacteristic(testVector.value.testPacket), testVector.value.hasCrc).responseCode) + } + } + + @Test + fun testParsingNumberOfRecordResponse() { + System.out.printf("testNumberOfRecordResponse\n") + for (testVector in racpTestVectors) { + System.out.printf("testing racpTestVector ${testVector.key}\n") + Assert.assertEquals(testVector.value.expectedNumberOfRecord, RACP(mockBTCharacteristic(testVector.value.testPacket), testVector.value.hasCrc).numberOfRecordResponse) + } + } + + @Test + fun testComposingRacp() { + System.out.printf("testComposingRacp\n") + var testRacp: RACP + var successCount = 0 + var skipCount = 0 + + for (testVector in racpTestVectors) { + System.out.printf("testing racpTestVector ${testVector.key}\n") + if (testVector.value.expectedParsingResult == false) { + System.out.printf("This case is expected to failed in parsing, so cant be composed backwards.\n") + skipCount++ + continue + } + testRacp = RACP(mockBTCharacteristic(ByteArray(0)), testVector.value.hasCrc, isComposing = true) + testRacp.operator = testVector.value.expectedOperator + testRacp.opcode = testVector.value.expectedOpcode + testRacp.filterType = testVector.value.expectedFilter + testRacp.minimumFilterValueUserFacingTime = testVector.value.expectedMinimumUserFacingTime + testRacp.minimumFilterValueSequenceNumber = testVector.value.expectedMinimumSequenceNumber + testRacp.maximumFilterValueUserFacingTime = testVector.value.expectedMaximumUserFacingTime + testRacp.maximumFilterValueSequenceNumber = testVector.value.expectedMaximumSequenceNumber + testRacp.requestOpcode = testVector.value.expectedRequestOpcode + testRacp.responseCode = testVector.value.expectedResponseCode + testRacp.hasCrc = testVector.value.hasCrc + testRacp.numberOfRecordResponse = testVector.value.expectedNumberOfRecord + val composedPacket = testRacp.composeCharacteristic(testVector.value.hasCrc) + System.out.printf("composed: " + composedPacket.contentToString() + "\n") + System.out.printf("test packet: " + testVector.value.testPacket.contentToString() + "\n") + Assert.assertTrue(testVector.value.testPacket.contentEquals(composedPacket)) + } + System.out.printf("Total cases: ${racpTestVectors.size} Cases skipped: $skipCount\n") + } + + @Test + fun testAppendingCrc() { + System.out.printf("testAppendingCrC in RACP\n") + var testRacp: RACP + var skipCount = 0 + + for (testVector in racpTestVectors) { + System.out.printf("testing racpTestVector ${testVector.key}\n") + if (testVector.value.expectedParsingResult == false) { + System.out.printf("This case is expected to failed in parsing, so cant be composed backwards.\n") + skipCount++ + continue + } + testRacp = RACP(mockBTCharacteristic(ByteArray(0)), testVector.value.hasCrc, isComposing = true) + testRacp.operator = testVector.value.expectedOperator + testRacp.opcode = testVector.value.expectedOpcode + testRacp.filterType = testVector.value.expectedFilter + testRacp.minimumFilterValueUserFacingTime = testVector.value.expectedMinimumUserFacingTime + testRacp.minimumFilterValueSequenceNumber = testVector.value.expectedMinimumSequenceNumber + testRacp.maximumFilterValueUserFacingTime = testVector.value.expectedMaximumUserFacingTime + testRacp.maximumFilterValueSequenceNumber = testVector.value.expectedMaximumSequenceNumber + testRacp.requestOpcode = testVector.value.expectedRequestOpcode + testRacp.responseCode = testVector.value.expectedResponseCode + testRacp.hasCrc = testVector.value.hasCrc + testRacp.numberOfRecordResponse = testVector.value.expectedNumberOfRecord + val composedPacket = testRacp.composeCharacteristic(testVector.value.hasCrc) + System.out.printf("composed: " + composedPacket.contentToString() + "\n") + System.out.printf("test packet: " + testVector.value.testPacket.contentToString() + "\n") + Assert.assertTrue(testVector.value.testPacket.contentEquals(composedPacket)) + } + System.out.printf("Total cases: ${racpTestVectors.size} Cases skipped: $skipCount\n") + } +} \ No newline at end of file diff --git a/jdrfandroidbleparser/src/test/java/org/ehealthinnovation/jdrfandroidbleparser/encodedvalues/bgmcontext/FlagsTest.kt b/jdrfandroidbleparser/src/test/java/org/ehealthinnovation/jdrfandroidbleparser/encodedvalue/bgm/bgmcontext/FlagsTest.kt similarity index 98% rename from jdrfandroidbleparser/src/test/java/org/ehealthinnovation/jdrfandroidbleparser/encodedvalues/bgmcontext/FlagsTest.kt rename to jdrfandroidbleparser/src/test/java/org/ehealthinnovation/jdrfandroidbleparser/encodedvalue/bgm/bgmcontext/FlagsTest.kt index c6972ff..4cc5510 100644 --- a/jdrfandroidbleparser/src/test/java/org/ehealthinnovation/jdrfandroidbleparser/encodedvalues/bgmcontext/FlagsTest.kt +++ b/jdrfandroidbleparser/src/test/java/org/ehealthinnovation/jdrfandroidbleparser/encodedvalue/bgm/bgmcontext/FlagsTest.kt @@ -1,4 +1,4 @@ -package org.ehealthinnovation.jdrfandroidbleparser.encodedvalues.bgmcontext +package org.ehealthinnovation.jdrfandroidbleparser.encodedvalues.bgm.bgmcontext import junit.framework.Assert import org.ehealthinnovation.jdrfandroidbleparser.BaseTest diff --git a/jdrfandroidbleparser/src/test/java/org/ehealthinnovation/jdrfandroidbleparser/encodedvalue/bgmmeasurement/FlagsTest.kt b/jdrfandroidbleparser/src/test/java/org/ehealthinnovation/jdrfandroidbleparser/encodedvalue/bgm/bgmmeasurement/FlagsTest.kt similarity index 95% rename from jdrfandroidbleparser/src/test/java/org/ehealthinnovation/jdrfandroidbleparser/encodedvalue/bgmmeasurement/FlagsTest.kt rename to jdrfandroidbleparser/src/test/java/org/ehealthinnovation/jdrfandroidbleparser/encodedvalue/bgm/bgmmeasurement/FlagsTest.kt index a494839..6d3e2d9 100644 --- a/jdrfandroidbleparser/src/test/java/org/ehealthinnovation/jdrfandroidbleparser/encodedvalue/bgmmeasurement/FlagsTest.kt +++ b/jdrfandroidbleparser/src/test/java/org/ehealthinnovation/jdrfandroidbleparser/encodedvalue/bgm/bgmmeasurement/FlagsTest.kt @@ -1,9 +1,8 @@ -package org.ehealthinnovation.jdrfandroidbleparser.encodedvalue.bgmmeasurement +package org.ehealthinnovation.jdrfandroidbleparser.encodedvalue.bgm.bgmmeasurement import junit.framework.Assert import org.ehealthinnovation.jdrfandroidbleparser.BaseTest import org.ehealthinnovation.jdrfandroidbleparser.bgm.encodedvalue.bgmmeasurement.Flags -import org.junit.Assert.* import org.junit.Test import java.util.* diff --git a/jdrfandroidbleparser/src/test/java/org/ehealthinnovation/jdrfandroidbleparser/encodedvalue/bgmmeasurement/SensorStatusAnnunciationTest.kt b/jdrfandroidbleparser/src/test/java/org/ehealthinnovation/jdrfandroidbleparser/encodedvalue/bgm/bgmmeasurement/SensorStatusAnnunciationTest.kt similarity index 96% rename from jdrfandroidbleparser/src/test/java/org/ehealthinnovation/jdrfandroidbleparser/encodedvalue/bgmmeasurement/SensorStatusAnnunciationTest.kt rename to jdrfandroidbleparser/src/test/java/org/ehealthinnovation/jdrfandroidbleparser/encodedvalue/bgm/bgmmeasurement/SensorStatusAnnunciationTest.kt index 827b03f..28a7823 100644 --- a/jdrfandroidbleparser/src/test/java/org/ehealthinnovation/jdrfandroidbleparser/encodedvalue/bgmmeasurement/SensorStatusAnnunciationTest.kt +++ b/jdrfandroidbleparser/src/test/java/org/ehealthinnovation/jdrfandroidbleparser/encodedvalue/bgm/bgmmeasurement/SensorStatusAnnunciationTest.kt @@ -1,9 +1,8 @@ -package org.ehealthinnovation.jdrfandroidbleparser.encodedvalue.bgmmeasurement +package org.ehealthinnovation.jdrfandroidbleparser.encodedvalue.bgm.bgmmeasurement import junit.framework.Assert import org.ehealthinnovation.jdrfandroidbleparser.BaseTest import org.ehealthinnovation.jdrfandroidbleparser.bgm.encodedvalue.bgmmeasurement.SensorStatusAnnunciation -import org.junit.Assert.* import org.junit.Test import java.util.* diff --git a/jdrfandroidbleparser/src/test/java/org/ehealthinnovation/jdrfandroidbleparser/utility/CrcHelperTest.kt b/jdrfandroidbleparser/src/test/java/org/ehealthinnovation/jdrfandroidbleparser/utility/CrcHelperTest.kt new file mode 100644 index 0000000..723aa2b --- /dev/null +++ b/jdrfandroidbleparser/src/test/java/org/ehealthinnovation/jdrfandroidbleparser/utility/CrcHelperTest.kt @@ -0,0 +1,133 @@ +package org.ehealthinnovation.jdrfandroidbleparser.utility + +import org.junit.Assert +import org.junit.Assert.* +import org.junit.Test +import kotlin.jvm.internal.Ref + +class CrcHelperTest { + /** + * Test Packet 1 + * Null package + */ + val testByteArray1 : ByteArray = byteArrayOf(0x00, 0x00) + val expectedCrc1 : Short = 0xf0b8.toShort(); + + /** + * Test Pakcet 2 + * Three bytes + */ + val testByteArray2 : ByteArray = byteArrayOf(0x01, 0x00, 0x00) + val expectedCrc2 : Short = 0x63ef; + + /** + * Test Packet 3 + * A Glucose Measurement + */ + val testByteArray3 : ByteArray = byteArrayOf(0x13, 0x01, 0x00, 0xe2.toByte(), 0x07, 0x06, 0x06, 0x0e, 0x35, 0x0e, 0x00, 0x00, 0x54, 0xb0.toByte(), 0x19) + val expectedCrc3 : Short = 0x8368.toShort(); + + /** + * Test Packet 4 + * A Glucose Measurement + */ + val testByteArray4 : ByteArray = byteArrayOf(0x03, 0x02, 0x00, 0xe2.toByte(), 0x07, 0x06, 0x06, 0x0e, 0x36, 0x12, 0x10, 0x00, 0x60, 0xb0.toByte(), 0x11.toByte()) + val expectedCrc4 : Short = 0xa45f.toShort(); + + /** + * Test Packet 5 + * A Glucose Measurement + */ + val testByteArray5 : ByteArray = byteArrayOf(0x03, 0x03, 0x00, 0xe2.toByte(), 0x07, 0x06, 0x06, 0x0e, 0x36, 0x2e, 0x00, 0x00, 0x75, 0xb0.toByte(), 0x12.toByte()) + val expectedCrc5 : Short = 0x1662.toShort(); + + /** + * Test Packet 6 + * Test verifier for two null bytes + */ + val testByteArray6 : ByteArray = byteArrayOf(0x00, 0x00, 0xb8.toByte(), 0xf0.toByte()) + val expectedResult6 : Boolean = true + + /** + * Test Packet 7 + * Test verifier for three null bytes + */ + val testByteArray7 : ByteArray = byteArrayOf(0x01, 0x00, 0x00, 0xef.toByte(), 0x63) + val expectedResult7 : Boolean = true + + /** + * Test Packet 8 + * Test verifier for glucose measurement packet + */ + val testByteArray8 : ByteArray = byteArrayOf(0x13, 0x01, 0x00, 0xe2.toByte(), 0x07, 0x06, 0x06, 0x0e, 0x35, 0x0e, 0x00, 0x00, 0x54, 0xb0.toByte(), 0x19.toByte(), 0x68, 0x83.toByte()) + val expectedResult8 : Boolean = true + + /** + * Test Packet 9 + * Test verifier for glucose measurement packet + */ + val testByteArray9 : ByteArray = byteArrayOf(0x03, 0x02, 0x00, 0xe2.toByte(), 0x07, 0x06, 0x06, 0x0e, 0x36, 0x12, 0x10, 0x00, 0x60, 0xb0.toByte(), 0x11.toByte(), 0x5f, 0xa4.toByte()) + val expectedResult9 : Boolean = true + + /** + * Test Packet 10 + * Test verifier for glucose measurement packet + */ + val testByteArray10 : ByteArray = byteArrayOf(0x03, 0x03, 0x00, 0xe2.toByte(), 0x07, 0x06, 0x06, 0x0e, 0x36, 0x2e, 0x00, 0x00, 0x75, 0xb0.toByte(), 0x12.toByte(), 0x62, 0x16) + val expectedResult10 : Boolean = true + + /** + * Test Packet 11 + * Test Verifier for three bytes, negative case + */ + val testByteArray11 : ByteArray = byteArrayOf(0x01, 0x00, 0x00, 0xef.toByte(), 0x64) + val expectedResult11 : Boolean = false + + /** + * Test Packet 12 + * Test Verifier for glucose measurement negative case + */ + val testByteArray12 : ByteArray = byteArrayOf(0x13, 0x01, 0x00, 0xe2.toByte(), 0x07, 0x06, 0x06, 0x0e, 0x35, 0x0e, 0x00, 0x00, 0x54, 0xb0.toByte(), 0x19.toByte(), 0x68, 0x82.toByte()) + val expectedResult12 : Boolean = false + + /** + * Test Packet 13 + * Test Verifier for glucose measurement negative case + */ + val testByteArray13 : ByteArray = byteArrayOf(0x03, 0x02, 0x00, 0xe2.toByte(), 0x07, 0x06, 0x06, 0x0e, 0x36, 0x12, 0x10, 0x00, 0x60, 0xb0.toByte(), 0x11.toByte(), 0x5f, 0xa5.toByte()) + val expectedResult13 : Boolean = false; + + /** + * Test Pakcet 14 + * Test Verifier for glucose measurement negative case + */ + val testByteArray14 : ByteArray = byteArrayOf(0x03, 0x03, 0x00, 0xe2.toByte(), 0x07, 0x06, 0x06, 0x0e, 0x36, 0x2e, 0x00, 0x00, 0x75, 0xb0.toByte(), 0x12.toByte(), 0x62, 0x17) + val expectedResult14 : Boolean = false; + + @Test + fun testTag() { + Assert.assertEquals(CrcHelper::class.java.canonicalName as String, CrcHelper.tag) + } + + @Test + fun testGeneratingCrcCode(){ + Assert.assertEquals(expectedCrc1, CrcHelper.calculateCcittCrc16(testByteArray1, testByteArray1.size)) + Assert.assertEquals(expectedCrc2, CrcHelper.calculateCcittCrc16(testByteArray2, testByteArray2.size)) + Assert.assertEquals(expectedCrc3, CrcHelper.calculateCcittCrc16(testByteArray3, testByteArray3.size)) + Assert.assertEquals(expectedCrc4, CrcHelper.calculateCcittCrc16(testByteArray4, testByteArray4.size)) + Assert.assertEquals(expectedCrc5, CrcHelper.calculateCcittCrc16(testByteArray5, testByteArray5.size)) + } + + @Test + fun testVerifyCrcAppendedPackets(){ + Assert.assertEquals(expectedResult6, CrcHelper.testCcittCrc16(testByteArray6, testByteArray6.size)) + Assert.assertEquals(expectedResult7, CrcHelper.testCcittCrc16(testByteArray7, testByteArray7.size)) + Assert.assertEquals(expectedResult8, CrcHelper.testCcittCrc16(testByteArray8, testByteArray8.size)) + Assert.assertEquals(expectedResult9, CrcHelper.testCcittCrc16(testByteArray9, testByteArray9.size)) + Assert.assertEquals(expectedResult10, CrcHelper.testCcittCrc16(testByteArray10, testByteArray10.size)) + Assert.assertEquals(expectedResult11, CrcHelper.testCcittCrc16(testByteArray11, testByteArray11.size)) + Assert.assertEquals(expectedResult12, CrcHelper.testCcittCrc16(testByteArray12, testByteArray12.size)) + Assert.assertEquals(expectedResult13, CrcHelper.testCcittCrc16(testByteArray13, testByteArray13.size)) + Assert.assertEquals(expectedResult14, CrcHelper.testCcittCrc16(testByteArray14, testByteArray14.size)) + } +} \ No newline at end of file