package com.eveningoutpost.dexdrip.ImportedLibraries.dexcom;
import android.hardware.usb.UsbDevice;
import android.hardware.usb.UsbDeviceConnection;
import android.hardware.usb.UsbEndpoint;
import android.hardware.usb.UsbInterface;
import android.util.Log;
import com.eveningoutpost.dexdrip.ImportedLibraries.usbserial.driver.CdcAcmSerialDriver;
import com.eveningoutpost.dexdrip.ImportedLibraries.usbserial.driver.UsbSerialDriver;
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.UsbSerialPort;
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;
import java.util.List;
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.w("FAILED WHILE", "trying to open");
}
// }
}
public EGVRecord[] getRecentEGVs() {
int recordType = 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 = 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 = 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 = 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 = 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 = 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(Constants.PING);
return read(MIN_LEN).getCommand() == Constants.ACK;
}
public int readBatteryLevel() {
Log.d(TAG, "Reading battery level...");
writeCommand(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(Constants.RECORD_TYPES.MANUFACTURING_DATA.ordinal(), PAGE_OFFSET);
Element md = ParsePage(readData, 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(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(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(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(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(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 (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;
}
}