Skip to content
This repository was archived by the owner on Feb 16, 2019. It is now read-only.
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -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.*
Expand Down
Original file line number Diff line number Diff line change
@@ -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.*

/**
Expand Down Expand Up @@ -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
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -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.*
Expand Down
Original file line number Diff line number Diff line change
@@ -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
Expand Down

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
@@ -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
Expand Down
Original file line number Diff line number Diff line change
@@ -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

Expand Down
Original file line number Diff line number Diff line change
@@ -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

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

Expand All @@ -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)
Expand All @@ -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.
Expand All @@ -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.
*
Expand Down Expand Up @@ -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.
*
Expand All @@ -145,4 +226,6 @@ abstract class BaseCharacteristic(val uuid: Int) {
return newIndex
}



}
Original file line number Diff line number Diff line change
@@ -1,22 +1,18 @@
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
* [BluetoothGattCharacteristic] to primitive data types, they must also be able to do the reverse
* 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 {

/**
* Compose a given characteristic into the resulting [ByteArray].
*/
@Throws(IllegalStateException::class)
fun composeCharacteristic(): ByteArray
fun composeCharacteristic(hasCrc : Boolean): ByteArray

}
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package org.ehealthinnovation.jdrfandroidbleparser.bgm.characteristic.common
package org.ehealthinnovation.jdrfandroidbleparser.common

import android.bluetooth.BluetoothGattCharacteristic
import kotlin.jvm.java
Expand Down
Original file line number Diff line number Diff line change
@@ -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,
Expand Down
Original file line number Diff line number Diff line change
@@ -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
}
}
Loading