Files
2020-07-18 21:44:27 -04:00

259 lines
11 KiB
Java

package com.eveningoutpost.dexdrip.Models;
import com.eveningoutpost.dexdrip.ImportedLibraries.usbserial.util.HexDump;
import com.eveningoutpost.dexdrip.Models.UserError.Log;
import com.eveningoutpost.dexdrip.NFCReaderX;
import com.eveningoutpost.dexdrip.R;
import com.eveningoutpost.dexdrip.UtilityModels.Blukon;
import com.eveningoutpost.dexdrip.UtilityModels.BridgeResponse;
import com.eveningoutpost.dexdrip.UtilityModels.LibreUtils;
import com.eveningoutpost.dexdrip.UtilityModels.PersistentStore;
import com.eveningoutpost.dexdrip.UtilityModels.Pref;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.Arrays;
import static com.eveningoutpost.dexdrip.xdrip.gs;
/**
* Created by Tzachi Dar on 7.3.2018.
*/
public class Tomato {
private static final String TAG = "DexCollectionService";//?????"Tomato";
private static final String CHECKSUM_FAILED = "checksum failed";
private static final String SERIAL_FAILED = "serial failed";
private enum TOMATO_STATES {
REQUEST_DATA_SENT,
RECIEVING_DATA
}
private final static int TOMATO_HEADER_LENGTH = 18;
private final static int TOMATO_PATCH_INFO = 6;
static volatile TOMATO_STATES s_state;
private static volatile long s_lastReceiveTimestamp;
private static volatile byte[] s_full_data = null;
private static volatile int s_acumulatedSize = 0;
private static volatile boolean s_recviedEnoughData;
public static boolean isTomato() {
final ActiveBluetoothDevice activeBluetoothDevice = ActiveBluetoothDevice.first();
if (activeBluetoothDevice == null || activeBluetoothDevice.name == null) {
return false;
}
return activeBluetoothDevice.name.startsWith("miaomiao")
|| activeBluetoothDevice.name.toLowerCase().startsWith("watlaa");
}
public static BridgeResponse decodeTomatoPacket(byte[] buffer, int len) {
final BridgeResponse reply = new BridgeResponse();
// Check time, probably need to start on sending
long now = JoH.tsl();
if(now - s_lastReceiveTimestamp > 3*1000) {
// We did not receive data in 3 seconds, moving to init state again
Log.e(TAG, "Recieved a buffer after " + (now - s_lastReceiveTimestamp) / 1000 + " seconds, starting again. "+
"already acumulated " + s_acumulatedSize + " bytes.");
s_state = TOMATO_STATES.REQUEST_DATA_SENT;
}
s_lastReceiveTimestamp = now;
if (buffer == null) {
Log.e(TAG, "null buffer passed to decodeTomatoPacket");
return reply;
}
if (s_state == TOMATO_STATES.REQUEST_DATA_SENT) {
if(buffer.length == 1 && buffer[0] == 0x32) {
Log.e(TAG, "returning allow sensor confirm");
ByteBuffer allowNewSensor = ByteBuffer.allocate(2);
allowNewSensor.put(0, (byte) 0xD3);
allowNewSensor.put(1, (byte) 0x01);
reply.add(allowNewSensor);
// For debug, make it send data every minute (did not work...)
ByteBuffer newFreqMessage = ByteBuffer.allocate(2);
newFreqMessage.put(0, (byte) 0xD1);
newFreqMessage.put(1, (byte) 0x05);
reply.add(newFreqMessage);
//command to start reading
ByteBuffer ackMessage = ByteBuffer.allocate(1);
ackMessage.put(0, (byte) 0xF0);
reply.add(ackMessage);
return reply;
}
if(buffer.length == 1 && buffer[0] == 0x34) {
Log.e(TAG, "No sensor has been found");
reply.setError_message(gs(R.string.no_sensor_found));
return reply;
}
// 18 is the expected header size
if(buffer.length >= TOMATO_HEADER_LENGTH && buffer[0] == 0x28) {
// We are starting to receive data, need to start accumulating
// &0xff is needed to convert to hex.
int expectedSize = 256 * (int)(buffer[1] & 0xFF) + (int)(buffer[2] & 0xFF);
Log.e(TAG, "Starting to acumulate data expectedSize = " + expectedSize);
InitBuffer(expectedSize + TOMATO_PATCH_INFO);
addData(buffer);
s_state = TOMATO_STATES.RECIEVING_DATA;
return reply;
} else {
if (JoH.quietratelimit("unknown-initial-packet", 1)) {
Log.d(TAG,"Unknown initial packet makeup received" + HexDump.dumpHexString(buffer));
}
return reply;
}
}
if (s_state == TOMATO_STATES.RECIEVING_DATA) {
//Log.e(TAG, "received more data s_acumulatedSize = " + s_acumulatedSize + " current buffer size " + buffer.length);
try {
addData(buffer);
} catch (RuntimeException e) {
// if the checksum failed lets ask for the data set again but not more than once per minute
if (e.getMessage().equals(CHECKSUM_FAILED)) {
if (JoH.ratelimit("tomato-full-retry",60)
|| JoH.ratelimit("tomato-full-retry2",60)) {
reply.getSend().clear();
reply.getSend().addAll(Tomato.resetTomatoState());
reply.setDelay(8000);
reply.setError_message(gs(R.string.checksum_failed__retrying));
Log.d(TAG,"Asking for retry of data");
}
} else if (e.getMessage().equals(SERIAL_FAILED)) {
reply.setError_message("Sensor Serial Problem");
} else throw e;
}
return reply;
}
Log.wtf(TAG, "Very strange, In an unexpected state " + s_state);
return reply;
}
static void addData(byte[] buffer) {
if(s_acumulatedSize + buffer.length > s_full_data.length) {
Log.e(TAG, "Error recieving too much data. exiting. s_acumulatedSize = " + s_acumulatedSize +
" buffer.length = " + buffer.length + " s_full_data.length " + s_full_data.length);
//??? send something to start back??
return;
}
System.arraycopy(buffer, 0, s_full_data, s_acumulatedSize, buffer.length);
s_acumulatedSize += buffer.length;
AreWeDone();
}
static void AreWeDone() {
// Give both versions a chance to work.
final int extended_length = 344 + TOMATO_HEADER_LENGTH + 1 + TOMATO_PATCH_INFO;
if(s_recviedEnoughData && (s_acumulatedSize != extended_length)) {
// This reading already ended
Log.e(TAG,"Getting out, as s_recviedEnoughData and we have too much data already s_acumulatedSize = " + s_acumulatedSize);
return;
}
if(s_acumulatedSize < 344 + TOMATO_HEADER_LENGTH + 1 ) {
//Log.e(TAG,"Getting out, since not enough data s_acumulatedSize = " + s_acumulatedSize);
return;
}
byte[] data = Arrays.copyOfRange(s_full_data, TOMATO_HEADER_LENGTH, TOMATO_HEADER_LENGTH+344);
s_recviedEnoughData = true;
long now = JoH.tsl();
// Important note, the actual serial number is 8 bytes long and starts at addresses 5.
final String SensorSn = LibreUtils.decodeSerialNumberKey(Arrays.copyOfRange(s_full_data, 5, 13));
byte []patchUid = null;
byte []patchInfo = null;
if(s_acumulatedSize >= extended_length) {
patchUid = Arrays.copyOfRange(s_full_data, 5, 13);
patchInfo = Arrays.copyOfRange(s_full_data, TOMATO_HEADER_LENGTH+ 344 + 1 , TOMATO_HEADER_LENGTH + 344 + 1+ TOMATO_PATCH_INFO);
}
Log.d(TAG, "patchUid = " + HexDump.dumpHexString(patchUid));
Log.d(TAG, "patchInfo = " + HexDump.dumpHexString(patchInfo));
boolean checksum_ok = NFCReaderX.HandleGoodReading(SensorSn, data, now, true, patchUid, patchInfo);
Log.e(TAG, "We have all the data that we need " + s_acumulatedSize + " checksum_ok = " + checksum_ok + HexDump.dumpHexString(data));
if(!checksum_ok) {
throw new RuntimeException(CHECKSUM_FAILED);
}
if (SensorSanity.checkLibreSensorChangeIfEnabled(SensorSn)) {
Log.e(TAG,"Problem with Libre Serial Number - not processing");
throw new RuntimeException(SERIAL_FAILED);
}
PersistentStore.setString("Tomatobattery", Integer.toString(s_full_data[13]));
Pref.setInt("bridge_battery", s_full_data[13]);
PersistentStore.setString("TomatoHArdware",HexDump.toHexString(s_full_data,16,2));
PersistentStore.setString("TomatoFirmware",HexDump.toHexString(s_full_data,14,2));
PersistentStore.setString("LibreSN", SensorSn);
}
// This is the function that we should have once we are able to read all data realiably.
static void AreWeDoneMax() {
if(s_acumulatedSize == s_full_data.length) {
Log.e(TAG, "We have a full packet");
} else {
return;
}
if(s_full_data[s_full_data.length -1] != 0x29) {
Log.e(TAG, "recieved full data, but last byte is not 0x29. It is " + s_full_data[s_full_data.length -1]);
return;
}
// We have all the data
if(s_full_data.length < 344 + TOMATO_HEADER_LENGTH + 1) {
Log.e(TAG, "We have all the data, but it is not enough... s_full_data.length = " + s_full_data.length );
return;
}
Log.e(TAG, "We have a full packet");
}
static void InitBuffer(int expectedSize) {
s_full_data = new byte[expectedSize];
s_acumulatedSize = 0;
s_recviedEnoughData = false;
}
public static ArrayList<ByteBuffer> initialize() {
Log.i(TAG, "initialize!");
Pref.setInt("bridge_battery", 0); //force battery to no-value before first reading
return resetTomatoState();
}
private static ArrayList<ByteBuffer> resetTomatoState() {
ArrayList<ByteBuffer> ret = new ArrayList<>();
s_state = TOMATO_STATES.REQUEST_DATA_SENT;
// Make tomato send data every 5 minutes
ByteBuffer newFreqMessage = ByteBuffer.allocate(2);
newFreqMessage.put(0, (byte) 0xD1);
newFreqMessage.put(1, (byte) 0x05);
ret.add(newFreqMessage);
//command to start reading
ByteBuffer ackMessage = ByteBuffer.allocate(1);
ackMessage.put(0, (byte) 0xF0);
ret.add(ackMessage);
return ret;
}
}