Initial project commit
This commit is contained in:
@@ -0,0 +1,20 @@
|
||||
package com.eveningoutpost.dexdrip.ImportedLibraries.dexcom;
|
||||
|
||||
// This code and this particular library are from the NightScout android uploader
|
||||
// Check them out here: https://github.com/nightscout/android-uploader
|
||||
// Some of this code may have been modified for use in this project
|
||||
|
||||
public class CRC16 {
|
||||
public static byte[] calculate(byte[] buff, int start, int end) {
|
||||
int crcShort = 0;
|
||||
for (int i = start; i < end; i++) {
|
||||
crcShort = ((crcShort >>> 8) | (crcShort << 8) )& 0xffff;
|
||||
crcShort ^= (buff[i] & 0xff);
|
||||
crcShort ^= ((crcShort & 0xff) >> 4);
|
||||
crcShort ^= (crcShort << 12) & 0xffff;
|
||||
crcShort ^= ((crcShort & 0xFF) << 5) & 0xffff;
|
||||
}
|
||||
crcShort &= 0xffff;
|
||||
return new byte[] {(byte) (crcShort & 0xff), (byte) ((crcShort >> 8) & 0xff)};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
package com.eveningoutpost.dexdrip.ImportedLibraries.dexcom;
|
||||
|
||||
// This code and this particular library are from the NightScout android uploader
|
||||
// Check them out here: https://github.com/nightscout/android-uploader
|
||||
// Some of this code may have been modified for use in this project
|
||||
|
||||
public class CRCFailRuntimeException extends RuntimeException {
|
||||
public CRCFailRuntimeException(String message){
|
||||
super(message);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,210 @@
|
||||
package com.eveningoutpost.dexdrip.ImportedLibraries.dexcom;
|
||||
|
||||
// This code and this particular library are from the NightScout android uploader
|
||||
// Check them out here: https://github.com/nightscout/android-uploader
|
||||
// Some of this code may have been modified for use in this project
|
||||
|
||||
public class Dex_Constants {
|
||||
|
||||
public final static int NULL = 0;
|
||||
public final static int ACK = 1;
|
||||
public final static int NAK = 2;
|
||||
public final static int INVALID_COMMAND = 3;
|
||||
public final static int INVALID_PARAM = 4;
|
||||
public final static int INCOMPLETE_PACKET_RECEIVED = 5;
|
||||
public final static int RECEIVER_ERROR = 6;
|
||||
public final static int INVALID_MODE = 7;
|
||||
public final static int PING = 10;
|
||||
public final static int READ_FIRMWARE_HEADER = 11;
|
||||
public final static int READ_DATABASE_PARTITION_INFO = 15;
|
||||
public final static int READ_DATABASE_PAGE_RANGE = 16;
|
||||
public final static int READ_DATABASE_PAGES = 17;
|
||||
public final static int READ_DATABASE_PAGE_HEADER = 18;
|
||||
public final static int READ_TRANSMITTER_ID = 25;
|
||||
public final static int WRITE_TRANSMITTER_ID = 26;
|
||||
public final static int READ_LANGUAGE = 27;
|
||||
public final static int WRITE_LANGUAGE = 28;
|
||||
public final static int READ_DISPLAY_TIME_OFFSET = 29;
|
||||
public final static int WRITE_DISPLAY_TIME_OFFSET = 30;
|
||||
public final static int READ_RTC = 31;
|
||||
public final static int RESET_RECEIVER = 32;
|
||||
public final static int READ_BATTERY_LEVEL = 33;
|
||||
public final static int READ_SYSTEM_TIME = 34;
|
||||
public final static int READ_SYSTEM_TIME_OFFSET = 35;
|
||||
public final static int WRITE_SYSTEM_TIME = 36;
|
||||
public final static int READ_GLUCOSE_UNIT = 37;
|
||||
public final static int WRITE_GLUCOSE_UNIT = 38;
|
||||
public final static int READ_BLINDED_MODE = 39;
|
||||
public final static int WRITE_BLINDED_MODE = 40;
|
||||
public final static int READ_CLOCK_MODE = 41;
|
||||
public final static int WRITE_CLOCK_MODE = 42;
|
||||
public final static int READ_DEVICE_MODE = 43;
|
||||
public final static int ERASE_DATABASE = 45;
|
||||
public final static int SHUTDOWN_RECEIVER = 46;
|
||||
public final static int WRITE_PC_PARAMETERS = 47;
|
||||
public final static int READ_BATTERY_STATE = 48;
|
||||
public final static int READ_HARDWARE_BOARD_ID = 49;
|
||||
public final static int READ_FIRMWARE_SETTINGS = 54;
|
||||
public final static int READ_ENABLE_SETUP_WIZARD_FLAG = 55;
|
||||
public final static int READ_SETUP_WIZARD_STATE = 57;
|
||||
public final static int MAX_COMMAND = 59;
|
||||
public final static int MAX_POSSIBLE_COMMAND = 255;
|
||||
public final static int EGV_VALUE_MASK = 1023;
|
||||
public final static int EGV_DISPLAY_ONLY_MASK = 32768;
|
||||
public final static int EGV_TREND_ARROW_MASK = 15;
|
||||
public final static int EGV_NOISE_MASK = 112;
|
||||
public final static float MG_DL_TO_MMOL_L = 0.05556f;
|
||||
public final static int CRC_LEN = 2;
|
||||
public static final int TRANSMITTER_BATTERY_LOW = 210;
|
||||
public static final int TRANSMITTER_BATTERY_EMPTY = 207;
|
||||
|
||||
public enum BATTERY_STATES {
|
||||
NONE,
|
||||
CHARGING,
|
||||
NOT_CHARGING,
|
||||
NTC_FAULT,
|
||||
BAD_BATTERY
|
||||
}
|
||||
|
||||
public enum RECORD_TYPES {
|
||||
MANUFACTURING_DATA,
|
||||
FIRMWARE_PARAMETER_DATA,
|
||||
PC_SOFTWARE_PARAMETER,
|
||||
SENSOR_DATA,
|
||||
EGV_DATA,
|
||||
CAL_SET,
|
||||
DEVIATION,
|
||||
INSERTION_TIME,
|
||||
RECEIVER_LOG_DATA,
|
||||
RECEIVER_ERROR_DATA,
|
||||
METER_DATA,
|
||||
USER_EVENT_DATA,
|
||||
USER_SETTING_DATA,
|
||||
MAX_VALUE
|
||||
}
|
||||
|
||||
public enum TREND_ARROW_VALUES {
|
||||
NONE(0),
|
||||
DOUBLE_UP(1,"\u21C8", "DoubleUp"),
|
||||
SINGLE_UP(2,"\u2191", "SingleUp"),
|
||||
UP_45(3,"\u2197", "FortyFiveUp"),
|
||||
FLAT(4,"\u2192", "Flat"),
|
||||
DOWN_45(5,"\u2198", "FortyFiveDown"),
|
||||
SINGLE_DOWN(6,"\u2193", "SingleDown"),
|
||||
DOUBLE_DOWN(7,"\u21CA", "DoubleDown"),
|
||||
NOT_COMPUTABLE(8, "", "NOT_COMPUTABLE"),
|
||||
OUT_OF_RANGE(9, "", "OUT_OF_RANGE");
|
||||
|
||||
private String arrowSymbol;
|
||||
private String trendName;
|
||||
private int myID;
|
||||
|
||||
TREND_ARROW_VALUES(int id, String a, String t) {
|
||||
myID=id;
|
||||
arrowSymbol = a;
|
||||
trendName = t;
|
||||
}
|
||||
|
||||
TREND_ARROW_VALUES(int id) {
|
||||
this(id,null, null);
|
||||
}
|
||||
|
||||
public String Symbol() {
|
||||
if (arrowSymbol == null) {
|
||||
return "\u2194";
|
||||
} else {
|
||||
return arrowSymbol;
|
||||
}
|
||||
}
|
||||
|
||||
public String friendlyTrendName() {
|
||||
if (trendName == null) {
|
||||
return this.name().replace("_", " ");
|
||||
} else {
|
||||
return this.trendName;
|
||||
}
|
||||
}
|
||||
|
||||
public int getID(){
|
||||
return myID;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public enum SPECIALBGVALUES_MGDL {
|
||||
NONE("??0", 0),
|
||||
SENSORNOTACTIVE("?SN", 1),
|
||||
MINIMALLYEGVAB("??2", 2),
|
||||
NOANTENNA("?NA", 3),
|
||||
SENSOROUTOFCAL("?NC", 5),
|
||||
COUNTSAB("?CD", 6),
|
||||
ABSOLUTEAB("?AD", 9),
|
||||
POWERAB("???", 10),
|
||||
RFBADSTATUS("?RF", 12);
|
||||
|
||||
|
||||
private String name;
|
||||
private int val;
|
||||
private SPECIALBGVALUES_MGDL(String s, int i){
|
||||
name=s;
|
||||
val=i;
|
||||
}
|
||||
|
||||
public int getValue(){
|
||||
return val;
|
||||
}
|
||||
|
||||
public String toString(){
|
||||
return name;
|
||||
}
|
||||
|
||||
public static SPECIALBGVALUES_MGDL getEGVSpecialValue(int val){
|
||||
for (SPECIALBGVALUES_MGDL e: values()){
|
||||
if (e.getValue()==val)
|
||||
return e;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static boolean isSpecialValue(int val){
|
||||
for (SPECIALBGVALUES_MGDL e: values()){
|
||||
if (e.getValue()==val)
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public enum InsertionState {
|
||||
NONE,
|
||||
REMOVED,
|
||||
EXPIRED,
|
||||
RESIDUAL_DEVIATION,
|
||||
COUNTS_DEVIATION,
|
||||
SECOND_SESSION,
|
||||
OFF_TIME_LOSS,
|
||||
STARTED,
|
||||
BAD_TRANSMITTER,
|
||||
MANUFACTURING_MODE,
|
||||
MAX_VALUE
|
||||
}
|
||||
|
||||
public enum NOISE {
|
||||
NOISE_NONE(0),
|
||||
CLEAN(1),
|
||||
LIGHT(2),
|
||||
MEDIUM(3),
|
||||
HEAVY(4),
|
||||
NOT_COMPUTED(5),
|
||||
MAX(6);
|
||||
|
||||
private final int value;
|
||||
|
||||
private NOISE(int value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,108 @@
|
||||
package com.eveningoutpost.dexdrip.ImportedLibraries.dexcom;
|
||||
|
||||
import com.eveningoutpost.dexdrip.Models.UserError.Log;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
// This code and this particular library are from the NightScout android uploader
|
||||
// Check them out here: https://github.com/nightscout/android-uploader
|
||||
// Some of this code may have been modified for use in this project
|
||||
|
||||
public class PacketBuilder {
|
||||
public static final int MAX_PAYLOAD = 1584;
|
||||
public static final int MIN_LEN = 6;
|
||||
public static final int MAX_LEN = MAX_PAYLOAD + MIN_LEN;
|
||||
public static final byte SOF = 0x01;
|
||||
public static final int OFFSET_SOF = 0;
|
||||
public static final int OFFSET_LENGTH = 1;
|
||||
public static final int OFFSET_NULL = 2;
|
||||
public static final byte NULL = 0x00;
|
||||
public static final int OFFSET_CMD = 3;
|
||||
public static final int OFFSET_PAYLOAD = 4;
|
||||
public static final int CRC_LEN = 2;
|
||||
public static final int HEADER_LEN = 4;
|
||||
public ArrayList<Byte> packet;
|
||||
public int command;
|
||||
public ArrayList<Byte> payload;
|
||||
|
||||
public PacketBuilder(int command) {
|
||||
this.command = command;
|
||||
}
|
||||
|
||||
public PacketBuilder(int command, ArrayList<Byte> payload) {
|
||||
this.command = command;
|
||||
this.payload = payload;
|
||||
}
|
||||
|
||||
public byte[] compose() {
|
||||
packet = new ArrayList<Byte>();
|
||||
packet.add(OFFSET_SOF, SOF);
|
||||
packet.add(OFFSET_LENGTH, getLength());
|
||||
packet.add(OFFSET_NULL, NULL);
|
||||
packet.add(OFFSET_CMD, (byte) command);
|
||||
if (this.payload != null) { this.packet.addAll(OFFSET_PAYLOAD, this.payload); }
|
||||
byte[] crc16 = CRC16.calculate(toBytes(), 0, this.packet.size());
|
||||
this.packet.add(crc16[0]);
|
||||
this.packet.add(crc16[1]);
|
||||
Log.d("ShareTest", "About to start adding to Byte, size: " + this.packet.size());
|
||||
return this.toBytes();
|
||||
}
|
||||
|
||||
public List<byte[]> composeList() {
|
||||
packet = new ArrayList<Byte>();
|
||||
packet.add(OFFSET_SOF, SOF);
|
||||
packet.add(OFFSET_LENGTH, getLength());
|
||||
packet.add(OFFSET_NULL, NULL);
|
||||
packet.add(OFFSET_CMD, (byte) command);
|
||||
if (this.payload != null) { this.packet.addAll(OFFSET_PAYLOAD, this.payload); }
|
||||
byte[] crc16 = CRC16.calculate(toBytes(), 0, this.packet.size());
|
||||
this.packet.add(crc16[0]);
|
||||
this.packet.add(crc16[1]);
|
||||
Log.d("ShareTest", "About to start adding to ByteList, size: " + this.packet.size());
|
||||
return this.toBytesList();
|
||||
}
|
||||
|
||||
private byte getLength() {
|
||||
int packetSize = payload == null ? MIN_LEN : payload.size() + CRC_LEN + HEADER_LEN;
|
||||
|
||||
if (packetSize > MAX_LEN) {
|
||||
throw new IndexOutOfBoundsException(packetSize + " bytes, but packet must between "
|
||||
+ MIN_LEN + " and " + MAX_LEN + " bytes.");
|
||||
}
|
||||
|
||||
return (byte) packetSize;
|
||||
}
|
||||
|
||||
public byte[] toBytes() {
|
||||
byte[] b = new byte[this.packet.size()];
|
||||
for (int i = 0; i < this.packet.size(); i++) {
|
||||
b[i] = this.packet.get(i).byteValue();
|
||||
}
|
||||
return b;
|
||||
}
|
||||
|
||||
public List<byte[]> toBytesList() {
|
||||
List<byte[]> byteMessages = new ArrayList<byte[]>();
|
||||
double totalPacketSize = packet.size();
|
||||
int messages =(int) Math.ceil(totalPacketSize/18);
|
||||
for(int m = 0; m < messages; m++) {
|
||||
int thisPacketSize;
|
||||
if (m == messages - 1) {
|
||||
thisPacketSize = ((this.packet.size()+2) % 18);
|
||||
} else {
|
||||
thisPacketSize = (20);
|
||||
}
|
||||
int offset = m * 18;
|
||||
Log.d("ShareTest", "This packet size: " + thisPacketSize);
|
||||
byte[] b = new byte[thisPacketSize];
|
||||
b[0] = (byte) (m + 1);
|
||||
b[1] = (byte) (messages);
|
||||
for (int i = 2; i < thisPacketSize; i++) {
|
||||
b[i] = packet.get(offset + i - 2).byteValue();
|
||||
}
|
||||
byteMessages.add(b);
|
||||
}
|
||||
return byteMessages;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,348 @@
|
||||
package com.eveningoutpost.dexdrip.ImportedLibraries.dexcom;
|
||||
|
||||
import android.hardware.usb.UsbDevice;
|
||||
import android.hardware.usb.UsbDeviceConnection;
|
||||
|
||||
import com.eveningoutpost.dexdrip.ImportedLibraries.dexcom.records.CalRecord;
|
||||
import com.eveningoutpost.dexdrip.ImportedLibraries.dexcom.records.EGVRecord;
|
||||
import com.eveningoutpost.dexdrip.ImportedLibraries.dexcom.records.GenericXMLRecord;
|
||||
import com.eveningoutpost.dexdrip.ImportedLibraries.dexcom.records.MeterRecord;
|
||||
import com.eveningoutpost.dexdrip.ImportedLibraries.dexcom.records.PageHeader;
|
||||
import com.eveningoutpost.dexdrip.ImportedLibraries.dexcom.records.SensorRecord;
|
||||
import com.eveningoutpost.dexdrip.ImportedLibraries.usbserial.driver.UsbSerialDriver;
|
||||
import com.eveningoutpost.dexdrip.Models.UserError.Log;
|
||||
|
||||
import org.w3c.dom.Element;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.ByteOrder;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Date;
|
||||
|
||||
public class ReadData {
|
||||
|
||||
// This code and this particular library are from the NightScout android uploader
|
||||
// Check them out here: https://github.com/nightscout/android-uploader
|
||||
// Some of this code may have been modified for use in this project
|
||||
|
||||
private static final String TAG = ReadData.class.getSimpleName();
|
||||
private static final int IO_TIMEOUT = 3000;
|
||||
private static final int MIN_LEN = 256;
|
||||
private UsbSerialDriver mSerialDevice;
|
||||
protected final Object mReadBufferLock = new Object();
|
||||
private UsbDeviceConnection mConnection;
|
||||
private UsbDevice mDevice;
|
||||
|
||||
public ReadData(){}
|
||||
public ReadData(UsbSerialDriver device) {
|
||||
mSerialDevice = device;
|
||||
}
|
||||
public ReadData(UsbSerialDriver device, UsbDeviceConnection connection, UsbDevice usbDevice) {
|
||||
mSerialDevice = device;
|
||||
mConnection = connection;
|
||||
mDevice = usbDevice;
|
||||
try {
|
||||
mSerialDevice.getPorts().get(0).open(connection);
|
||||
} catch(IOException e) {
|
||||
Log.d("FAILED WHILE", "trying to open");
|
||||
}
|
||||
// }
|
||||
}
|
||||
|
||||
public EGVRecord[] getRecentEGVs() {
|
||||
int recordType = Dex_Constants.RECORD_TYPES.EGV_DATA.ordinal();
|
||||
int endPage = readDataBasePageRange(recordType);
|
||||
return readDataBasePage(recordType, endPage);
|
||||
}
|
||||
|
||||
public EGVRecord[] getRecentEGVsPages(int numOfRecentPages) {
|
||||
if (numOfRecentPages < 1) {
|
||||
throw new IllegalArgumentException("Number of pages must be greater than 1.");
|
||||
}
|
||||
Log.d(TAG, "Reading EGV page range...");
|
||||
int recordType = Dex_Constants.RECORD_TYPES.EGV_DATA.ordinal();
|
||||
int endPage = readDataBasePageRange(recordType);
|
||||
Log.d(TAG, "Reading " + numOfRecentPages + " EGV page(s)...");
|
||||
numOfRecentPages = numOfRecentPages - 1;
|
||||
EGVRecord[] allPages = new EGVRecord[0];
|
||||
for (int i = Math.min(numOfRecentPages,endPage); i >= 0; i--) {
|
||||
int nextPage = endPage - i;
|
||||
Log.d(TAG, "Reading #" + i + " EGV pages (page number " + nextPage + ")");
|
||||
EGVRecord[] ithEGVRecordPage = readDataBasePage(recordType, nextPage);
|
||||
EGVRecord[] result = Arrays.copyOf(allPages, allPages.length + ithEGVRecordPage.length);
|
||||
System.arraycopy(ithEGVRecordPage, 0, result, allPages.length, ithEGVRecordPage.length);
|
||||
allPages = result;
|
||||
}
|
||||
Log.d(TAG, "Read complete of EGV pages.");
|
||||
return allPages;
|
||||
}
|
||||
|
||||
public long getTimeSinceEGVRecord(EGVRecord egvRecord) {
|
||||
return readSystemTime() - egvRecord.getSystemTimeSeconds();
|
||||
}
|
||||
|
||||
public MeterRecord[] getRecentMeterRecords() {
|
||||
Log.d(TAG, "Reading Meter page...");
|
||||
int recordType = Dex_Constants.RECORD_TYPES.METER_DATA.ordinal();
|
||||
int endPage = readDataBasePageRange(recordType);
|
||||
return readDataBasePage(recordType, endPage);
|
||||
}
|
||||
|
||||
public SensorRecord[] getRecentSensorRecords(int numOfRecentPages) {
|
||||
if (numOfRecentPages < 1) {
|
||||
throw new IllegalArgumentException("Number of pages must be greater than 1.");
|
||||
}
|
||||
Log.d(TAG, "Reading Sensor page range...");
|
||||
int recordType = Dex_Constants.RECORD_TYPES.SENSOR_DATA.ordinal();
|
||||
int endPage = readDataBasePageRange(recordType);
|
||||
Log.d(TAG, "Reading " + numOfRecentPages + " Sensor page(s)...");
|
||||
numOfRecentPages = numOfRecentPages - 1;
|
||||
SensorRecord[] allPages = new SensorRecord[0];
|
||||
for (int i = Math.min(numOfRecentPages,endPage); i >= 0; i--) {
|
||||
int nextPage = endPage - i;
|
||||
Log.d(TAG, "Reading #" + i + " Sensor pages (page number " + nextPage + ")");
|
||||
SensorRecord[] ithSensorRecordPage = readDataBasePage(recordType, nextPage);
|
||||
SensorRecord[] result = Arrays.copyOf(allPages, allPages.length + ithSensorRecordPage.length);
|
||||
System.arraycopy(ithSensorRecordPage, 0, result, allPages.length, ithSensorRecordPage.length);
|
||||
allPages = result;
|
||||
}
|
||||
Log.d(TAG, "Read complete of Sensor pages.");
|
||||
return allPages;
|
||||
}
|
||||
|
||||
public CalRecord[] getRecentCalRecords() {
|
||||
Log.d(TAG, "Reading Cal Records page range...");
|
||||
int recordType = Dex_Constants.RECORD_TYPES.CAL_SET.ordinal();
|
||||
int endPage = readDataBasePageRange(recordType);
|
||||
Log.d(TAG, "Reading Cal Records page...");
|
||||
return readDataBasePage(recordType, endPage);
|
||||
}
|
||||
public byte[] getRecentCalRecordsTest() {
|
||||
Log.d(TAG, "Reading Cal Records page range...");
|
||||
int recordType = Dex_Constants.RECORD_TYPES.CAL_SET.ordinal();
|
||||
int endPage = readDataBasePageRange(recordType);
|
||||
Log.d(TAG, "Reading Cal Records page...");
|
||||
return readDataBasePageTest(recordType, endPage);
|
||||
}
|
||||
|
||||
public boolean ping() {
|
||||
writeCommand(Dex_Constants.PING);
|
||||
return read(MIN_LEN).getCommand() == Dex_Constants.ACK;
|
||||
}
|
||||
|
||||
public int readBatteryLevel() {
|
||||
Log.d(TAG, "Reading battery level...");
|
||||
writeCommand(Dex_Constants.READ_BATTERY_LEVEL);
|
||||
byte[] readData = read(MIN_LEN).getData();
|
||||
return ByteBuffer.wrap(readData).order(ByteOrder.LITTLE_ENDIAN).getInt();
|
||||
}
|
||||
|
||||
public String readSerialNumber() {
|
||||
int PAGE_OFFSET = 0;
|
||||
byte[] readData = readDataBasePage(Dex_Constants.RECORD_TYPES.MANUFACTURING_DATA.ordinal(), PAGE_OFFSET);
|
||||
Element md = ParsePage(readData, Dex_Constants.RECORD_TYPES.MANUFACTURING_DATA.ordinal());
|
||||
return md.getAttribute("SerialNumber");
|
||||
}
|
||||
|
||||
public Date readDisplayTime() {
|
||||
return Utils.receiverTimeToDate(readSystemTime() + readDisplayTimeOffset());
|
||||
}
|
||||
|
||||
public long readSystemTime() {
|
||||
Log.d(TAG, "Reading system time...");
|
||||
writeCommand(Dex_Constants.READ_SYSTEM_TIME);
|
||||
byte[] readData = read(MIN_LEN).getData();
|
||||
return ByteBuffer.wrap(readData).order(ByteOrder.LITTLE_ENDIAN).getInt() & 0xffffffff;
|
||||
}
|
||||
|
||||
public int readDisplayTimeOffset() {
|
||||
Log.d(TAG, "Reading display time offset...");
|
||||
writeCommand(Dex_Constants.READ_DISPLAY_TIME_OFFSET);
|
||||
byte[] readData = read(MIN_LEN).getData();
|
||||
return ByteBuffer.wrap(readData).order(ByteOrder.LITTLE_ENDIAN).getInt() & 0xffffffff;
|
||||
}
|
||||
|
||||
private int readDataBasePageRange(int recordType) {
|
||||
ArrayList<Byte> payload = new ArrayList<Byte>();
|
||||
Log.d(TAG, "adding Payload");
|
||||
payload.add((byte) recordType);
|
||||
Log.d(TAG, "Sending write command");
|
||||
writeCommand(Dex_Constants.READ_DATABASE_PAGE_RANGE, payload);
|
||||
Log.d(TAG, "About to call getdata");
|
||||
byte[] readData = read(MIN_LEN).getData();
|
||||
Log.d(TAG, "Going to return");
|
||||
return ByteBuffer.wrap(readData).order(ByteOrder.LITTLE_ENDIAN).getInt(4);
|
||||
}
|
||||
|
||||
private <T> T readDataBasePage(int recordType, int page) {
|
||||
byte numOfPages = 1;
|
||||
if (page < 0){
|
||||
throw new IllegalArgumentException("Invalid page requested:" + page);
|
||||
}
|
||||
ArrayList<Byte> payload = new ArrayList<Byte>();
|
||||
payload.add((byte) recordType);
|
||||
byte[] pageInt = ByteBuffer.allocate(4).putInt(page).array();
|
||||
payload.add(pageInt[3]);
|
||||
payload.add(pageInt[2]);
|
||||
payload.add(pageInt[1]);
|
||||
payload.add(pageInt[0]);
|
||||
payload.add(numOfPages);
|
||||
writeCommand(Dex_Constants.READ_DATABASE_PAGES, payload);
|
||||
byte[] readData = read(2122).getData();
|
||||
return ParsePage(readData, recordType);
|
||||
}
|
||||
private byte[] readDataBasePageTest(int recordType, int page) {
|
||||
byte numOfPages = 1;
|
||||
if (page < 0){
|
||||
throw new IllegalArgumentException("Invalid page requested:" + page);
|
||||
}
|
||||
ArrayList<Byte> payload = new ArrayList<Byte>();
|
||||
payload.add((byte) recordType);
|
||||
byte[] pageInt = ByteBuffer.allocate(4).putInt(page).array();
|
||||
payload.add(pageInt[3]);
|
||||
payload.add(pageInt[2]);
|
||||
payload.add(pageInt[1]);
|
||||
payload.add(pageInt[0]);
|
||||
payload.add(numOfPages);
|
||||
return writeCommandTest(Dex_Constants.READ_DATABASE_PAGES, payload);
|
||||
}
|
||||
|
||||
private void writeCommand(int command, ArrayList<Byte> payload) {
|
||||
byte[] packet = new PacketBuilder(command, payload).compose();
|
||||
if (mSerialDevice != null) {
|
||||
try {
|
||||
// UsbInterface mDataInterface = mDevice.getInterface(1);
|
||||
// UsbEndpoint mWriteEndpoint = mDataInterface.getEndpoint(0);
|
||||
// mConnection.bulkTransfer(mWriteEndpoint, packet, packet.length, IO_TIMEOUT);
|
||||
mSerialDevice.getPorts().get(0).write(packet, IO_TIMEOUT);
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, "Unable to write to serial device.", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
private byte[] writeCommandTest(int command, ArrayList<Byte> payload) {
|
||||
byte[] packet = new PacketBuilder(command, payload).compose();
|
||||
return packet;
|
||||
}
|
||||
private void writeCommand(int command) {
|
||||
byte[] packet = new PacketBuilder(command).compose();
|
||||
if (mSerialDevice != null) {
|
||||
try {
|
||||
// UsbInterface mDataInterface = mDevice.getInterface(1);
|
||||
// UsbEndpoint mWriteEndpoint = mDataInterface.getEndpoint(0);
|
||||
// mConnection.bulkTransfer(mWriteEndpoint, packet, packet.length, IO_TIMEOUT);
|
||||
mSerialDevice.getPorts().get(0).write(packet, IO_TIMEOUT);
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, "Unable to write to serial device.", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private ReadPacket read(int numOfBytes) {
|
||||
byte[] readData = new byte[numOfBytes];
|
||||
int len = 0;
|
||||
try {
|
||||
// UsbInterface mDataInterface = mDevice.getInterface(1);
|
||||
// UsbEndpoint mReadEndpoint = mDataInterface.getEndpoint(1);
|
||||
// byte[] mReadBuffer;
|
||||
// mReadBuffer = new byte[16 * 1024];
|
||||
//
|
||||
// int readAmt = Math.min(readData.length, mReadBuffer.length);
|
||||
// synchronized (mReadBufferLock) {
|
||||
//
|
||||
//
|
||||
// Log.d(TAG, "Read about to call bulk transfer.");
|
||||
// if (len < 0) {
|
||||
// // This sucks: we get -1 on timeout, not 0 as preferred.
|
||||
// // We *should* use UsbRequest, except it has a bug/api oversight
|
||||
// // where there is no way to determine the number of bytes read
|
||||
// // in response :\ -- http://b.android.com/28023
|
||||
// if (IO_TIMEOUT == Integer.MAX_VALUE) {
|
||||
// // Hack: Special case "~infinite timeout" as an error.
|
||||
// len = -1;
|
||||
// }
|
||||
// len = 0;
|
||||
// }
|
||||
//
|
||||
//// System.arraycopy(mReadBuffer, 0, readData, 0, readAmt);
|
||||
// }
|
||||
// len = mConnection.bulkTransfer(mReadEndpoint, readData, readAmt, IO_TIMEOUT);
|
||||
|
||||
len = mSerialDevice.getPorts().get(0).read(readData, IO_TIMEOUT);
|
||||
|
||||
Log.d(TAG, "Read " + len + " byte(s) complete.");
|
||||
|
||||
// Add a 100ms delay for when multiple write/reads are occurring in series
|
||||
Thread.sleep(100);
|
||||
|
||||
// TODO: this debug code to print data of the read, should be removed after
|
||||
// finding the source of the reading issue
|
||||
String bytes = "";
|
||||
int readAmount = len;
|
||||
for (int i = 0; i < readAmount; i++) bytes += String.format("%02x", readData[i]) + " ";
|
||||
Log.d(TAG, "Read data: " + bytes);
|
||||
////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, "Unable to read from serial device.", e);
|
||||
}
|
||||
byte[] data = Arrays.copyOfRange(readData, 0, len);
|
||||
return new ReadPacket(data);
|
||||
}
|
||||
|
||||
private <T> T ParsePage(byte[] data, int recordType) {
|
||||
int HEADER_LEN = 28;
|
||||
PageHeader pageHeader=new PageHeader(data);
|
||||
int NUM_REC_OFFSET = 4;
|
||||
int numRec = data[NUM_REC_OFFSET];
|
||||
int rec_len;
|
||||
|
||||
switch (Dex_Constants.RECORD_TYPES.values()[recordType]) {
|
||||
case MANUFACTURING_DATA:
|
||||
GenericXMLRecord xmlRecord = new GenericXMLRecord(Arrays.copyOfRange(data, HEADER_LEN, data.length - 1));
|
||||
return (T) xmlRecord;
|
||||
case SENSOR_DATA:
|
||||
rec_len = 20;
|
||||
SensorRecord[] sensorRecords = new SensorRecord[numRec];
|
||||
for (int i = 0; i < numRec; i++) {
|
||||
int startIdx = HEADER_LEN + rec_len * i;
|
||||
sensorRecords[i] = new SensorRecord(Arrays.copyOfRange(data, startIdx, startIdx + rec_len - 1));
|
||||
}
|
||||
return (T) sensorRecords;
|
||||
case EGV_DATA:
|
||||
rec_len = 13;
|
||||
EGVRecord[] egvRecords = new EGVRecord[numRec];
|
||||
for (int i = 0; i < numRec; i++) {
|
||||
int startIdx = HEADER_LEN + rec_len * i;
|
||||
egvRecords[i] = new EGVRecord(Arrays.copyOfRange(data, startIdx, startIdx + rec_len - 1));
|
||||
}
|
||||
return (T) egvRecords;
|
||||
case METER_DATA:
|
||||
rec_len = 16;
|
||||
MeterRecord[] meterRecords = new MeterRecord[numRec];
|
||||
for (int i = 0; i < numRec; i++) {
|
||||
int startIdx = HEADER_LEN + rec_len * i;
|
||||
meterRecords[i] = new MeterRecord(Arrays.copyOfRange(data, startIdx, startIdx + rec_len - 1));
|
||||
}
|
||||
return (T) meterRecords;
|
||||
case CAL_SET:
|
||||
rec_len = 249;
|
||||
if (pageHeader.getRevision()<=2) {
|
||||
rec_len = 148;
|
||||
}
|
||||
CalRecord[] calRecords = new CalRecord[numRec];
|
||||
for (int i = 0; i < numRec; i++) {
|
||||
int startIdx = HEADER_LEN + rec_len * i;
|
||||
calRecords[i] = new CalRecord(Arrays.copyOfRange(data, startIdx, startIdx + rec_len - 1));
|
||||
}
|
||||
return (T) calRecords;
|
||||
default:
|
||||
// Throw error "Database record not supported"
|
||||
break;
|
||||
}
|
||||
|
||||
return (T) null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,305 @@
|
||||
package com.eveningoutpost.dexdrip.ImportedLibraries.dexcom;
|
||||
|
||||
import com.eveningoutpost.dexdrip.Models.UserError.Log;
|
||||
|
||||
import com.eveningoutpost.dexdrip.ImportedLibraries.dexcom.records.CalRecord;
|
||||
import com.eveningoutpost.dexdrip.ImportedLibraries.dexcom.records.EGVRecord;
|
||||
import com.eveningoutpost.dexdrip.ImportedLibraries.dexcom.records.GenericXMLRecord;
|
||||
import com.eveningoutpost.dexdrip.ImportedLibraries.dexcom.records.MeterRecord;
|
||||
import com.eveningoutpost.dexdrip.ImportedLibraries.dexcom.records.PageHeader;
|
||||
import com.eveningoutpost.dexdrip.ImportedLibraries.dexcom.records.SensorRecord;
|
||||
import com.eveningoutpost.dexdrip.Services.DexShareCollectionService;
|
||||
import com.eveningoutpost.dexdrip.ShareTest;
|
||||
|
||||
import org.w3c.dom.Element;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.ByteOrder;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
import rx.Observable;
|
||||
import rx.functions.Action1;
|
||||
|
||||
// This code and this particular library are from the NightScout android uploader
|
||||
// Check them out here: https://github.com/nightscout/android-uploader
|
||||
// Some of this code may have been modified for use in this project
|
||||
|
||||
public class ReadDataShare {
|
||||
byte[] accumulatedResponse;
|
||||
private ShareTest mShareTest;
|
||||
private DexShareCollectionService mCollectionService;
|
||||
|
||||
public ReadDataShare(ShareTest aShareTest){
|
||||
mShareTest = aShareTest;
|
||||
}
|
||||
public ReadDataShare(DexShareCollectionService collectionService){
|
||||
mCollectionService = collectionService;
|
||||
}
|
||||
|
||||
public void getRecentEGVs(final Action1<EGVRecord[]> recordListener) {
|
||||
final int recordType = Dex_Constants.RECORD_TYPES.EGV_DATA.ordinal();
|
||||
final Action1<byte[]> fullPageListener = new Action1<byte[]>() {
|
||||
@Override
|
||||
public void call(byte[] s) { ParsePage(read(0,s).getData(), recordType, recordListener); }
|
||||
};
|
||||
Action1<Integer> databasePageRangeCaller = new Action1<Integer>() {
|
||||
@Override
|
||||
public void call(Integer s) { readDataBasePage(recordType, s, fullPageListener); }
|
||||
};
|
||||
readDataBasePageRange(recordType, databasePageRangeCaller);
|
||||
}
|
||||
|
||||
public void getRecentMeterRecords(final Action1<MeterRecord[]> recordListener) {
|
||||
final int recordType = Dex_Constants.RECORD_TYPES.METER_DATA.ordinal();
|
||||
final Action1<byte[]> fullPageListener = new Action1<byte[]>() {
|
||||
@Override
|
||||
public void call(byte[] s) { ParsePage(read(0,s).getData(), recordType, recordListener); }
|
||||
};
|
||||
Action1<Integer> databasePageRangeCaller = new Action1<Integer>() {
|
||||
@Override
|
||||
public void call(Integer s) { readDataBasePage(recordType, s, fullPageListener); }
|
||||
};
|
||||
readDataBasePageRange(recordType, databasePageRangeCaller);
|
||||
}
|
||||
|
||||
public void getRecentCalRecords(final Action1<CalRecord[]> recordListener) {
|
||||
final int recordType = Dex_Constants.RECORD_TYPES.CAL_SET.ordinal();
|
||||
final Action1<byte[]> fullPageListener = new Action1<byte[]>() {
|
||||
@Override
|
||||
public void call(byte[] s) { ParsePage(read(0,s).getData(), recordType, recordListener); }
|
||||
};
|
||||
Action1<Integer> databasePageRangeCaller = new Action1<Integer>() {
|
||||
@Override
|
||||
public void call(Integer s) { readDataBasePage(recordType, s, fullPageListener); }
|
||||
};
|
||||
readDataBasePageRange(recordType, databasePageRangeCaller);
|
||||
}
|
||||
|
||||
|
||||
public void getRecentSensorRecords(final Action1<SensorRecord[]> recordListener) {
|
||||
final int recordType = Dex_Constants.RECORD_TYPES.SENSOR_DATA.ordinal();
|
||||
final Action1<byte[]> fullPageListener = new Action1<byte[]>() {
|
||||
@Override
|
||||
public void call(byte[] s) { ParsePage(read(0,s).getData(), recordType, recordListener); }
|
||||
};
|
||||
Action1<Integer> databasePageRangeCaller = new Action1<Integer>() {
|
||||
@Override
|
||||
public void call(Integer s) { readDataBasePage(recordType, s, fullPageListener); }
|
||||
};
|
||||
readDataBasePageRange(recordType, databasePageRangeCaller);
|
||||
}
|
||||
|
||||
public void getTimeSinceEGVRecord(final EGVRecord egvRecord, final Action1<Long> timeSinceEgvRecord) {
|
||||
Action1<Long> tempSystemTimeListener = new Action1<Long>() {
|
||||
@Override
|
||||
public void call(Long s) { Observable.just(s - egvRecord.getSystemTimeSeconds()).subscribe(timeSinceEgvRecord); }
|
||||
};
|
||||
readSystemTime(tempSystemTimeListener);
|
||||
}
|
||||
|
||||
public void ping(final Action1<Boolean> pingListener) {
|
||||
Action1<byte[]> pingReader = new Action1<byte[]>() {
|
||||
@Override
|
||||
public void call(byte[] s) { Observable.just(read(0, s).getCommand() == Dex_Constants.ACK).subscribe(pingListener); }
|
||||
};
|
||||
writeCommand(Dex_Constants.PING, pingReader);
|
||||
}
|
||||
|
||||
public void readBatteryLevel(final Action1<Integer> batteryLevelListener) {
|
||||
Action1<byte[]> batteryLevelReader = new Action1<byte[]>() {
|
||||
@Override //TODO: find out if this should be wrapped in read(s).getData();
|
||||
public void call(byte[] s) { Observable.just(ByteBuffer.wrap(s).order(ByteOrder.LITTLE_ENDIAN).getInt()).subscribe(batteryLevelListener); }
|
||||
};
|
||||
writeCommand(Dex_Constants.READ_BATTERY_LEVEL, batteryLevelReader);
|
||||
}
|
||||
|
||||
public void readSerialNumber(final Action1<String> serialNumberListener) {
|
||||
final Action1<byte[]> manufacturingDataListener = new Action1<byte[]>() {
|
||||
@Override
|
||||
public void call(byte[] s) {
|
||||
Element el = ParsePage(s, Dex_Constants.RECORD_TYPES.MANUFACTURING_DATA.ordinal());
|
||||
Observable.just(el.getAttribute("SerialNumber")).subscribe(serialNumberListener);
|
||||
}
|
||||
};
|
||||
readDataBasePage(Dex_Constants.RECORD_TYPES.MANUFACTURING_DATA.ordinal(), 0, manufacturingDataListener);
|
||||
}
|
||||
|
||||
public void readDisplayTime(final Action1<Date> displayTimeListener) {
|
||||
Action1<Long> tempSystemTimeListener = new Action1<Long>() {
|
||||
@Override
|
||||
public void call(Long s) {
|
||||
final long systemTime = s;
|
||||
Action1<Long> tempSystemTimeListener = new Action1<Long>() {
|
||||
@Override
|
||||
public void call(Long s) {
|
||||
Date dateDisplayTime = Utils.receiverTimeToDate(systemTime + s);
|
||||
Observable.just(dateDisplayTime).subscribe(displayTimeListener); }
|
||||
};
|
||||
readDisplayTimeOffset(tempSystemTimeListener);
|
||||
}
|
||||
};
|
||||
readSystemTime(tempSystemTimeListener);
|
||||
}
|
||||
|
||||
public void readSystemTime(final Action1<Long> systemTimeListener) {
|
||||
Action1<byte[]> systemTimeReader = new Action1<byte[]>() {
|
||||
@Override
|
||||
public void call(byte[] s) {
|
||||
Observable.just(Utils.receiverTimeToDate(ByteBuffer.wrap(read(0,s).getData()).order(ByteOrder.LITTLE_ENDIAN).getInt()).getTime()).subscribe(systemTimeListener);
|
||||
}
|
||||
};
|
||||
writeCommand(Dex_Constants.READ_SYSTEM_TIME, systemTimeReader);
|
||||
}
|
||||
|
||||
public void readDisplayTimeOffset(final Action1<Long> displayTimeOffsetListener) {
|
||||
Action1<byte[]> displayTimeOffsetReader = new Action1<byte[]>() {
|
||||
@Override
|
||||
public void call(byte[] s) { Observable.just((long) ByteBuffer.wrap(read(0,s).getData()).order(ByteOrder.LITTLE_ENDIAN).getInt()).subscribe(displayTimeOffsetListener); }
|
||||
};
|
||||
writeCommand(Dex_Constants.READ_DISPLAY_TIME_OFFSET, displayTimeOffsetReader);
|
||||
}
|
||||
|
||||
private void readDataBasePageRange(int recordType, final Action1<Integer> databasePageRangeCaller) {
|
||||
ArrayList<Byte> payload = new ArrayList<Byte>();
|
||||
payload.add((byte) recordType);
|
||||
final Action1<byte[]> databasePageRangeListener = new Action1<byte[]>() {
|
||||
@Override
|
||||
public void call(byte[] s) {
|
||||
Observable.just(ByteBuffer.wrap(new ReadPacket(s).getData()).order(ByteOrder.LITTLE_ENDIAN).getInt(4)).subscribe(databasePageRangeCaller);
|
||||
}
|
||||
};
|
||||
writeCommand(Dex_Constants.READ_DATABASE_PAGE_RANGE, payload, databasePageRangeListener);
|
||||
}
|
||||
|
||||
private <T> T readDataBasePage(final int recordType, int page, final Action1<byte[]> fullPageListener) {
|
||||
byte numOfPages = 1;
|
||||
if (page < 0){ throw new IllegalArgumentException("Invalid page requested:" + page); }
|
||||
ArrayList<Byte> payload = new ArrayList<Byte>();
|
||||
payload.add((byte) recordType);
|
||||
byte[] pageInt = ByteBuffer.allocate(4).putInt(page).array();
|
||||
payload.add(pageInt[3]);
|
||||
payload.add(pageInt[2]);
|
||||
payload.add(pageInt[1]);
|
||||
payload.add(pageInt[0]);
|
||||
payload.add(numOfPages);
|
||||
accumulatedResponse = null;
|
||||
final Action1<byte[]> databasePageReader = new Action1<byte[]>() {
|
||||
@Override
|
||||
public void call(byte[] s) {
|
||||
Log.d("ShareTest", "Database Page Reader received SIZE: " + s.length);
|
||||
byte[] temp = s;
|
||||
if (accumulatedResponse == null) {
|
||||
accumulatedResponse = s;
|
||||
} else {
|
||||
try {
|
||||
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
|
||||
outputStream.write(accumulatedResponse);
|
||||
outputStream.write(temp);
|
||||
accumulatedResponse = outputStream.toByteArray();
|
||||
Log.d("ShareTest", "Combined Response length: " + accumulatedResponse.length);
|
||||
} catch (Exception e) { e.printStackTrace(); }
|
||||
}
|
||||
if (temp.length < 20) { Observable.just(accumulatedResponse).subscribe(fullPageListener).unsubscribe(); }
|
||||
}
|
||||
};
|
||||
writeCommand(Dex_Constants.READ_DATABASE_PAGES, payload, databasePageReader);
|
||||
return null;
|
||||
}
|
||||
|
||||
private void writeCommand(int command, ArrayList<Byte> payload, Action1<byte[]> responseListener) {
|
||||
List<byte[]> packets = new PacketBuilder(command, payload).composeList();
|
||||
if(mShareTest != null) { mShareTest.writeCommand(packets, 0, responseListener); }
|
||||
else if (mCollectionService != null) { mCollectionService.writeCommand(packets, 0, responseListener); }
|
||||
}
|
||||
|
||||
private void writeCommand(int command, Action1<byte[]> responseListener) {
|
||||
List<byte[]> packets = new PacketBuilder(command).composeList();
|
||||
if(mShareTest != null) { mShareTest.writeCommand(packets, 0, responseListener); }
|
||||
else if (mCollectionService != null) { mCollectionService.writeCommand(packets, 0, responseListener); }
|
||||
}
|
||||
|
||||
private ReadPacket read(int numOfBytes, byte[] readPacket) {
|
||||
return new ReadPacket(Arrays.copyOfRange(readPacket, 0, readPacket.length));
|
||||
}
|
||||
|
||||
private <T> T ParsePage(byte[] data, int recordType) { return ParsePage(data, recordType, null); }
|
||||
private <T> T ParsePage(byte[] data, int recordType, Action1<T> parsedPageReceiver) {
|
||||
int HEADER_LEN = 28;
|
||||
PageHeader pageHeader=new PageHeader(data);
|
||||
int NUM_REC_OFFSET = 4;
|
||||
int numRec = data[NUM_REC_OFFSET];
|
||||
int rec_len;
|
||||
|
||||
switch (Dex_Constants.RECORD_TYPES.values()[recordType]) {
|
||||
case MANUFACTURING_DATA:
|
||||
GenericXMLRecord xmlRecord = new GenericXMLRecord(Arrays.copyOfRange(data, HEADER_LEN, data.length - 1));
|
||||
if(parsedPageReceiver != null) {
|
||||
Observable.just((T) xmlRecord).subscribe(parsedPageReceiver);
|
||||
} else {
|
||||
return (T) xmlRecord;
|
||||
}
|
||||
break;
|
||||
case SENSOR_DATA:
|
||||
rec_len = 20;
|
||||
SensorRecord[] sensorRecords = new SensorRecord[numRec];
|
||||
for (int i = 0; i < numRec; i++) {
|
||||
int startIdx = HEADER_LEN + rec_len * i;
|
||||
sensorRecords[i] = new SensorRecord(Arrays.copyOfRange(data, startIdx, startIdx + rec_len - 1));
|
||||
}
|
||||
if(parsedPageReceiver != null) {
|
||||
Observable.just((T) sensorRecords).subscribe(parsedPageReceiver);
|
||||
} else {
|
||||
return (T) sensorRecords;
|
||||
}
|
||||
break;
|
||||
case EGV_DATA:
|
||||
rec_len = 13;
|
||||
EGVRecord[] egvRecords = new EGVRecord[numRec];
|
||||
for (int i = 0; i < numRec; i++) {
|
||||
int startIdx = HEADER_LEN + rec_len * i;
|
||||
egvRecords[i] = new EGVRecord(Arrays.copyOfRange(data, startIdx, startIdx + rec_len - 1));
|
||||
}
|
||||
if(parsedPageReceiver != null) {
|
||||
Observable.just((T) egvRecords).subscribe(parsedPageReceiver);
|
||||
} else {
|
||||
return (T) egvRecords;
|
||||
}
|
||||
break;
|
||||
case METER_DATA:
|
||||
rec_len = 16;
|
||||
MeterRecord[] meterRecords = new MeterRecord[numRec];
|
||||
for (int i = 0; i < numRec; i++) {
|
||||
int startIdx = HEADER_LEN + rec_len * i;
|
||||
meterRecords[i] = new MeterRecord(Arrays.copyOfRange(data, startIdx, startIdx + rec_len - 1));
|
||||
}
|
||||
if(parsedPageReceiver != null) {
|
||||
Observable.just((T) meterRecords).subscribe(parsedPageReceiver);
|
||||
} else {
|
||||
return (T) meterRecords;
|
||||
}
|
||||
break;
|
||||
case CAL_SET:
|
||||
rec_len = 249;
|
||||
if (pageHeader.getRevision()<=2) { rec_len = 148; }
|
||||
CalRecord[] calRecords = new CalRecord[numRec];
|
||||
for (int i = 0; i < numRec; i++) {
|
||||
int startIdx = HEADER_LEN + rec_len * i;
|
||||
calRecords[i] = new CalRecord(Arrays.copyOfRange(data, startIdx, startIdx + rec_len - 1));
|
||||
}
|
||||
if(parsedPageReceiver != null) {
|
||||
Observable.just((T) calRecords).subscribe(parsedPageReceiver);
|
||||
} else {
|
||||
return (T) calRecords;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
Observable.just((T) null).subscribe(parsedPageReceiver);
|
||||
return (T) null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
package com.eveningoutpost.dexdrip.ImportedLibraries.dexcom;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
// This code and this particular library are from the NightScout android uploader
|
||||
// Check them out here: https://github.com/nightscout/android-uploader
|
||||
// Some of this code may have been modified for use in this project
|
||||
|
||||
|
||||
public class ReadPacket {
|
||||
private int command;
|
||||
private byte[] data;
|
||||
private byte[] crc_calc;
|
||||
private byte[] crc;
|
||||
private int OFFSET_CMD = 3;
|
||||
private int OFFSET_DATA = 4;
|
||||
private int CRC_LEN = 2;
|
||||
|
||||
public ReadPacket(byte[] readPacket) {
|
||||
this.command = readPacket[OFFSET_CMD];
|
||||
this.data = Arrays.copyOfRange(readPacket, OFFSET_DATA, readPacket.length - CRC_LEN);
|
||||
this.crc = Arrays.copyOfRange(readPacket, readPacket.length - CRC_LEN, readPacket.length);
|
||||
this.crc_calc=CRC16.calculate(readPacket, 0, readPacket.length - 2);
|
||||
if (!Arrays.equals(this.crc, this.crc_calc)) {
|
||||
throw new CRCFailRuntimeException("CRC check failed: " + Utils.bytesToHex(this.crc) + " vs " + Utils.bytesToHex(this.crc_calc));
|
||||
}
|
||||
}
|
||||
|
||||
public int getCommand() {
|
||||
return command;
|
||||
}
|
||||
|
||||
public byte[] getData() {
|
||||
return data;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,346 @@
|
||||
package com.eveningoutpost.dexdrip.ImportedLibraries.dexcom;
|
||||
|
||||
import android.app.IntentService;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.SharedPreferences;
|
||||
import android.hardware.usb.UsbDevice;
|
||||
import android.hardware.usb.UsbDeviceConnection;
|
||||
import android.hardware.usb.UsbManager;
|
||||
import android.os.PowerManager;
|
||||
import android.preference.PreferenceManager;
|
||||
|
||||
import com.eveningoutpost.dexdrip.Models.JoH;
|
||||
import com.eveningoutpost.dexdrip.Models.UserError.Log;
|
||||
|
||||
import com.eveningoutpost.dexdrip.ImportedLibraries.dexcom.records.CalRecord;
|
||||
import com.eveningoutpost.dexdrip.ImportedLibraries.dexcom.records.EGVRecord;
|
||||
import com.eveningoutpost.dexdrip.ImportedLibraries.dexcom.records.GlucoseDataSet;
|
||||
import com.eveningoutpost.dexdrip.ImportedLibraries.dexcom.records.MeterRecord;
|
||||
import com.eveningoutpost.dexdrip.ImportedLibraries.dexcom.records.SensorRecord;
|
||||
import com.eveningoutpost.dexdrip.ImportedLibraries.usbserial.driver.CdcAcmSerialDriver;
|
||||
import com.eveningoutpost.dexdrip.ImportedLibraries.usbserial.driver.ProbeTable;
|
||||
import com.eveningoutpost.dexdrip.ImportedLibraries.usbserial.driver.UsbSerialDriver;
|
||||
import com.eveningoutpost.dexdrip.ImportedLibraries.usbserial.driver.UsbSerialProber;
|
||||
import com.eveningoutpost.dexdrip.Models.Calibration;
|
||||
|
||||
import org.json.JSONArray;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
|
||||
// This code and this particular library are from the NightScout android uploader
|
||||
// Check them out here: https://github.com/nightscout/android-uploader
|
||||
// Some of this code may have been modified for use in this project
|
||||
|
||||
|
||||
/**
|
||||
* An {@link IntentService} subclass for handling asynchronous CGM Receiver downloads and cloud uploads
|
||||
* requests in a service on a separate handler thread.
|
||||
*/
|
||||
public class SyncingService extends IntentService {
|
||||
|
||||
// Action for intent
|
||||
private static final String ACTION_SYNC = "com.eveningoutpost.dexdrip.ImportedLibraries.dexcom.action.SYNC";
|
||||
private static final String ACTION_CALIBRATION_CHECKIN = "com.eveningoutpost.dexdrip.CalibrationCheckInActivity";
|
||||
|
||||
// Parameters for intent
|
||||
private static final String SYNC_PERIOD = "com.eveningoutpost.dexdrip.ImportedLibraries.dexcom.extra.SYNC_PERIOD";
|
||||
|
||||
// Response to broadcast to activity
|
||||
public static final String RESPONSE_SGV = "mySGV";
|
||||
public static final String RESPONSE_TREND = "myTrend";
|
||||
public static final String RESPONSE_TIMESTAMP = "myTimestamp";
|
||||
public static final String RESPONSE_NEXT_UPLOAD_TIME = "myUploadTime";
|
||||
public static final String RESPONSE_UPLOAD_STATUS = "myUploadStatus";
|
||||
public static final String RESPONSE_DISPLAY_TIME = "myDisplayTime";
|
||||
public static final String RESPONSE_JSON = "myJSON";
|
||||
public static final String RESPONSE_BAT = "myBatLvl";
|
||||
|
||||
private final String TAG = SyncingService.class.getSimpleName();
|
||||
private Context mContext;
|
||||
private UsbManager mUsbManager;
|
||||
private UsbSerialDriver mSerialDevice;
|
||||
private UsbDevice dexcom;
|
||||
private UsbDeviceConnection mConnection;
|
||||
|
||||
// Constants
|
||||
private final int TIME_SYNC_OFFSET = 10000;
|
||||
public static final int MIN_SYNC_PAGES = 2;
|
||||
public static final int GAP_SYNC_PAGES = 20;
|
||||
|
||||
|
||||
/**
|
||||
* Starts this service to perform action Single Sync with the given parameters. If
|
||||
* the service is already performing a task this action will be queued.
|
||||
*
|
||||
* @see IntentService
|
||||
*/
|
||||
public static void startActionSingleSync(Context context, int numOfPages) {
|
||||
Intent intent = new Intent(context, SyncingService.class);
|
||||
intent.setAction(ACTION_SYNC);
|
||||
intent.putExtra(SYNC_PERIOD, numOfPages);
|
||||
context.startService(intent);
|
||||
}
|
||||
public static void startActionCalibrationCheckin(Context context) {
|
||||
Intent intent = new Intent(context, SyncingService.class);
|
||||
intent.setAction(ACTION_CALIBRATION_CHECKIN);
|
||||
context.startService(intent);
|
||||
}
|
||||
public SyncingService() {
|
||||
super("SyncingService");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onHandleIntent(Intent intent) {
|
||||
mContext = getApplicationContext();
|
||||
if (intent != null) {
|
||||
final String action = intent.getAction();
|
||||
if (ACTION_SYNC.equals(action)) {
|
||||
final int param1 = intent.getIntExtra(SYNC_PERIOD, 1);
|
||||
handleActionSync(param1);
|
||||
} else if (ACTION_CALIBRATION_CHECKIN.equals(action)) {
|
||||
Log.i("CALIBRATION-CHECK-IN: ", "Beginning check in process");
|
||||
performCalibrationCheckin();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle action Sync in the provided background thread with the provided
|
||||
* parameters.
|
||||
*/
|
||||
private void performCalibrationCheckin(){
|
||||
PowerManager pm = (PowerManager) getApplicationContext().getSystemService(Context.POWER_SERVICE);
|
||||
PowerManager.WakeLock wl = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "NSDownload");
|
||||
wl.acquire();
|
||||
try {
|
||||
Log.i("CALIBRATION-CHECK-IN: ", "Wake Lock Acquired");
|
||||
if (acquireSerialDevice()) {
|
||||
try {
|
||||
ReadData readData = new ReadData(mSerialDevice, mConnection, dexcom);
|
||||
|
||||
// ReadData readData = new ReadData(mSerialDevice);
|
||||
CalRecord[] calRecords = readData.getRecentCalRecords();
|
||||
Log.i("CALIBRATION-CHECK-IN: ", "Found " + calRecords.length + " Records!");
|
||||
save_most_recent_cal_record(calRecords);
|
||||
|
||||
} catch (Exception e) {
|
||||
Log.wtf("Unhandled exception caught", e);
|
||||
} finally {
|
||||
// Close serial
|
||||
try {
|
||||
mSerialDevice.getPorts().get(0).close();
|
||||
} catch (IOException e) {
|
||||
Log.e(TAG, "Unable to close", e);
|
||||
}
|
||||
|
||||
}
|
||||
} else {
|
||||
Log.w("CALIBRATION-CHECK-IN: ", "Failed to acquire serial device");
|
||||
}
|
||||
} finally {
|
||||
JoH.releaseWakeLock(wl);
|
||||
}
|
||||
}
|
||||
|
||||
private void handleActionSync(int numOfPages) {
|
||||
boolean broadcastSent = false;
|
||||
|
||||
PowerManager pm = (PowerManager) getApplicationContext().getSystemService(Context.POWER_SERVICE);
|
||||
PowerManager.WakeLock wl = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "NSDownload");
|
||||
wl.acquire();
|
||||
|
||||
try {
|
||||
sync(numOfPages);
|
||||
} finally {
|
||||
wl.release();
|
||||
}
|
||||
}
|
||||
|
||||
private void sync(int numOfPages) {
|
||||
boolean broadcastSent;
|
||||
if (acquireSerialDevice()) {
|
||||
try {
|
||||
|
||||
ReadData readData = new ReadData(mSerialDevice);
|
||||
// TODO: need to check if numOfPages if valid on ReadData side
|
||||
EGVRecord[] recentRecords = readData.getRecentEGVsPages(numOfPages);
|
||||
MeterRecord[] meterRecords = readData.getRecentMeterRecords();
|
||||
// TODO: need to check if numOfPages if valid on ReadData side
|
||||
SensorRecord[] sensorRecords = readData.getRecentSensorRecords(numOfPages);
|
||||
GlucoseDataSet[] glucoseDataSets = Utils.mergeGlucoseDataRecords(recentRecords, sensorRecords);
|
||||
|
||||
// FIXME: This is a workaround for the new Dexcom AP which seems to have a new format
|
||||
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(mContext);
|
||||
CalRecord[] calRecords = new CalRecord[1];
|
||||
if (prefs.getBoolean("cloud_cal_data", false)) {
|
||||
calRecords = readData.getRecentCalRecords();
|
||||
}
|
||||
|
||||
long timeSinceLastRecord = readData.getTimeSinceEGVRecord(recentRecords[recentRecords.length - 1]);
|
||||
// TODO: determine if the logic here is correct. I suspect it assumes the last record was less than 5
|
||||
// minutes ago. If a reading is skipped and the device is plugged in then nextUploadTime will be
|
||||
// set to a negative number. This situation will eventually correct itself.
|
||||
long nextUploadTime = (1000 * 60 * 5) - (timeSinceLastRecord * (1000));
|
||||
long displayTime = readData.readDisplayTime().getTime();
|
||||
// FIXME: Device seems to flake out on battery level reads. Removing for now.
|
||||
// int batLevel = readData.readBatteryLevel();
|
||||
int batLevel = 100;
|
||||
|
||||
// convert into json for d3 plot
|
||||
JSONArray array = new JSONArray();
|
||||
for (int i = 0; i < recentRecords.length; i++) array.put(recentRecords[i].toJSON());
|
||||
|
||||
EGVRecord recentEGV = recentRecords[recentRecords.length - 1];
|
||||
// broadcastSGVToUI(recentEGV, uploadStatus, nextUploadTime + TIME_SYNC_OFFSET,
|
||||
// displayTime, array ,batLevel);
|
||||
broadcastSent=true;
|
||||
} catch (ArrayIndexOutOfBoundsException e) {
|
||||
Log.wtf("Unable to read from the dexcom, maybe it will work next time", e);
|
||||
} catch (NegativeArraySizeException e) {
|
||||
Log.wtf("Negative array exception from receiver", e);
|
||||
} catch (IndexOutOfBoundsException e) {
|
||||
Log.wtf("IndexOutOfBounds exception from receiver", e);
|
||||
} catch (CRCFailRuntimeException e){
|
||||
// FIXME: may consider localizing this catch at a lower level (like ReadData) so that
|
||||
// if the CRC check fails on one type of record we can capture the values if it
|
||||
// doesn't fail on other types of records. This means we'd need to broadcast back
|
||||
// partial results to the UI. Adding it to a lower level could make the ReadData class
|
||||
// more difficult to maintain - needs discussion.
|
||||
Log.wtf("CRC failed", e);
|
||||
} catch (Exception e) {
|
||||
Log.wtf("Unhandled exception caught", e);
|
||||
} finally {
|
||||
// Close serial
|
||||
try {
|
||||
mSerialDevice.getPorts().get(0).close();
|
||||
} catch (IOException e) {
|
||||
|
||||
Log.e(TAG, "Unable to close", e);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
// if (!broadcastSent) broadcastSGVToUI();
|
||||
}
|
||||
|
||||
private void save_most_recent_cal_record(CalRecord[] calRecords) {
|
||||
int size = calRecords.length;
|
||||
Calibration.create(calRecords,getApplicationContext(), false, 0);
|
||||
}
|
||||
|
||||
private boolean acquireSerialDevice() {
|
||||
UsbDevice found_device = findDexcom();
|
||||
|
||||
if (mUsbManager == null) {
|
||||
Log.w("CALIBRATION-CHECK-IN: ", "USB manager is null");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (dexcom == null) {
|
||||
Log.e(TAG, "dex device == null");
|
||||
return false;
|
||||
}
|
||||
|
||||
if( mUsbManager.hasPermission(dexcom)) { // the system is allowing us to poke around this device
|
||||
|
||||
ProbeTable customTable = new ProbeTable(); // From the USB library...
|
||||
customTable.addProduct(0x22A3, 0x0047, CdcAcmSerialDriver.class); // ...Specify the Vendor ID and Product ID
|
||||
|
||||
UsbSerialProber prober = new UsbSerialProber(customTable); // Probe the device with the custom values
|
||||
List<UsbSerialDriver> drivers = prober.findAllDrivers(mUsbManager); // let's go through the list
|
||||
Iterator<UsbSerialDriver> foo = drivers.iterator(); // Invalid Return code
|
||||
while (foo.hasNext()) { // let's loop through
|
||||
UsbSerialDriver driver = foo.next(); // set fooDriver to the next available driver
|
||||
if (driver != null) {
|
||||
UsbDeviceConnection connection = mUsbManager.openDevice(driver.getDevice());
|
||||
if (connection != null) {
|
||||
mSerialDevice = driver;
|
||||
|
||||
mConnection = connection;
|
||||
Log.i("CALIBRATION-CHECK-IN: ", "CONNECTEDDDD!!");
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
Log.w("CALIBRATION-CHECK-IN: ", "Driver was no good");
|
||||
}
|
||||
}
|
||||
Log.w("CALIBRATION-CHECK-IN: ", "No usable drivers found");
|
||||
} else {
|
||||
Log.w("CALIBRATION-CHECK-IN: ", "You dont have permissions for that dexcom!!");
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static public boolean isG4Connected(Context c){
|
||||
UsbManager manager = (UsbManager) c.getSystemService(Context.USB_SERVICE);
|
||||
HashMap<String, UsbDevice> deviceList = manager.getDeviceList();
|
||||
Log.i("USB DEVICES = ", deviceList.toString());
|
||||
Iterator<UsbDevice> deviceIterator = deviceList.values().iterator();
|
||||
Log.i("USB DEVICES = ", String.valueOf(deviceList.size()));
|
||||
|
||||
while(deviceIterator.hasNext()){
|
||||
UsbDevice device = deviceIterator.next();
|
||||
if (device.getVendorId() == 8867 && device.getProductId() == 71
|
||||
&& device.getDeviceClass() == 2 && device.getDeviceSubclass() ==0
|
||||
&& device.getDeviceProtocol() == 0){
|
||||
Log.i("CALIBRATION-CHECK-IN: ", "Dexcom Found!");
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public UsbDevice findDexcom() {
|
||||
Log.i("CALIBRATION-CHECK-IN: ", "Searching for dexcom");
|
||||
mUsbManager = (UsbManager) getApplicationContext().getSystemService(Context.USB_SERVICE);
|
||||
Log.i("USB MANAGER = ", mUsbManager.toString());
|
||||
HashMap<String, UsbDevice> deviceList = mUsbManager.getDeviceList();
|
||||
Log.i("USB DEVICES = ", deviceList.toString());
|
||||
Iterator<UsbDevice> deviceIterator = deviceList.values().iterator();
|
||||
Log.i("USB DEVICES = ", String.valueOf(deviceList.size()));
|
||||
|
||||
while(deviceIterator.hasNext()){
|
||||
UsbDevice device = deviceIterator.next();
|
||||
if (device.getVendorId() == 8867 && device.getProductId() == 71
|
||||
&& device.getDeviceClass() == 2 && device.getDeviceSubclass() ==0
|
||||
&& device.getDeviceProtocol() == 0){
|
||||
dexcom = device;
|
||||
Log.i("CALIBRATION-CHECK-IN: ", "Dexcom Found!");
|
||||
return device;
|
||||
} else {
|
||||
Log.w("CALIBRATION-CHECK-IN: ", "that was not a dexcom (I dont think)");
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private void broadcastSGVToUI(EGVRecord egvRecord, boolean uploadStatus,
|
||||
long nextUploadTime, long displayTime,
|
||||
JSONArray json, int batLvl) {
|
||||
Log.d(TAG, "Current EGV: " + egvRecord.getBGValue());
|
||||
Intent broadcastIntent = new Intent();
|
||||
// broadcastIntent.setAction(MainActivity.CGMStatusReceiver.PROCESS_RESPONSE);
|
||||
broadcastIntent.addCategory(Intent.CATEGORY_DEFAULT);
|
||||
broadcastIntent.putExtra(RESPONSE_SGV, egvRecord.getBGValue());
|
||||
broadcastIntent.putExtra(RESPONSE_TREND, egvRecord.getTrend().getID());
|
||||
broadcastIntent.putExtra(RESPONSE_TIMESTAMP, egvRecord.getDisplayTime().getTime());
|
||||
broadcastIntent.putExtra(RESPONSE_NEXT_UPLOAD_TIME, nextUploadTime);
|
||||
broadcastIntent.putExtra(RESPONSE_UPLOAD_STATUS, uploadStatus);
|
||||
broadcastIntent.putExtra(RESPONSE_DISPLAY_TIME, displayTime);
|
||||
if (json!=null)
|
||||
broadcastIntent.putExtra(RESPONSE_JSON, json.toString());
|
||||
broadcastIntent.putExtra(RESPONSE_BAT, batLvl);
|
||||
sendBroadcast(broadcastIntent);
|
||||
}
|
||||
|
||||
private void broadcastSGVToUI() {
|
||||
EGVRecord record=new EGVRecord(-1, Dex_Constants.TREND_ARROW_VALUES.NONE,new Date(),new Date());
|
||||
broadcastSGVToUI(record,false, (long) (1000 * 60 * 5) + TIME_SYNC_OFFSET, new Date().getTime(), null, 0);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
package com.eveningoutpost.dexdrip.ImportedLibraries.dexcom;
|
||||
|
||||
import com.eveningoutpost.dexdrip.ImportedLibraries.dexcom.records.EGVRecord;
|
||||
import com.eveningoutpost.dexdrip.ImportedLibraries.dexcom.records.GlucoseDataSet;
|
||||
import com.eveningoutpost.dexdrip.ImportedLibraries.dexcom.records.SensorRecord;
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.TimeZone;
|
||||
|
||||
// This code and this particular library are from the NightScout android uploader
|
||||
// Check them out here: https://github.com/nightscout/android-uploader
|
||||
// Some of this code may have been modified for use in this project
|
||||
|
||||
public class Utils {
|
||||
|
||||
public static Date receiverTimeToDate(long delta) {
|
||||
int currentTZOffset = TimeZone.getDefault().getRawOffset();
|
||||
long epochMS = 1230768000000L; // Jan 01, 2009 00:00 in UTC
|
||||
long milliseconds = epochMS - currentTZOffset;
|
||||
long timeAdd = milliseconds + (1000L * delta);
|
||||
TimeZone tz = TimeZone.getDefault();
|
||||
if (tz.inDaylightTime(new Date())) timeAdd = timeAdd - (1000 * 60 * 60);
|
||||
return new Date(timeAdd);
|
||||
}
|
||||
|
||||
public static String getTimeString(long timeDeltaMS) {
|
||||
long minutes = (timeDeltaMS / 1000) / 60;
|
||||
long hours = minutes / 60;
|
||||
long days = hours / 24;
|
||||
long weeks = days / 7;
|
||||
minutes= minutes - hours * 60;
|
||||
hours = hours - days * 24;
|
||||
days= days - weeks * 7;
|
||||
|
||||
String timeAgoString = "";
|
||||
if (weeks > 0) {
|
||||
timeAgoString += weeks + " weeks ";
|
||||
}
|
||||
if (days > 0) {
|
||||
timeAgoString += days + " days ";
|
||||
}
|
||||
if (hours > 0) {
|
||||
timeAgoString += hours + " hours ";
|
||||
}
|
||||
if (minutes >= 0) {
|
||||
timeAgoString += minutes + " min ";
|
||||
}
|
||||
|
||||
return (timeAgoString.equals("") ? "--" : timeAgoString + "ago");
|
||||
}
|
||||
|
||||
public static GlucoseDataSet[] mergeGlucoseDataRecords(EGVRecord[] egvRecords,
|
||||
SensorRecord[] sensorRecords) {
|
||||
int egvLength = egvRecords.length;
|
||||
int sensorLength = sensorRecords.length;
|
||||
int smallerLength = egvLength < sensorLength ? egvLength : sensorLength;
|
||||
GlucoseDataSet[] glucoseDataSets = new GlucoseDataSet[smallerLength];
|
||||
for (int i = 1; i <= smallerLength; i++) {
|
||||
glucoseDataSets[smallerLength - i] = new GlucoseDataSet(egvRecords[egvLength - i], sensorRecords[sensorLength - i]);
|
||||
}
|
||||
return glucoseDataSets;
|
||||
}
|
||||
|
||||
public static String bytesToHex(byte[] bytes) {
|
||||
char[] hexArray = "0123456789ABCDEF".toCharArray();
|
||||
char[] hexChars = new char[bytes.length * 3];
|
||||
for ( int j = 0; j < bytes.length; j++ ) {
|
||||
int v = bytes[j] & 0xFF;
|
||||
hexChars[j * 3] = hexArray[v >>> 4];
|
||||
hexChars[j * 3 + 1] = hexArray[v & 0x0F];
|
||||
hexChars[j * 3 + 2] = " ".toCharArray()[0];
|
||||
}
|
||||
return new String(hexChars);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
package com.eveningoutpost.dexdrip.ImportedLibraries.dexcom.records;
|
||||
|
||||
import com.eveningoutpost.dexdrip.Models.UserError.Log;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.ByteOrder;
|
||||
|
||||
// This code and this particular library are from the NightScout android uploader
|
||||
// Check them out here: https://github.com/nightscout/android-uploader
|
||||
// Some of this code may have been modified for use in this project
|
||||
|
||||
public class CalRecord extends GenericTimestampRecord {
|
||||
private static final String TAG = CalRecord.class.getSimpleName();
|
||||
private double slope;
|
||||
private double intercept;
|
||||
private double scale;
|
||||
private int[] unk = new int[3];
|
||||
private double decay;
|
||||
private int numRecords;
|
||||
private CalSubrecord[] calSubrecords = new CalSubrecord[12];
|
||||
private int SUB_LEN = 17;
|
||||
|
||||
public CalRecord(byte[] packet) {
|
||||
super(packet);
|
||||
slope = ByteBuffer.wrap(packet).order(ByteOrder.LITTLE_ENDIAN).getDouble(8);
|
||||
intercept = ByteBuffer.wrap(packet).order(ByteOrder.LITTLE_ENDIAN).getDouble(16);
|
||||
scale = ByteBuffer.wrap(packet).order(ByteOrder.LITTLE_ENDIAN).getDouble(24);
|
||||
unk[0] = packet[32];
|
||||
unk[1] = packet[33];
|
||||
unk[2] = packet[34];
|
||||
decay = ByteBuffer.wrap(packet).order(ByteOrder.LITTLE_ENDIAN).getDouble(35);
|
||||
numRecords = packet[43];
|
||||
long displayTimeOffset = (getDisplayTime().getTime() - getSystemTime().getTime()) / (1000);
|
||||
int start = 44;
|
||||
for (int i = 0; i < numRecords; i++) {
|
||||
Log.d("CalDebug","Loop #"+i);
|
||||
byte[] temp = new byte[SUB_LEN];
|
||||
System.arraycopy(packet, start, temp, 0, temp.length);
|
||||
calSubrecords[i] = new CalSubrecord(temp, displayTimeOffset);
|
||||
start += SUB_LEN;
|
||||
}
|
||||
|
||||
Log.d("ShareTest", "slope: " + slope + " intercept: " + intercept);
|
||||
}
|
||||
|
||||
public double getSlope() {
|
||||
return slope;
|
||||
}
|
||||
|
||||
public double getIntercept() {
|
||||
return intercept;
|
||||
}
|
||||
|
||||
public double getScale() {
|
||||
return scale;
|
||||
}
|
||||
|
||||
public int[] getUnk() {
|
||||
return unk;
|
||||
}
|
||||
|
||||
public double getDecay() {
|
||||
return decay;
|
||||
}
|
||||
|
||||
public int getNumRecords() {
|
||||
return numRecords;
|
||||
}
|
||||
|
||||
public CalSubrecord[] getCalSubrecords() {
|
||||
return calSubrecords;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
package com.eveningoutpost.dexdrip.ImportedLibraries.dexcom.records;
|
||||
|
||||
import com.eveningoutpost.dexdrip.Models.UserError.Log;
|
||||
|
||||
import com.eveningoutpost.dexdrip.ImportedLibraries.dexcom.Utils;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.ByteOrder;
|
||||
import java.util.Date;
|
||||
|
||||
// This code and this particular library are from the NightScout android uploader
|
||||
// Check them out here: https://github.com/nightscout/android-uploader
|
||||
// Some of this code may have been modified for use in this project
|
||||
|
||||
public class CalSubrecord {
|
||||
private static final String TAG = CalSubrecord.class.getSimpleName();
|
||||
private Date dateEntered;
|
||||
private int calBGL;
|
||||
private int calRaw;
|
||||
private Date dateApplied;
|
||||
private byte unk;
|
||||
|
||||
public CalSubrecord(byte[] packet, long displayTimeOffset) {
|
||||
int delta = ByteBuffer.wrap(packet).order(ByteOrder.LITTLE_ENDIAN).getInt();
|
||||
dateEntered = Utils.receiverTimeToDate(delta + displayTimeOffset);
|
||||
calBGL = ByteBuffer.wrap(packet).order(ByteOrder.LITTLE_ENDIAN).getInt(4);
|
||||
calRaw = ByteBuffer.wrap(packet).order(ByteOrder.LITTLE_ENDIAN).getInt(8);
|
||||
delta = ByteBuffer.wrap(packet).order(ByteOrder.LITTLE_ENDIAN).getInt(12);
|
||||
dateApplied = Utils.receiverTimeToDate(delta + displayTimeOffset);
|
||||
unk = packet[16];
|
||||
}
|
||||
|
||||
public Date getDateEntered() {
|
||||
return dateEntered;
|
||||
}
|
||||
|
||||
public int getCalBGL() {
|
||||
return calBGL;
|
||||
}
|
||||
|
||||
public int getCalRaw() {
|
||||
return calRaw;
|
||||
}
|
||||
|
||||
public Date getDateApplied() {
|
||||
return dateApplied;
|
||||
}
|
||||
|
||||
public byte getUnk() {
|
||||
return unk;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
package com.eveningoutpost.dexdrip.ImportedLibraries.dexcom.records;
|
||||
|
||||
import com.eveningoutpost.dexdrip.ImportedLibraries.dexcom.Dex_Constants;
|
||||
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.ByteOrder;
|
||||
import java.util.Date;
|
||||
|
||||
// This code and this particular library are from the NightScout android uploader
|
||||
// Check them out here: https://github.com/nightscout/android-uploader
|
||||
// Some of this code may have been modified for use in this project
|
||||
|
||||
public class EGVRecord extends GenericTimestampRecord {
|
||||
|
||||
private int bGValue;
|
||||
private int noise;
|
||||
private Dex_Constants.TREND_ARROW_VALUES trend;
|
||||
|
||||
public EGVRecord(byte[] packet) {
|
||||
// system_time (UInt), display_time (UInt), glucose (UShort), trend_arrow (Byte), crc (UShort))
|
||||
super(packet);
|
||||
bGValue = ByteBuffer.wrap(packet).order(ByteOrder.LITTLE_ENDIAN).getShort(8) & Dex_Constants.EGV_VALUE_MASK;
|
||||
byte trendAndNoise = ByteBuffer.wrap(packet).order(ByteOrder.LITTLE_ENDIAN).get(10);
|
||||
int trendValue = trendAndNoise & Dex_Constants.EGV_TREND_ARROW_MASK;
|
||||
byte noiseValue = (byte) ((trendAndNoise & Dex_Constants.EGV_NOISE_MASK) >> 4);
|
||||
trend = Dex_Constants.TREND_ARROW_VALUES.values()[trendValue];
|
||||
noise = noiseValue;
|
||||
}
|
||||
|
||||
public EGVRecord(int bGValue, Dex_Constants.TREND_ARROW_VALUES trend, Date displayTime, Date systemTime){
|
||||
super(displayTime, systemTime);
|
||||
this.bGValue=bGValue;
|
||||
this.trend=trend;
|
||||
}
|
||||
|
||||
public String noiseValue() { return String.valueOf(noise); }
|
||||
public int getBGValue() {
|
||||
return bGValue;
|
||||
}
|
||||
|
||||
public Dex_Constants.TREND_ARROW_VALUES getTrend() {
|
||||
return trend;
|
||||
}
|
||||
|
||||
public JSONObject toJSON() {
|
||||
JSONObject obj = new JSONObject();
|
||||
try {
|
||||
obj.put("sgv", getBGValue());
|
||||
obj.put("date", getDisplayTimeSeconds());
|
||||
} catch (JSONException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return obj;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
package com.eveningoutpost.dexdrip.ImportedLibraries.dexcom.records;
|
||||
|
||||
import com.eveningoutpost.dexdrip.ImportedLibraries.dexcom.Utils;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.ByteOrder;
|
||||
import java.util.Date;
|
||||
|
||||
// This code and this particular library are from the NightScout android uploader
|
||||
// Check them out here: https://github.com/nightscout/android-uploader
|
||||
// Some of this code may have been modified for use in this project
|
||||
|
||||
public class GenericTimestampRecord {
|
||||
|
||||
protected final int OFFSET_SYS_TIME = 0;
|
||||
protected final int OFFSET_DISPLAY_TIME = 4;
|
||||
protected Date systemTime;
|
||||
protected int systemTimeSeconds;
|
||||
protected Date displayTime;
|
||||
|
||||
public GenericTimestampRecord(byte[] packet) {
|
||||
systemTimeSeconds = ByteBuffer.wrap(packet).order(ByteOrder.LITTLE_ENDIAN).getInt(OFFSET_SYS_TIME);
|
||||
systemTime = Utils.receiverTimeToDate(systemTimeSeconds);
|
||||
int dt = ByteBuffer.wrap(packet).order(ByteOrder.LITTLE_ENDIAN).getInt(OFFSET_DISPLAY_TIME);
|
||||
displayTime = Utils.receiverTimeToDate(dt);
|
||||
}
|
||||
|
||||
public GenericTimestampRecord(Date displayTime, Date systemTime){
|
||||
this.displayTime=displayTime;
|
||||
this.systemTime=systemTime;
|
||||
}
|
||||
|
||||
public Date getSystemTime() {
|
||||
return systemTime;
|
||||
}
|
||||
|
||||
public int getSystemTimeSeconds() {
|
||||
return systemTimeSeconds;
|
||||
}
|
||||
|
||||
public Date getDisplayTime() {
|
||||
return displayTime;
|
||||
}
|
||||
public long getDisplayTimeSeconds() {
|
||||
return displayTime.getTime();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
package com.eveningoutpost.dexdrip.ImportedLibraries.dexcom.records;
|
||||
|
||||
import com.eveningoutpost.dexdrip.Models.UserError.Log;
|
||||
|
||||
import org.w3c.dom.Document;
|
||||
import org.w3c.dom.Element;
|
||||
import org.xml.sax.InputSource;
|
||||
|
||||
import javax.xml.parsers.DocumentBuilder;
|
||||
import javax.xml.parsers.DocumentBuilderFactory;
|
||||
import java.io.Serializable;
|
||||
import java.io.StringReader;
|
||||
import java.util.Arrays;
|
||||
|
||||
// This code and this particular library are from the NightScout android uploader
|
||||
// Check them out here: https://github.com/nightscout/android-uploader
|
||||
// Some of this code may have been modified for use in this project
|
||||
|
||||
public class GenericXMLRecord extends GenericTimestampRecord {
|
||||
int XML_START = 8;
|
||||
int XML_END = 241;
|
||||
|
||||
private final String TAG = GenericXMLRecord.class.getSimpleName();
|
||||
|
||||
private Element xmlElement;
|
||||
|
||||
public GenericXMLRecord(byte[] packet) {
|
||||
super(packet);
|
||||
Document document;
|
||||
// TODO: it would be best if we could just remove /x00 characters and read till end
|
||||
String xml = new String(Arrays.copyOfRange(packet, XML_START, XML_END));
|
||||
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
|
||||
DocumentBuilder builder;
|
||||
try
|
||||
{
|
||||
builder = factory.newDocumentBuilder();
|
||||
document = builder.parse(new InputSource(new StringReader(xml)));
|
||||
xmlElement = document.getDocumentElement();
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, "Unable to build xml element", e);
|
||||
}
|
||||
}
|
||||
|
||||
// example: String sn = getXmlElement().getAttribute("SerialNumber");
|
||||
public Element getXmlElement() {
|
||||
return xmlElement;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
package com.eveningoutpost.dexdrip.ImportedLibraries.dexcom.records;
|
||||
|
||||
import com.eveningoutpost.dexdrip.ImportedLibraries.dexcom.Dex_Constants;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
// This code and this particular library are from the NightScout android uploader
|
||||
// Check them out here: https://github.com/nightscout/android-uploader
|
||||
// Some of this code may have been modified for use in this project
|
||||
|
||||
public class GlucoseDataSet {
|
||||
|
||||
private Date systemTime;
|
||||
private Date displayTime;
|
||||
private int bGValue;
|
||||
private Dex_Constants.TREND_ARROW_VALUES trend;
|
||||
private long unfiltered;
|
||||
private long filtered;
|
||||
private int rssi;
|
||||
|
||||
public GlucoseDataSet(EGVRecord egvRecord, SensorRecord sensorRecord) {
|
||||
// TODO check times match between record
|
||||
systemTime = egvRecord.getSystemTime();
|
||||
displayTime = egvRecord.getDisplayTime();
|
||||
bGValue = egvRecord.getBGValue();
|
||||
trend = egvRecord.getTrend();
|
||||
unfiltered = sensorRecord.getUnfiltered();
|
||||
filtered = sensorRecord.getFiltered();
|
||||
rssi = sensorRecord.getRSSI();
|
||||
}
|
||||
|
||||
public Date getSystemTime() {
|
||||
return systemTime;
|
||||
}
|
||||
|
||||
public Date getDisplayTime() {
|
||||
return displayTime;
|
||||
}
|
||||
|
||||
public int getBGValue() {
|
||||
return bGValue;
|
||||
}
|
||||
|
||||
public Dex_Constants.TREND_ARROW_VALUES getTrend() {
|
||||
return trend;
|
||||
}
|
||||
|
||||
public String getTrendSymbol() {
|
||||
return trend.Symbol();
|
||||
}
|
||||
|
||||
public long getUnfiltered() {
|
||||
return unfiltered;
|
||||
}
|
||||
|
||||
public long getFiltered() {
|
||||
return filtered;
|
||||
}
|
||||
|
||||
public int getRssi() {
|
||||
return rssi;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
package com.eveningoutpost.dexdrip.ImportedLibraries.dexcom.records;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.ByteOrder;
|
||||
|
||||
// This code and this particular library are from the NightScout android uploader
|
||||
// Check them out here: https://github.com/nightscout/android-uploader
|
||||
// Some of this code may have been modified for use in this project
|
||||
|
||||
public class MeterRecord extends GenericTimestampRecord {
|
||||
|
||||
private int meterBG;
|
||||
private int meterTime;
|
||||
|
||||
public MeterRecord(byte[] packet) {
|
||||
super(packet);
|
||||
meterBG = ByteBuffer.wrap(packet).order(ByteOrder.LITTLE_ENDIAN).getShort(8);
|
||||
meterTime = ByteBuffer.wrap(packet).order(ByteOrder.LITTLE_ENDIAN).getInt(10);
|
||||
}
|
||||
|
||||
public int getMeterBG() {
|
||||
return meterBG;
|
||||
}
|
||||
|
||||
public int getMeterTime() {
|
||||
return meterTime;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,89 @@
|
||||
package com.eveningoutpost.dexdrip.ImportedLibraries.dexcom.records;
|
||||
|
||||
import com.eveningoutpost.dexdrip.Models.UserError.Log;
|
||||
|
||||
import com.eveningoutpost.dexdrip.ImportedLibraries.dexcom.CRC16;
|
||||
import com.eveningoutpost.dexdrip.ImportedLibraries.dexcom.CRCFailRuntimeException;
|
||||
import com.eveningoutpost.dexdrip.ImportedLibraries.dexcom.Dex_Constants;
|
||||
import com.eveningoutpost.dexdrip.ImportedLibraries.dexcom.Utils;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.ByteOrder;
|
||||
import java.util.Arrays;
|
||||
|
||||
// This code and this particular library are from the NightScout android uploader
|
||||
// Check them out here: https://github.com/nightscout/android-uploader
|
||||
// Some of this code may have been modified for use in this project
|
||||
|
||||
public class PageHeader {
|
||||
protected final int HEADER_SIZE=28;
|
||||
protected final int FIRSTRECORDINDEX_OFFSET=0;
|
||||
protected final int NUMRECS_OFFSET=4;
|
||||
protected final int RECTYPE_OFFSET=8;
|
||||
protected final int REV_OFFSET=9;
|
||||
protected final int PAGENUMBER_OFFSET=10;
|
||||
protected final int RESERVED2_OFFSET=14;
|
||||
protected final int RESERVED3_OFFSET=18;
|
||||
protected final int RESERVED4_OFFSET=22;
|
||||
|
||||
protected int firstRecordIndex;
|
||||
protected int numOfRecords;
|
||||
protected Dex_Constants.RECORD_TYPES recordType;
|
||||
protected byte revision;
|
||||
protected int pageNumber;
|
||||
protected int reserved2;
|
||||
protected int reserved3;
|
||||
protected int reserved4;
|
||||
protected byte[] crc=new byte[2];
|
||||
|
||||
|
||||
public PageHeader(byte[] packet) {
|
||||
Log.d("ShareTest", "Header Packet Data Length: " + packet.length);
|
||||
|
||||
firstRecordIndex = ByteBuffer.wrap(packet).order(ByteOrder.LITTLE_ENDIAN).getInt(FIRSTRECORDINDEX_OFFSET);
|
||||
numOfRecords = ByteBuffer.wrap(packet).order(ByteOrder.LITTLE_ENDIAN).getInt(NUMRECS_OFFSET);
|
||||
recordType = Dex_Constants.RECORD_TYPES.values()[packet[RECTYPE_OFFSET]];
|
||||
revision = packet[REV_OFFSET];
|
||||
pageNumber = ByteBuffer.wrap(packet).order(ByteOrder.LITTLE_ENDIAN).getInt(PAGENUMBER_OFFSET);
|
||||
reserved2 = ByteBuffer.wrap(packet).order(ByteOrder.LITTLE_ENDIAN).getInt(RESERVED2_OFFSET);
|
||||
reserved3 = ByteBuffer.wrap(packet).order(ByteOrder.LITTLE_ENDIAN).getInt(RESERVED3_OFFSET);
|
||||
reserved4 = ByteBuffer.wrap(packet).order(ByteOrder.LITTLE_ENDIAN).getInt(RESERVED4_OFFSET);
|
||||
System.arraycopy(packet,HEADER_SIZE- Dex_Constants.CRC_LEN,crc,0, Dex_Constants.CRC_LEN);
|
||||
byte[] crc_calc = CRC16.calculate(packet,0,HEADER_SIZE - Dex_Constants.CRC_LEN);
|
||||
if (!Arrays.equals(this.crc, crc_calc)) {
|
||||
throw new CRCFailRuntimeException("CRC check failed: " + Utils.bytesToHex(this.crc) + " vs " + Utils.bytesToHex(crc_calc));
|
||||
}
|
||||
}
|
||||
|
||||
public byte getRevision() {
|
||||
return revision;
|
||||
}
|
||||
|
||||
public Dex_Constants.RECORD_TYPES getRecordType() {
|
||||
return recordType;
|
||||
}
|
||||
|
||||
public int getFirstRecordIndex() {
|
||||
return firstRecordIndex;
|
||||
}
|
||||
|
||||
public int getNumOfRecords() {
|
||||
return numOfRecords;
|
||||
}
|
||||
|
||||
public int getPageNumber() {
|
||||
return pageNumber;
|
||||
}
|
||||
|
||||
public int getReserved2() {
|
||||
return reserved2;
|
||||
}
|
||||
|
||||
public int getReserved3() {
|
||||
return reserved3;
|
||||
}
|
||||
|
||||
public int getReserved4() {
|
||||
return reserved4;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
package com.eveningoutpost.dexdrip.ImportedLibraries.dexcom.records;
|
||||
|
||||
import com.eveningoutpost.dexdrip.Models.UserError.Log;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.ByteOrder;
|
||||
|
||||
// This code and this particular library are from the NightScout android uploader
|
||||
// Check them out here: https://github.com/nightscout/android-uploader
|
||||
// Some of this code may have been modified for use in this project
|
||||
|
||||
public class SensorRecord extends GenericTimestampRecord {
|
||||
|
||||
private int unfiltered;
|
||||
private int filtered;
|
||||
private int rssi;
|
||||
private int OFFSET_UNFILTERED = 8;
|
||||
private int OFFSET_FILTERED = 12;
|
||||
private int OFFSET_RSSI = 16;
|
||||
|
||||
public SensorRecord(byte[] packet) {
|
||||
super(packet);
|
||||
unfiltered = ByteBuffer.wrap(packet).order(ByteOrder.LITTLE_ENDIAN).getInt(OFFSET_UNFILTERED);
|
||||
filtered = ByteBuffer.wrap(packet).order(ByteOrder.LITTLE_ENDIAN).getInt(OFFSET_FILTERED);
|
||||
byte [] usRSSI = new byte[]{packet[17],packet[16]};
|
||||
rssi = usRSSI[0] << 8 | usRSSI[1];
|
||||
//rssi = ByteBuffer.wrap(packet).order(ByteOrder.LITTLE_ENDIAN).getShort(OFFSET_RSSI);
|
||||
Log.d("ShareTest", "filtered: " + filtered + " unfiltered: " + unfiltered);
|
||||
}
|
||||
|
||||
public long getUnfiltered() {
|
||||
return unfiltered;
|
||||
}
|
||||
|
||||
public long getFiltered() {
|
||||
return filtered;
|
||||
}
|
||||
|
||||
public int getRSSI() {
|
||||
return rssi;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user