package org.greengin.sciencetoolkit.logic.datalogging;
import java.io.File;
import java.util.HashMap;
import java.util.Vector;
import java.util.concurrent.locks.ReentrantLock;
import org.greengin.sciencetoolkit.common.model.Model;
import org.greengin.sciencetoolkit.logic.location.CurrentLocation;
import org.greengin.sciencetoolkit.logic.sensors.SensorWrapper;
import org.greengin.sciencetoolkit.logic.sensors.SensorWrapperManager;
import org.greengin.sciencetoolkit.logic.sensors.TimeValue;
import org.greengin.sciencetoolkit.logic.streams.DataPipe;
import org.greengin.sciencetoolkit.logic.streams.filters.FixedRateDataFilter;
import org.greengin.sciencetoolkit.model.ModelOperations;
import org.greengin.sciencetoolkit.model.ProfileManager;
import org.greengin.sciencetoolkit.model.SenseItModelDefaults;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.support.v4.content.LocalBroadcastManager;
public class DataLogger {
private static final String DATA_LOGGING_NEW_DATA = "DATA_LOGGING_NEW_DATA";
private static final String DATA_LOGGING_NEW_STATUS = "DATA_LOGGING_NEW_STATUS";
private static DataLogger instance;
public static void init(Context applicationContext) {
instance = new DataLogger(applicationContext);
}
public static DataLogger get() {
return instance;
}
ReentrantLock runningLock;
ReentrantLock listenersLock;
Context applicationContext;
String profileId;
Model profile;
int series;
File seriesFile;
HashMap<String, Vector<TimeValue>> seriesRecord;
boolean running;
boolean geolocated;
Vector<DataPipe> pipes;
Vector<DataLoggerDataListener> dataListeners;
BroadcastReceiver dataReceiver;
Vector<DataLoggerStatusListener> statusListeners;
BroadcastReceiver statusReceiver;
DataLoggerFileManager fileManager;
DataLoggerSerializer serializer;
public DataLogger(Context applicationContext) {
this.applicationContext = applicationContext;
runningLock = new ReentrantLock();
listenersLock = new ReentrantLock();
pipes = new Vector<DataPipe>();
fileManager = new DataLoggerFileManager(applicationContext);
series = 0;
serializer = new DataLoggerSerializer(this);
dataListeners = new Vector<DataLoggerDataListener>();
statusListeners = new Vector<DataLoggerStatusListener>();
dataReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
String msg = intent.getExtras().getString("msg");
for (DataLoggerDataListener listener : dataListeners) {
listener.dataLoggerDataModified(msg);
}
}
};
statusReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
String msg = intent.getExtras().getString("msg");
for (DataLoggerStatusListener listener : statusListeners) {
listener.dataLoggerStatusModified(msg);
}
}
};
}
public void registerDataListener(DataLoggerDataListener listener) {
listenersLock.lock();
if (!dataListeners.contains(listener)) {
dataListeners.add(listener);
if (dataListeners.size() == 1) {
LocalBroadcastManager.getInstance(applicationContext).registerReceiver(dataReceiver, new IntentFilter(DataLogger.DATA_LOGGING_NEW_DATA));
}
}
listenersLock.unlock();
}
public void unregisterDataListener(DataLoggerDataListener listener) {
listenersLock.lock();
if (dataListeners.remove(listener) && dataListeners.size() == 0) {
LocalBroadcastManager.getInstance(applicationContext).unregisterReceiver(dataReceiver);
}
}
public void registerStatusListener(DataLoggerStatusListener listener) {
listenersLock.lock();
if (!statusListeners.contains(listener)) {
statusListeners.add(listener);
if (statusListeners.size() == 1) {
LocalBroadcastManager.getInstance(applicationContext).registerReceiver(statusReceiver, new IntentFilter(DataLogger.DATA_LOGGING_NEW_STATUS));
}
}
listenersLock.unlock();
}
public void unregisterStatusListener(DataLoggerStatusListener listener) {
listenersLock.lock();
if (statusListeners.remove(listener) && statusListeners.size() == 0) {
LocalBroadcastManager.getInstance(applicationContext).unregisterReceiver(statusReceiver);
}
}
public boolean isIdle() {
return !running;
}
public boolean isRunning() {
return running;
}
public void startNewSeries() {
runningLock.lock();
if (!running) {
profile = ProfileManager.get().getActiveProfile();
profileId = profile.getString("id");
geolocated = profile.getBool("requires_location");
profile.setBool("initial_edit", false);
geolocated = profile.getBool("requires_location");
pipes.clear();
Vector<Model> sensors = profile.getModel("sensors", true).getModels();
if (sensors.size() > 0) {
running = true;
series = fileManager.startNewSeries(profileId);
seriesFile = fileManager.getCurrentSeriesFile(profileId);
serializer.open(seriesFile, profile);
seriesRecord = new HashMap<String, Vector<TimeValue>>();
for (Model profileSensor : sensors) {
String profileSensorId = profileSensor.getString("id");
String sensorId = profileSensor.getString("sensorid");
SensorWrapper sensor = SensorWrapperManager.get().getSensor(sensorId);
int period = ModelOperations.rate2period(profileSensor, "sample_rate", SenseItModelDefaults.DATA_LOGGING_RATE, null, SenseItModelDefaults.DATA_LOGGING_RATE_MAX);
Vector<TimeValue> record = new Vector<TimeValue>();
seriesRecord.put(profileSensorId, record);
if (sensor != null) {
DataPipe pipe = new DataPipe(sensor);
pipe.addFilter(new FixedRateDataFilter(period));
pipe.setEnd(new DataLoggingInput(profileId, profileSensorId, sensorId, serializer, record));
pipes.add(pipe);
}
}
for (DataPipe pipe : pipes) {
pipe.attach();
}
fireStatusModified("start");
}
}
runningLock.unlock();
}
public void stopSeries() {
runningLock.lock();
if (running) {
for (DataPipe pipe : pipes) {
pipe.detach();
}
pipes.clear();
serializer.close();
if (geolocated) {
String loc = CurrentLocation.get().locationString();
DataLogger.get().getSeriesMetadata(profileId, seriesFile).setString("location", loc);
}
running = false;
fireStatusModified("stop");
}
runningLock.unlock();
}
public File getCurrentSeriesFile() {
return seriesFile;
}
public int getSeriesCount(String profileId) {
return this.fileManager.seriesCount(profileId);
}
public File[] getSeries(String profileId) {
return this.fileManager.series(profileId);
}
public File getSeriesFile(String profileId, String fileName) {
return this.fileManager.seriesFile(profileId, fileName);
}
public HashMap<String, Integer> getCurrentSeriesSampleCountMap() {
return this.serializer.getCountMap();
}
public int getCurrentSeriesSampleCount() {
return this.serializer.getCount();
}
public Vector<TimeValue> getCurrentRecord(String profileSensorId) {
return this.seriesRecord != null ? this.seriesRecord.get(profileSensorId) : null;
}
public void deleteAllData() {
// this.helper.emptyData(null);
}
public void deleteData(String profileId) {
this.fileManager.deleteSeries(profileId);
Model profile = ProfileManager.get().get(profileId);
if (profile != null) {
profile.clear("series", true);
ProfileManager.get().forceSave();
}
this.fireStatusModified("delete");
}
public void deleteData(File series) {
this.deleteData(ProfileManager.get().getActiveProfileId(), series);
}
public void deleteData(String profileId, File series) {
this.fileManager.deleteSeries(series);
Model profile = ProfileManager.get().get(profileId);
if (profile != null) {
profile.getModel("series", true).clear(series.getName(), true);
ProfileManager.get().forceSave();
}
this.fireStatusModified("delete");
}
public void markAsSent(String profileId, File series, int status) {
Model profile = ProfileManager.get().get(profileId);
if (profile != null) {
Model seriesModel = profile.getModel("series", true).getModel(series.getName(), true, true);
seriesModel.setInt("uploaded", status, true);
ProfileManager.get().forceSave();
this.fireStatusModified("upload");
}
}
public int currentUploadedStatus() {
return profileId == null || seriesFile == null ? -1 : this.uploadedStatus(profileId, seriesFile);
}
public int uploadedStatus(String profileId, File series) {
Model profile = ProfileManager.get().get(profileId);
return profile.getModel("series", true).getModel(series.getName(), true).getInt("uploaded", 0);
}
public Model getSeriesMetadata(String profileId, File series) {
Model profile = ProfileManager.get().get(profileId);
return profile.getModel("series", true).getModel(series.getName(), true).getModel("metadata", true);
}
private void fireStatusModified(String event) {
if (statusListeners.size() > 0) {
Intent intent = new Intent(DataLogger.DATA_LOGGING_NEW_STATUS);
intent.putExtra("msg", event);
LocalBroadcastManager.getInstance(applicationContext).sendBroadcast(intent);
}
}
public void fireDataModified(String profileSensorId) {
if (dataListeners.size() > 0) {
Intent intent = new Intent(DataLogger.DATA_LOGGING_NEW_DATA);
intent.putExtra("msg", profileSensorId);
LocalBroadcastManager.getInstance(applicationContext).sendBroadcast(intent);
}
}
public boolean getRange(long[] values, String profileId) {
values[0] = values[1] = 0;
return false;
}
public long getSeriesDuration(File series) {
return this.serializer.duration(series);
}
public HashMap<String, String> getSensorsInSeries(File series) {
return this.serializer.getSensorsInSeries(series);
}
public File getPublicFile(Model profile, File series) {
return this.fileManager.getPublicFile(profile, series);
}
public String seriesName(Model profile, File series) {
return profile.getModel("series", true).getModel(series.getName(), true, true).getString("title", series.getName().replaceFirst("[.][^.]+$", ""));
}
}