package com.eveningoutpost.dexdrip.Models; import android.provider.BaseColumns; import com.activeandroid.Model; import com.activeandroid.annotation.Column; import com.activeandroid.annotation.Table; import com.activeandroid.query.Select; import com.eveningoutpost.dexdrip.GcmActivity; import com.eveningoutpost.dexdrip.Home; import com.eveningoutpost.dexdrip.ImportedLibraries.usbserial.util.HexDump; import com.eveningoutpost.dexdrip.Models.UserError.Log; import com.eveningoutpost.dexdrip.UtilityModels.Constants; import com.eveningoutpost.dexdrip.UtilityModels.Pref; import com.eveningoutpost.dexdrip.utils.CheckBridgeBattery; import com.eveningoutpost.dexdrip.utils.DexCollectionType; import com.google.gson.annotations.Expose; import java.nio.ByteBuffer; import java.nio.ByteOrder; import java.util.Date; import java.util.List; import java.util.UUID; /** * Created by Emma Black on 11/6/14. */ @Table(name = "TransmitterData", id = BaseColumns._ID) public class TransmitterData extends Model { private final static String TAG = TransmitterData.class.getSimpleName(); @Expose @Column(name = "timestamp", index = true) public long timestamp; // TODO these should be int or long surely @Expose @Column(name = "raw_data") public double raw_data; @Expose @Column(name = "filtered_data") public double filtered_data; @Expose @Column(name = "sensor_battery_level") public int sensor_battery_level; @Expose @Column(name = "uuid", index = true) public String uuid; public static synchronized TransmitterData create(byte[] buffer, int len, Long timestamp) { if (len < 6) { return null; } final TransmitterData transmitterData = new TransmitterData(); try { if ((buffer[0] == 0x11 || buffer[0] == 0x15) && buffer[1] == 0x00) { //this is a dexbridge packet. Process accordingly. Log.i(TAG, "create Processing a Dexbridge packet"); final ByteBuffer txData = ByteBuffer.allocate(len); txData.order(ByteOrder.LITTLE_ENDIAN); txData.put(buffer, 0, len); transmitterData.raw_data = txData.getInt(2); transmitterData.filtered_data = txData.getInt(6); // bitwise and with 0xff (1111....1) to avoid that the byte is treated as signed. transmitterData.sensor_battery_level = txData.get(10) & 0xff; if (buffer[0] == 0x15) { Log.i(TAG, "create Processing a Dexbridge packet includes delay information"); transmitterData.timestamp = timestamp - txData.getInt(16); } else { transmitterData.timestamp = timestamp; } Log.i(TAG, "Created transmitterData record with Raw value of " + transmitterData.raw_data + " and Filtered value of " + transmitterData.filtered_data + " at " + timestamp + " with timestamp " + transmitterData.timestamp); } else { //this is NOT a dexbridge packet. Process accordingly. Log.i(TAG, "create Processing a BTWixel or IPWixel packet"); StringBuilder data_string = new StringBuilder(); for (int i = 0; i < len; ++i) { data_string.append((char) buffer[i]); } final String[] data = data_string.toString().split("\\s+"); if (data.length > 1) { transmitterData.sensor_battery_level = Integer.parseInt(data[1]); if (data.length > 2) { try { Pref.setInt("bridge_battery", Integer.parseInt(data[2])); if (Home.get_master()) { GcmActivity.sendBridgeBattery(Pref.getInt("bridge_battery", -1)); } CheckBridgeBattery.checkBridgeBattery(); } catch (Exception e) { Log.e(TAG, "Got exception processing classic wixel or limitter battery value: " + e.toString()); } if (data.length > 3) { if ((DexCollectionType.getDexCollectionType() == DexCollectionType.LimiTTer) && (!Pref.getBooleanDefaultFalse("use_transmiter_pl_bluetooth"))) { try { // reported sensor age in minutes final Integer sensorAge = Integer.parseInt(data[3]); if ((sensorAge > 0) && (sensorAge < 200000)) Pref.setInt("nfc_sensor_age", sensorAge); } catch (Exception e) { Log.e(TAG, "Got exception processing field 4 in classic limitter protocol: " + e); } } } } } transmitterData.raw_data = Integer.parseInt(data[0]); transmitterData.filtered_data = Integer.parseInt(data[0]); // TODO process does_have_filtered_here with extended protocol transmitterData.timestamp = timestamp; } //Stop allowing readings that are older than the last one - or duplicate data, its bad! (from savek-cc) final TransmitterData lastTransmitterData = TransmitterData.last(); if (lastTransmitterData != null && lastTransmitterData.timestamp >= transmitterData.timestamp) { Log.e(TAG, "Rejecting TransmitterData constraint: last: " + JoH.dateTimeText(lastTransmitterData.timestamp) + " >= this: " + JoH.dateTimeText(transmitterData.timestamp)); return null; } if (lastTransmitterData != null && lastTransmitterData.raw_data == transmitterData.raw_data && Math.abs(lastTransmitterData.timestamp - transmitterData.timestamp) < (Constants.MINUTE_IN_MS * 2)) { Log.e(TAG, "Rejecting identical TransmitterData constraint: last: " + JoH.dateTimeText(lastTransmitterData.timestamp) + " due to 2 minute rule this: " + JoH.dateTimeText(transmitterData.timestamp)); return null; } final Calibration lastCalibration = Calibration.lastValid(); if (lastCalibration != null && lastCalibration.timestamp > transmitterData.timestamp) { Log.e(TAG, "Rejecting historical TransmitterData constraint: calib: " + JoH.dateTimeText(lastCalibration.timestamp) + " > this: " + JoH.dateTimeText(transmitterData.timestamp)); return null; } transmitterData.uuid = UUID.randomUUID().toString(); transmitterData.save(); return transmitterData; } catch (Exception e) { Log.e(TAG, "Got exception processing fields in protocol: " + e + " " + HexDump.dumpHexString(buffer)); } return null; } public static synchronized TransmitterData create(int raw_data, int filtered_data, int sensor_battery_level, long timestamp) { TransmitterData lastTransmitterData = TransmitterData.last(); if (lastTransmitterData != null && lastTransmitterData.raw_data == raw_data && Math.abs(lastTransmitterData.timestamp - new Date().getTime()) < (Constants.MINUTE_IN_MS * 2)) { //Stop allowing duplicate data, its bad! return null; } TransmitterData transmitterData = new TransmitterData(); transmitterData.sensor_battery_level = sensor_battery_level; transmitterData.raw_data = raw_data; transmitterData.filtered_data = filtered_data; transmitterData.timestamp = timestamp; transmitterData.uuid = UUID.randomUUID().toString(); transmitterData.save(); return transmitterData; } public static synchronized TransmitterData create(int raw_data ,int sensor_battery_level, long timestamp) { TransmitterData lastTransmitterData = TransmitterData.last(); if (lastTransmitterData != null && lastTransmitterData.raw_data == raw_data && Math.abs(lastTransmitterData.timestamp - new Date().getTime()) < (Constants.MINUTE_IN_MS * 2)) { //Stop allowing duplicate data, its bad! return null; } TransmitterData transmitterData = new TransmitterData(); transmitterData.sensor_battery_level = sensor_battery_level; transmitterData.raw_data = raw_data ; transmitterData.timestamp = timestamp; transmitterData.uuid = UUID.randomUUID().toString(); transmitterData.save(); return transmitterData; } public static TransmitterData last() { return new Select() .from(TransmitterData.class) .orderBy("_ID desc") .executeSingle(); } public static List last(int count) { return new Select() .from(TransmitterData.class) .orderBy("_ID desc") .limit(count) .execute(); } public static TransmitterData lastByTimestamp() { return new Select() .from(TransmitterData.class) .orderBy("timestamp desc") .executeSingle(); } public static TransmitterData getForTimestamp(double timestamp) {//KS try { Sensor sensor = Sensor.currentSensor(); if (sensor != null) { TransmitterData bgReading = new Select() .from(TransmitterData.class) .where("timestamp <= ?", (timestamp + (60 * 1000))) // 1 minute padding (should never be that far off, but why not) .orderBy("timestamp desc") .executeSingle(); if (bgReading != null && Math.abs(bgReading.timestamp - timestamp) < (3 * 60 * 1000)) { //cool, so was it actually within 4 minutes of that bg reading? Log.i(TAG, "getForTimestamp: Found a BG timestamp match"); return bgReading; } } } catch (Exception e) { Log.e(TAG,"getForTimestamp() Got exception on Select : "+e.toString()); return null; } Log.d(TAG, "getForTimestamp: No luck finding a BG timestamp match"); return null; } public static TransmitterData findByUuid(String uuid) {//KS try { return new Select() .from(TransmitterData.class) .where("uuid = ?", uuid) .executeSingle(); } catch (Exception e) { Log.e(TAG,"findByUuid() Got exception on Select : "+e.toString()); return null; } } public static TransmitterData byid(long id) { return new Select() .from(TransmitterData.class) .where("_ID = ?", id) .executeSingle(); } public static void updateTransmitterBatteryFromSync(final int battery_level) { try { TransmitterData td = TransmitterData.last(); if ((td == null) || (td.raw_data!=0)) { td=TransmitterData.create(0,battery_level,(long)JoH.ts()); Log.d(TAG,"Created new fake transmitter data record for battery sync"); if (td==null) return; } if ((battery_level != td.sensor_battery_level) || ((JoH.ts()-td.timestamp)>(1000*60*60))) { td.sensor_battery_level = battery_level; td.timestamp = (long)JoH.ts(); // freshen timestamp on this bogus record for system status Log.d(TAG,"Saving synced sensor battery, new level: "+battery_level); td.save(); } else { Log.d(TAG,"Synced sensor battery level same as existing: "+battery_level); } } catch (Exception e) { Log.e(TAG,"Got exception updating sensor battery from sync: "+e.toString()); } } private static double roundRaw(TransmitterData td) { return JoH.roundDouble(td.raw_data,3); } private static double roundFiltered(TransmitterData td) { return JoH.roundDouble(td.filtered_data,3); } public static boolean unchangedRaw() { final List items = last(3); if (items != null && items.size() == 3) { return (roundRaw(items.get(0)) == roundRaw(items.get(1)) && roundRaw(items.get(0)) == roundRaw(items.get(2)) && roundFiltered(items.get(0)) == roundFiltered(items.get(1)) && roundFiltered(items.get(0)) == roundFiltered(items.get(2))); } return false; } }