package dk.silverbullet.telemed.questionnaire.node.monica.realtime;
import android.app.AlertDialog;
import android.app.ProgressDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.os.AsyncTask;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.CheckBox;
import android.widget.TextView;
import dk.silverbullet.telemed.OpenTeleApplication;
import dk.silverbullet.telemed.device.AmbiguousDeviceException;
import dk.silverbullet.telemed.device.BluetoothDisabledException;
import dk.silverbullet.telemed.device.BluetoothNotAvailableException;
import dk.silverbullet.telemed.device.DeviceInitialisationException;
import dk.silverbullet.telemed.device.monica.MonicaDeviceController;
import dk.silverbullet.telemed.questionnaire.MainQuestionnaire;
import dk.silverbullet.telemed.questionnaire.Questionnaire;
import dk.silverbullet.telemed.questionnaire.R;
import dk.silverbullet.telemed.questionnaire.node.IONode;
import dk.silverbullet.telemed.questionnaire.node.monica.DeviceState;
import dk.silverbullet.telemed.questionnaire.node.monica.MonicaDeviceCallback;
import dk.silverbullet.telemed.questionnaire.node.monica.SimulatedMonicaDevice;
import dk.silverbullet.telemed.questionnaire.node.monica.realtime.communicators.CommunicatorFactory;
import dk.silverbullet.telemed.utils.Util;
import java.util.Date;
import java.util.UUID;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
public class RealTimeCTGNode extends IONode implements MonicaDeviceCallback {
private static final String TAG = Util.getTag(RealTimeCTGNode.class);
private MonicaDeviceController device;
private CheckBox buttonOrange;
private CheckBox buttonWhite;
private CheckBox buttonGreen;
private CheckBox buttonBlack;
private CheckBox buttonYellow;
private BlockingQueue<RealTimeCTGMessage> measurementsQueue = new ArrayBlockingQueue<RealTimeCTGMessage>(50000);
private RealtimeCTGMeasurementsExportWorker worker;
private int sampleCount = 0;
private boolean simulate = false;
private DeviceState currentState;
private TextView statusTextView;
private String deviceName;
private SimulatedMonicaDevice simulatedDevice;
private ProgressDialog progress;
private boolean isStopping, hasStopped;
private StopMeasurementsAsyncTask waitForEmptyQueue;
private PatientInfo patientInfo;
private UUID registrationIdentifier;
public RealTimeCTGNode(Questionnaire questionnaire, String nodeName) {
super(questionnaire, nodeName);
}
@Override
public void enter() {
clearElements();
inflateLayout();
ViewGroup rootLayout = questionnaire.getRootLayout();
linkTopPanel(rootLayout);
hideBackButton();
super.enter();
}
private void inflateLayout() {
Context context = questionnaire.getContext();
ViewGroup rootLayout = questionnaire.getRootLayout();
LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
View topView = inflater.inflate(R.layout.realtime_ctg_node, null);
statusTextView = (TextView) topView.findViewById(R.id.realtime_ctg_current_status);
rootLayout.removeAllViews();
rootLayout.addView(topView);
topView.findViewById(R.id.realtime_ctg_start_measurement).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
startMeasurement();
}
});
topView.findViewById(R.id.realtime_ctg_stop_measurement).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
stopMeasurement();
}
});
resolveViews(topView);
}
private void resolveViews(View parentView) {
buttonOrange = (CheckBox) parentView.findViewById(R.id.buttonOrange);
buttonWhite = (CheckBox) parentView.findViewById(R.id.buttonWhite);
buttonGreen = (CheckBox) parentView.findViewById(R.id.buttonGreen);
buttonBlack = (CheckBox) parentView.findViewById(R.id.buttonBlack);
buttonYellow = (CheckBox) parentView.findViewById(R.id.buttonYellow);
}
private void startMeasurement() {
questionnaire.getActivity().findViewById(R.id.realtime_ctg_stop_measurement).setVisibility(View.VISIBLE);
questionnaire.getActivity().findViewById(R.id.realtime_ctg_start_measurement).setVisibility(View.INVISIBLE);
initializeDevice();
setupMeasurementExportWorker();
}
private void initializeDevice() {
if(simulate) {
simulatedDevice = new SimulatedMonicaDevice(this);
this.deviceName = "silverbullet-monica-test";
} else {
try {
device = new MonicaDeviceController(this, questionnaire.getContext());
this.deviceName = device.getDeviceName();
} catch (BluetoothDisabledException e) {
OpenTeleApplication.instance().logException(e);
abort(Util.getString(R.string.realtime_monica_bluetooth_off, questionnaire));
} catch (BluetoothNotAvailableException e) {
OpenTeleApplication.instance().logException(e);
abort(Util.getString(R.string.realtime_monica_bluetooth_unavaliable, questionnaire));
} catch (AmbiguousDeviceException e) {
OpenTeleApplication.instance().logException(e);
abort(Util.getString(R.string.realtime_monica_multiple_devices, questionnaire));
} catch (DeviceInitialisationException e) {
OpenTeleApplication.instance().logException(e);
abort(Util.getString(R.string.realtime_monica_failed_to_start, questionnaire));
}
}
}
private void setupMeasurementExportWorker() {
patientInfo = new PatientInfo();
patientInfo.id = questionnaire.getUserId();
String name = questionnaire.getFullName();
int lastNameStartIndex = name.lastIndexOf(" ");
patientInfo.lastName = name.substring(lastNameStartIndex, name.length());
patientInfo.firstName = name.substring(0, lastNameStartIndex);
registrationIdentifier = UUID.randomUUID();
new setupWorkerAsyncTask().execute();
}
private void stopMeasurement() {
this.questionnaire.getActivity().runOnUiThread(new Runnable() {
@Override
public void run() {
statusTextView.setText(Util.getString(R.string.realtime_ctg_stopping, questionnaire));
}
});
//Show dialog
showStoppingDialog();
isStopping = true;
waitForEmptyQueue = new StopMeasurementsAsyncTask();
waitForEmptyQueue.execute();
}
private class StopMeasurementsAsyncTask extends AsyncTask<Void, Void, Void> {
@Override
protected Void doInBackground(Void... voids) {
isStopping = true;
closeDevice();
measurementsQueue.add(new StopMessage()); //Tells the worker thread to stop working when it gets to this message
while(measurementsQueue.size() > 0 && worker.isRunning()) {
Log.d(TAG, "waiting for empty queue:" + measurementsQueue.size() + " and worker thread stop:" + worker.isRunning());
} //This wont run indefinitely. If the worker thread cant send the measurements it will call abort
hasStopped = true;
return null;
}
@Override
protected void onPostExecute(Void aVoid) {
hideStoppingDialog();
//show dialog
returnToMainMenu();
}
}
private void closeDevice() {
if(device != null) {
device.close();
} else if(simulatedDevice != null) {
simulatedDevice.close();
}
}
private void returnToMainMenu() {
questionnaire.getActivity().runOnUiThread(new Runnable() {
@Override
public void run() {
getQuestionnaire().setCurrentNode(MainQuestionnaire.getInstance().getMainMenu());
}
});
}
private void hideStoppingDialog() {
questionnaire.getActivity().runOnUiThread(new Runnable() {
@Override
public void run() {
if(progress != null && progress.isShowing()) {
progress.hide();
}
}
});
}
private void showStoppingDialog() {
questionnaire.getActivity().runOnUiThread(new Runnable() {
@Override
public void run() {
progress = new ProgressDialog(questionnaire.getActivity());
progress.setTitle(questionnaire.getActivity().getString(R.string.realtime_ctg_stopping));
progress.setMessage(questionnaire.getActivity().getString(R.string.realtime_ctg_stopping_detail));
progress.setCancelable(false);
progress.show();
}
});
}
private void showStartingDialog() {
questionnaire.getActivity().runOnUiThread(new Runnable() {
@Override
public void run() {
progress = new ProgressDialog(questionnaire.getActivity());
progress.setTitle(questionnaire.getActivity().getString(R.string.realtime_ctg_starting));
progress.setMessage(questionnaire.getActivity().getString(R.string.realtime_ctg_starting_detail));
progress.setCancelable(false);
progress.show();
}
});
}
private void hideStartingDialog() {
questionnaire.getActivity().runOnUiThread(new Runnable() {
@Override
public void run() {
if(progress != null && progress.isShowing()) {
progress.hide();
}
}
});
}
private class setupWorkerAsyncTask extends AsyncTask<Void, Void, Void> {
@Override
protected void onPreExecute() {
super.onPreExecute();
showStartingDialog();
}
@Override
protected Void doInBackground(Void... voids) {
worker = new RealtimeCTGMeasurementsExportWorker(CommunicatorFactory.getCommunicator(questionnaire), measurementsQueue, patientInfo, registrationIdentifier, RealTimeCTGNode.this);
return null;
}
@Override
protected void onPostExecute(Void result) {
super.onPostExecute(result);
hideStartingDialog();
worker.start();
}
}
@Override
public void addSamples(float[] mhr, float[] fhr, int[] qfhr, float[] toco, Date readTime) {
Log.d(TAG, "Got sample");
sampleCount++;
boolean didAddMessage = measurementsQueue.offer(new SampleMessage(mhr, fhr, qfhr, toco, sampleCount, readTime));
if(!didAddMessage) {
abort(Util.getString(R.string.realtime_ctg_stopped_network_error, questionnaire.getContext()));
OpenTeleApplication.instance().logMessage("Aborted Realtime-ctg due to overflow of measurementsQueue");
}
}
@Override
public void updateProgress(int i, int samples) {
this.questionnaire.getActivity().runOnUiThread(new Runnable() {
@Override
public void run() {
statusTextView.setText(Util.getString(R.string.monica_receiving_data, questionnaire));
}
});
}
@Override
public void setProbeState(final boolean orange, final boolean white, final boolean green, final boolean black, final boolean yellow) {
questionnaire.getActivity().runOnUiThread(new Runnable() {
@Override
public void run() {
buttonOrange.setChecked(orange);
buttonWhite.setChecked(white);
buttonGreen.setChecked(green);
buttonBlack.setChecked(black);
buttonYellow.setChecked(yellow);
}
});
}
@Override
public void setState(DeviceState state) {
if (currentState == state) {
return;
}
currentState = state;
final String text;
switch (currentState) {
case WAITING_FOR_CONNECTION:
text = Util.getString(R.string.monica_waiting_for_connection, questionnaire);
break;
case CHECKING_STARTING_CONDITION:
text = Util.getString(R.string.monica_checking_connection, questionnaire);
break;
case WAITING_FOR_DATA:
text = Util.getString(R.string.monica_waiting_for_data, questionnaire);
break;
case RECEIVING_DATA:
text = Util.getString(R.string.monica_receiving_data, questionnaire);
break;
case CLOSING:
case PROCESSING_DATA:
text = Util.getString(R.string.monica_closing, questionnaire);
break;
default:
text = "";
}
questionnaire.getActivity().runOnUiThread(new Runnable() {
@Override
public void run() {
statusTextView.setText(text);
}
});
}
@Override
public void abort(final String reason) {
if(hasStopped) {
return;
}
if(isStopping) { //Error occurred while trying to stop the worker.
if(waitForEmptyQueue != null && !waitForEmptyQueue.isCancelled()) {
waitForEmptyQueue.cancel(true);
}
hideStoppingDialog();
hasStopped = true;
returnToMainMenu();
closeDevice();
return;
}
this.questionnaire.getActivity().runOnUiThread(new Runnable() {
@Override
public void run() {
showDialog(reason);
statusTextView.setText(Util.getString(R.string.realtime_ctg_stopped_network_error, questionnaire));
}
});
closeDevice();
}
private void showDialog(String reason) {
AlertDialog alertDialog = new AlertDialog.Builder(this.questionnaire.getActivity()).create();
alertDialog.setMessage(reason);
alertDialog.setButton(AlertDialog.BUTTON_NEUTRAL, Util.getString(R.string.default_ok, questionnaire.getActivity()), new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialogInterface, int i) {
getQuestionnaire().setCurrentNode(MainQuestionnaire.getInstance().getMainMenu());
}
});
alertDialog.show();
}
@Override
public void done(String message) {
//Called in error MonicaDeviceController in error siutations
abort(message);
}
@Override
public void done() {}
@Override
public void setStartVoltage(float voltage) {}
@Override
public void setEndVoltage(float voltage) {}
@Override
public void setStartTimeValue(Date dateTime) {}
@Override
public void setEndTimeValue(Date dateTime) {}
@Override
public void addSignal(Date dateTime) {
measurementsQueue.add(new SignalMessage(dateTime));
}
@Override
public void setDeviceIdString(String deviceId) {}
@Override
public int getSampleTimeMinutes() {
return 43200; //One month should be sufficient
}
@Override
public Date getStartTimeValue() {
return null;
}
@Override
public void addFetalHeight(int fetalHeight) {}
@Override
public void addSignalToNoise(int signalToNoise) {}
public String getDeviceName() {
return this.deviceName;
}
public void connectionProblems() {
abort(Util.getString(R.string.realtime_ctg_stopped_network_error, questionnaire));
}
}