package com.eveningoutpost.dexdrip.Services;
import java.io.IOException;
import java.util.Date;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.Socket;
import java.net.SocketTimeoutException;
import java.util.LinkedList;
import java.util.List;
import android.content.Context;
import android.content.SharedPreferences;
import android.preference.PreferenceManager;
import android.util.Log;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.eveningoutpost.dexdrip.Sensor;
import com.eveningoutpost.dexdrip.Models.BgReading;
import com.eveningoutpost.dexdrip.Models.TransmitterData;
public class WixelReader extends Thread {
private final static String TAG = WixelReader.class.getName();
private static WixelReader singleton;
public synchronized static WixelReader getInstance(Context ctx) {
if(singleton == null) {
singleton = new WixelReader(ctx);
}
return singleton;
}
private final Context mContext;
private volatile boolean mStop = false;
private static boolean sStarted = false;
public WixelReader(Context ctx) {
mContext = ctx.getApplicationContext();
}
public static void sStart(Context ctx) {
if(sStarted) {
return;
}
WixelReader theWixelReader = getInstance(ctx);
theWixelReader.start();
sStarted = true;
}
public static void sStop() {
if(!sStarted) {
return;
}
WixelReader theWixelReader = getInstance(null);
theWixelReader.Stop();
try {
theWixelReader.join();
} catch (InterruptedException e) {
Log.e(TAG, "cought InterruptedException, could not wait for the wixel thread to exit", e);
}
sStarted = false;
// A stopped thread can not start again, so we need to kill it and will start a new one
// on demand
singleton = null;
}
public static boolean IsConfigured(Context ctx) {
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(ctx);
String recieversIpAddresses = prefs.getString("wifi_recievers_addresses", "");
if(recieversIpAddresses == null || recieversIpAddresses.equals("") ) {
return false;
}
return true;
}
public static boolean almostEquals( TransmitterRawData e1, TransmitterRawData e2)
{
if (e1 == null || e2==null) {
return false;
}
// relative time is in ms
if ((Math.abs(e1.CaptureDateTime - e2.CaptureDateTime) < 120 * 1000 ) &&
(e1.TransmissionId == e2.TransmissionId)) {
return true;
}
return false;
}
// last in the array, is first in time
public static List<TransmitterRawData> Merge2Lists(List<TransmitterRawData> list1 , List<TransmitterRawData> list2)
{
List<TransmitterRawData> merged = new LinkedList <TransmitterRawData>();
while (true) {
if(list1.size() == 0 && list2.size() == 0) {
break;
}
if (list1.size() == 0) {
merged.addAll(list2);
break;
}
if (list2.size() == 0) {
merged.addAll(list1);
break;
}
if (almostEquals(list1.get(0), list2.get(0))) {
list2.remove(0);
merged.add(list1.remove(0));
continue;
}
if(list1.get(0).RelativeTime > list2.get(0).RelativeTime) {
merged.add(list1.remove(0));
} else {
merged.add(list2.remove(0));
}
}
return merged;
}
public static List<TransmitterRawData> MergeLists(List <List<TransmitterRawData>> allTransmitterRawData)
{
List<TransmitterRawData> MergedList;
MergedList = allTransmitterRawData.remove(0);
for (List<TransmitterRawData> it : allTransmitterRawData) {
MergedList = Merge2Lists(MergedList, it);
}
return MergedList;
}
public static List<TransmitterRawData> ReadHost(String hostAndIp, int numberOfRecords)
{
int port;
System.out.println("Reading From " + hostAndIp);
Log.i(TAG,"Reading From " + hostAndIp);
String []hosts = hostAndIp.split(":");
if(hosts.length != 2) {
System.out.println("Invalid hostAndIp " + hostAndIp);
Log.e(TAG, "Invalid hostAndIp " + hostAndIp);
return null;
}
try {
port = Integer.parseInt(hosts[1]);
} catch (NumberFormatException nfe) {
System.out.println("Invalid port " +hosts[1]);
Log.e(TAG, "Invalid hostAndIp " + hostAndIp, nfe);
return null;
}
if (port < 10 || port > 65536) {
System.out.println("Invalid port " +hosts[1]);
Log.e(TAG, "Invalid hostAndIp " + hostAndIp);
return null;
}
System.out.println("Reading from " + hosts[0] + " " + port);
List<TransmitterRawData> ret;
try {
ret = Read(hosts[0], port, numberOfRecords);
} catch(Exception e) {
// We had some error, need to move on...
System.out.println("read from host failed cought expation" + hostAndIp);
Log.e(TAG, "read from host failed " + hostAndIp, e);
return null;
}
return ret;
}
public static List<TransmitterRawData> ReadFromMongo(String dbury, int numberOfRecords)
{
Log.i(TAG,"Reading From " + dbury);
List<TransmitterRawData> tmpList;
// format is dburi/db/collection. We need to find the collection and strip it from the dburi.
int indexOfSlash = dbury.lastIndexOf('/');
if(indexOfSlash == -1) {
// We can not find a collection name
Log.e(TAG, "Error bad dburi. Did not find a collection name starting with / " + dbury);
// in order for the user to understand that there is a problem, we return null
return null;
}
String collection = dbury.substring(indexOfSlash + 1);
dbury = dbury.substring(0, indexOfSlash);
// Make sure that we have another /, since this is used in the constructor.
indexOfSlash = dbury.lastIndexOf('/');
if(indexOfSlash == -1) {
// We can not find a collection name
Log.e(TAG, "Error bad dburi. Did not find a collection name starting with / " + dbury);
// in order for the user to understand that there is a problem, we return null
return null;
}
MongoWrapper mt = new MongoWrapper(dbury, collection, "CaptureDateTime", "MachineNameNotUsed");
return mt.ReadFromMongo(numberOfRecords);
}
// format of string is ip1:port1,ip2:port2;
public static TransmitterRawData[] Read(String hostsNames, int numberOfRecords)
{
String []hosts = hostsNames.split(",");
if(hosts.length == 0) {
Log.e(TAG, "Error no hosts were found " + hostsNames);
return null;
}
List <List<TransmitterRawData>> allTransmitterRawData = new LinkedList <List<TransmitterRawData>>();
// go over all hosts and read data from them
for(String host : hosts) {
List<TransmitterRawData> tmpList;
if (host.startsWith("mongodb://")) {
tmpList = ReadFromMongo(host ,numberOfRecords);
} else {
tmpList = ReadHost(host, numberOfRecords);
}
if(tmpList != null && tmpList.size() > 0) {
allTransmitterRawData.add(tmpList);
}
}
// merge the information
if (allTransmitterRawData.size() == 0) {
System.out.println("Could not read anything from " + hostsNames);
Log.e(TAG, "Could not read anything from " + hostsNames);
return null;
}
List<TransmitterRawData> mergedData= MergeLists(allTransmitterRawData);
int retSize = Math.min(numberOfRecords, mergedData.size());
TransmitterRawData []trd_array = new TransmitterRawData[retSize];
mergedData.subList(mergedData.size() - retSize, mergedData.size()).toArray(trd_array);
System.out.println("Final Results========================================================================");
for (int i= 0; i < trd_array.length; i++) {
// System.out.println( trd_array[i].toTableString());
}
return trd_array;
}
public static List<TransmitterRawData> Read(String hostName,int port, int numberOfRecords)
{
List<TransmitterRawData> trd_list = new LinkedList<TransmitterRawData>();
try
{
Log.i(TAG, "Read called");
Gson gson = new GsonBuilder().create();
// An example of using gson.
ComunicationHeader ch = new ComunicationHeader();
ch.version = 1;
ch.numberOfRecords = numberOfRecords;
String flat = gson.toJson(ch);
ComunicationHeader ch2 = gson.fromJson(flat, ComunicationHeader.class);
System.out.println("Results code" + flat + ch2.version);
// Real client code
Socket MySocket = new Socket(hostName, port);
System.out.println("After the new socket \n");
MySocket.setSoTimeout(2000);
System.out.println("client connected... " );
PrintWriter out = new PrintWriter(MySocket.getOutputStream(), true);
BufferedReader in = new BufferedReader(new InputStreamReader(MySocket.getInputStream()));
out.println(flat);
while(true) {
String data = in.readLine();
if(data == null) {
System.out.println("recieved null exiting");
break;
}
if(data.equals("")) {
System.out.println("recieved \"\" exiting");
break;
}
//System.out.println( "data size " +data.length() + " data = "+ data);
TransmitterRawData trd = gson.fromJson(data, TransmitterRawData.class);
trd.CaptureDateTime = System.currentTimeMillis() - trd.RelativeTime;
trd_list.add(0,trd);
// System.out.println( trd.toTableString());
if(trd_list.size() == numberOfRecords) {
// We have the data we want, let's get out
break;
}
}
MySocket.close();
return trd_list;
}catch(SocketTimeoutException s) {
Log.e(TAG, "Socket timed out! ", s);
}
catch(IOException e) {
Log.e(TAG, "cought IOException! ", e);
}
return trd_list;
}
public void run()
{
Long LastReportedTime = new Date().getTime();
TransmitterRawData LastReportedReading = null;
Log.e(TAG, "Starting... LastReportedReading " + LastReportedReading);
try {
while (!mStop && !interrupted()) {
// try to read one object...
TransmitterRawData[] LastReadingArr = null;
if(WixelReader.IsConfigured(mContext)) {
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(mContext);
String recieversIpAddresses = prefs.getString("wifi_recievers_addresses", "");
LastReadingArr = Read(recieversIpAddresses ,1);
}
if (LastReadingArr != null && LastReadingArr.length > 0) {
// Last in the array is the most updated reading we have.
TransmitterRawData LastReading = LastReadingArr[LastReadingArr.length -1];
//if (LastReading.CaptureDateTime > LastReportedReading + 5000) {
// Make sure we do not report packets from the far future...
if ((LastReading.CaptureDateTime > LastReportedTime ) &&
(!almostEquals(LastReading, LastReportedReading)) &&
LastReading.CaptureDateTime < new Date().getTime() + 12000) {
// We have a real new reading...
Log.e(TAG, "calling setSerialDataToTransmitterRawData " + LastReading.RawValue +
" LastReading.CaptureDateTime " + LastReading.CaptureDateTime + " " + LastReading.TransmissionId);
setSerialDataToTransmitterRawData(LastReading.RawValue , LastReading.BatteryLife, LastReading.CaptureDateTime);
LastReportedReading = LastReading;
LastReportedTime = LastReading.CaptureDateTime;
}
}
// let's sleep (right now for 30 seconds)
Thread.sleep(30000);
}
} catch (InterruptedException e) {
Log.e(TAG, "cought InterruptedException! ", e);
// time to get out...
}
}
// this function is only a test function. It is used to set many points fast in order to allow
// faster testing without real data.
public void runFake()
{
// let's start by faking numbers....
int i = 0;
int added = 5;
while (!mStop) {
try {
for (int j = 0 ; j < 3; j++) {
Thread.sleep(1000);
if(mStop ) {
// we were asked to leave, so do it....
return;
}
}
i+=added;
if (i==50) {
added = -5;
}
if (i==0) {
added = 5;
}
int fakedRaw = 150000 + i * 1000;
Log.e(TAG, "calling setSerialDataToTransmitterRawData " + fakedRaw);
setSerialDataToTransmitterRawData(fakedRaw, 100, new Date().getTime());
} catch (InterruptedException e) {
// time to get out...
Log.e(TAG, "cought InterruptedException! ", e);
break;
}
}
}
public void Stop()
{
mStop = true;
interrupt();
}
public void setSerialDataToTransmitterRawData(int raw_data ,int sensor_battery_leve, Long CaptureTime) {
TransmitterData transmitterData = TransmitterData.create(raw_data, sensor_battery_leve, CaptureTime);
if (transmitterData != null) {
Sensor sensor = Sensor.currentSensor();
if (sensor != null) {
BgReading bgReading = BgReading.create(transmitterData.raw_data, mContext, CaptureTime);
sensor.latest_battery_level = transmitterData.sensor_battery_level;
sensor.save();
} else {
Log.w(TAG, "No Active Sensor, Data only stored in Transmitter Data");
}
}
}
}