Added ExportData function, and fixed issue with fault showing for HR and O2sat.

This commit is contained in:
Graham Jones
2023-06-03 19:30:04 +01:00
parent 36ba4e2f77
commit 0a57ff0ae0
9 changed files with 276 additions and 28 deletions

View File

@@ -1,22 +1,57 @@
package uk.org.openseizuredetector;
import android.app.Activity;
import android.app.DatePickerDialog;
import android.app.TimePickerDialog;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.database.Cursor;
import android.database.SQLException;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.Handler;
import androidx.appcompat.app.AppCompatActivity;
import android.os.Bundle;
import android.os.IBinder;
import android.provider.DocumentsContract;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.DatePicker;
import android.widget.EditText;
import android.widget.TimePicker;
import android.os.ParcelFileDescriptor;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Calendar;
import java.util.Date;
public class ExportDataActivity extends AppCompatActivity
implements View.OnClickListener {
implements View.OnClickListener {
public interface BooleanCallback {
void accept(Boolean retVal);
}
String TAG = "ExportDataActivity";
// Request code for creating a PDF document.
private static final int FILE_REQUEST_CODE = 1353;
Button mDateBtn;
Button mTimeBtn;
Button mExportBtn;
@@ -24,6 +59,8 @@ public class ExportDataActivity extends AppCompatActivity
EditText mTimeTxt;
EditText mDurationTxt;
Date mEndDate;
int mYear;
int mMonth;
int mDay;
@@ -34,6 +71,10 @@ public class ExportDataActivity extends AppCompatActivity
OsdUtil mUtil;
Handler mHandler;
SdServiceConnection mConnection;
boolean mConnected;
LogManager mLm;
@Override
protected void onCreate(Bundle savedInstanceState) {
@@ -43,15 +84,16 @@ public class ExportDataActivity extends AppCompatActivity
mHandler = new Handler();
mUtil = new OsdUtil(this, mHandler);
mDateBtn = (Button)findViewById(R.id.dateBtn);
mDateBtn = (Button) findViewById(R.id.dateBtn);
mDateBtn.setOnClickListener(this);
mTimeBtn = (Button)findViewById(R.id.timeBtn);
mTimeBtn = (Button) findViewById(R.id.timeBtn);
mTimeBtn.setOnClickListener(this);
mExportBtn = (Button)findViewById(R.id.exportBtn);
mExportBtn = (Button) findViewById(R.id.exportBtn);
mExportBtn.setOnClickListener(this);
mDateTxt = (EditText)findViewById(R.id.endDateText);
mTimeTxt = (EditText)findViewById(R.id.endTimeText);
mDurationTxt = (EditText)findViewById(R.id.durationText);
mExportBtn.setEnabled(false);
mDateTxt = (EditText) findViewById(R.id.endDateText);
mTimeTxt = (EditText) findViewById(R.id.endTimeText);
mDurationTxt = (EditText) findViewById(R.id.durationText);
// Get Current Date
final Calendar c = Calendar.getInstance();
@@ -61,11 +103,64 @@ public class ExportDataActivity extends AppCompatActivity
mHour = c.get(Calendar.HOUR_OF_DAY);
mMinute = c.get(Calendar.MINUTE);
mDateTxt.setText(String.format("%02d-%02d-%04d",mDay, mMonth+1, mYear));
mDateTxt.setText(String.format("%02d-%02d-%04d", mDay, mMonth + 1, mYear));
mTimeTxt.setText(String.format("%02d:%02d:%02d", mHour, mMinute, 00));
mDuration = 2.0;
mDurationTxt.setText(String.format("%03.1f", mDuration));
mConnection = new SdServiceConnection(getApplicationContext());
mConnected = false;
}
@Override
public void onStart() {
super.onStart();
mUtil.bindToServer(getApplicationContext(), mConnection);
waitForConnection();
}
@Override
protected void onStop() {
Log.v(TAG, "onStop()");
super.onStop();
mUtil.unbindFromServer(getApplicationContext(), mConnection);
}
private void waitForConnection() {
// We want the UI to update as soon as it is displayed, but it takes a finite time for
// the mConnection to bind to the service, so we delay half a second to give it chance
// to connect before trying to update the UI for the first time (it happens again periodically using the uiTimer)
if (mConnection.mBound) {
Log.v(TAG, "waitForConnection - Bound!");
initialiseServiceConnection();
} else {
Log.v(TAG, "waitForConnection - waiting...");
new Handler().postDelayed(new Runnable() {
@Override
public void run() {
waitForConnection();
}
}, 100);
}
}
// FIXME - for some reason this never gets called, which is why we have the 'waitForConnection()'
// function that polls the connection until it is connected.
public void onServiceConnected(ComponentName name, IBinder service) {
Log.w(TAG, "onServiceConnected()");
initialiseServiceConnection();
}
private void initialiseServiceConnection() {
mConnected = true;
mExportBtn.setEnabled(true);
mLm = mConnection.mSdServer.mLm;
//mUtil.showToast("Connected!!");
}
@Override
@@ -80,7 +175,7 @@ public class ExportDataActivity extends AppCompatActivity
mDay = dayOfMonth;
mMonth = monthOfYear;
mYear = year;
mDateTxt.setText(String.format("%02d-%02d-%04d",mDay, mMonth+1, mYear));
mDateTxt.setText(String.format("%02d-%02d-%04d", mDay, mMonth + 1, mYear));
}
}, mYear, mMonth, mDay);
datePickerDialog.show();
@@ -100,14 +195,133 @@ public class ExportDataActivity extends AppCompatActivity
timePickerDialog.show();
}
if (view == mExportBtn) {
mDateTxt.setText(String.format("%02d-%02d-%04d",mDay, mMonth+1, mYear));
mDateTxt.setText(String.format("%02d-%02d-%04d", mDay, mMonth + 1, mYear));
mTimeTxt.setText(String.format("%02d:%02d:%02d", mHour, mMinute, 00));
mDuration = Double.parseDouble(mDurationTxt.getText().toString());
String dateTimeStr = String.format("%04d-%02d-%02dT%02d:%02d:%02dZ", mYear, mMonth + 1, mDay, mHour, mMinute, 00);
//mUtil.showToast(dateTimeStr);
mEndDate = mUtil.string2date(dateTimeStr);
//mUtil.showToast(mEndDate.toString());
mUtil.showToast(String.format("EndDate=%s %s, Duration=%3.1f hrs",
mDateTxt.getText().toString(), mTimeTxt.getText().toString() ,mDuration));
//mUtil.showToast(String.format("EndDate=%s %s, Duration=%3.1f hrs",
// mDateTxt.getText().toString(), mTimeTxt.getText().toString(), mDuration));
Log.d(TAG, String.format("EndDate=%s %s, Duration=%3.1f hrs",
mDateTxt.getText().toString(), mTimeTxt.getText().toString(), mDuration));
this.openFile();
}
}
private void openFile() {
Intent intent = new Intent(Intent.ACTION_CREATE_DOCUMENT);
intent.addCategory(Intent.CATEGORY_OPENABLE);
intent.setType("text/csv");
intent.putExtra(Intent.EXTRA_TITLE, "osd_data.csv");
//intent.putExtra(DocumentsContract.EXTRA_INITIAL_URI, pickerInitialUri);
Log.v(TAG, "openFile() - showing open dialog");
startActivityForResult(intent, FILE_REQUEST_CODE);
}
// Called when the file picker created in openFile() is closed.
@Override
public void onActivityResult(int requestCode, int resultCode,
Intent resultData) {
Log.v(TAG, "onActivityResult - requestCode=" + requestCode);
if (requestCode == FILE_REQUEST_CODE
&& resultCode == Activity.RESULT_OK) {
// The result data contains a URI for the document or directory that
// the user selected.
Uri uri = null;
if (resultData != null) {
uri = resultData.getData();
// Perform operations on the document using its URI.
//mUtil.showToast("URI="+uri.toString());
Log.v(TAG, "onActivityResult() - exporting to file " + uri.toString());
exportToFile(uri);
}
}
super.onActivityResult(requestCode, resultCode, resultData);
}
private void exportToFile(Uri uri) {
Log.v(TAG, "exportToFile(): uri=" + uri.toString());
long endDateMillis = mEndDate.getTime();
long durationMillis = (long) (mDuration * 3600. * 1000);
long startDateMillis = endDateMillis - durationMillis;
Log.v(TAG, "exportToFile() - endDateMillis=" + endDateMillis + ", startDateMillis=" + startDateMillis + ", durationMillis=" + durationMillis);
DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
String sDateStr = dateFormat.format(new Date(startDateMillis));
String eDateStr = dateFormat.format(new Date(endDateMillis));
Log.v(TAG, "exportToFile() - sDateStr=" + sDateStr + " eDateStr=" + eDateStr);
mLm.getDatapointsByDate(
sDateStr, eDateStr, (String datapointsJsonStr) -> {
Log.v(TAG, "exportToFile() - datapoints=" + datapointsJsonStr);
// Open file for writing
try {
ParcelFileDescriptor pfd = this.getContentResolver().
openFileDescriptor(uri, "w");
FileOutputStream fileOutputStream =
new FileOutputStream(pfd.getFileDescriptor());
// fileOutputStream.write(("Overwritten at " + System.currentTimeMillis() +
// "\n").getBytes());
JSONArray dataObj;
try {
dataObj = new JSONArray(datapointsJsonStr);
Log.v(TAG, "exportToFile() - dataObj length=" + dataObj.length());
for (int i = 0; i < dataObj.length(); i++) {
JSONObject datapointJsonObj = dataObj.getJSONObject(i);
String dataJsonStr = datapointJsonObj.getString("dataJSON");
Log.v(TAG, "exportToFile() - i=" + i + "dataJsonStr=" + dataJsonStr);
JSONObject dataJsonObj = new JSONObject(dataJsonStr);
JSONArray rawDataArr = dataJsonObj.getJSONArray("rawData");
try {
fileOutputStream.write(dataJsonObj.getString("dataTime").getBytes(StandardCharsets.UTF_8));
fileOutputStream.write(", ".getBytes(StandardCharsets.UTF_8));
fileOutputStream.write(dataJsonObj.getString("alarmState").getBytes(StandardCharsets.UTF_8));
fileOutputStream.write(", ".getBytes(StandardCharsets.UTF_8));
fileOutputStream.write(dataJsonObj.getString("hr").getBytes(StandardCharsets.UTF_8));
fileOutputStream.write(", ".getBytes(StandardCharsets.UTF_8));
fileOutputStream.write(dataJsonObj.getString("o2Sat").getBytes(StandardCharsets.UTF_8));
for (int j = 0; j < rawDataArr.length(); j++) {
fileOutputStream.write(", ".getBytes(StandardCharsets.UTF_8));
fileOutputStream.write(rawDataArr.getString(j).getBytes(StandardCharsets.UTF_8));
}
fileOutputStream.write("\n".getBytes(StandardCharsets.UTF_8));
} catch (IOException e) {
Log.e(TAG, "exportToFile() - ERROR Writing File: " + e.toString());
//mUtil.showToast("ERROR WRITING FILE");
}
}
} catch (JSONException | NullPointerException e) {
Log.v(TAG, "createEventCallback(): Error Creating JSON Object from string " + datapointsJsonStr);
dataObj = null;
mUtil.showToast(getString(R.string.error_exporting_data));
Log.e(TAG, "exportToFile() - JSONException: " + e.toString());
}
// Let the document provider know you're done by closing the stream.
fileOutputStream.close();
pfd.close();
mUtil.showToast(getString(R.string.data_exported_ok));
} catch (FileNotFoundException e) {
e.printStackTrace();
mUtil.showToast(getString(R.string.error_exporting_data));
Log.e(TAG, "exportToFile() - FileNotFoundException: " + e.toString());
} catch (IOException e) {
e.printStackTrace();
mUtil.showToast(getString(R.string.error_exporting_data));
Log.e(TAG, "exportToFile() - IOException: " + e.toString());
}
});
}
}

View File

@@ -200,7 +200,7 @@ public class LogManagerControlActivity extends AppCompatActivity {
final CheckBox includeWarningsCb = (CheckBox) findViewById(R.id.include_warnings_cb);
final CheckBox includeNDACb = (CheckBox) findViewById(R.id.include_nda_cb);
getRemoteEvents(includeWarningsCb.isChecked(), includeNDACb.isChecked());
ProgressBar pb = (ProgressBar)findViewById(R.id.remoteAccessPb);
ProgressBar pb = (ProgressBar) findViewById(R.id.remoteAccessPb);
pb.setIndeterminate(true);
pb.setVisibility(View.VISIBLE);
// Populate events list - we only do it once when the activity is created because the query might slow down the UI.
@@ -269,11 +269,11 @@ public class LogManagerControlActivity extends AppCompatActivity {
eventHashMap.put("type", typeStr);
eventHashMap.put("subType", subType);
eventHashMap.put("desc", desc);
if ((osdAlarmState!=1 | includeWarnings) &&
(osdAlarmState!=6 | includeNDA)) {
if ((osdAlarmState != 1 | includeWarnings) &&
(osdAlarmState != 6 | includeNDA)) {
mRemoteEventsList.add(eventHashMap);
} else {
Log.v(TAG,"getRemoteEvents - skipping warning or NDA record");
Log.v(TAG, "getRemoteEvents - skipping warning or NDA record");
}
}
Log.v(TAG, "getRemoteEvents() - set mRemoteEventsList(). Updating UI");
@@ -304,9 +304,9 @@ public class LogManagerControlActivity extends AppCompatActivity {
TextView tv2 = (TextView) findViewById(R.id.num_local_datapoints_tv);
tv2.setText(String.format("%d", datapointsCount));
});
TextView tv3 = (TextView)findViewById(R.id.nda_time_remaining_tv);
tv3.setText(String.format("%.1f hrs",mLm.mNDATimeRemaining));
Log.d(TAG,"mNDATimeRemaining = "+String.format("%.1f hrs",mLm.mNDATimeRemaining));
TextView tv3 = (TextView) findViewById(R.id.nda_time_remaining_tv);
tv3.setText(String.format("%.1f hrs", mLm.mNDATimeRemaining));
Log.d(TAG, "mNDATimeRemaining = " + String.format("%.1f hrs", mLm.mNDATimeRemaining));
} else {
stopUpdating = false;
}
@@ -333,7 +333,7 @@ public class LogManagerControlActivity extends AppCompatActivity {
}
// Remote Database List View
if (mRemoteEventsList != null) {
ProgressBar pb = (ProgressBar)findViewById(R.id.remoteAccessPb);
ProgressBar pb = (ProgressBar) findViewById(R.id.remoteAccessPb);
pb.setIndeterminate(false);
pb.setVisibility(View.INVISIBLE);
ListView lv = (ListView) findViewById(R.id.remoteEventsLv);
@@ -453,7 +453,7 @@ public class LogManagerControlActivity extends AppCompatActivity {
}
return true;
case R.id.start_stop_nda:
Log.i(TAG,"start/stop NDA");
Log.i(TAG, "start/stop NDA");
if (mConnection.mSdServer.mLogNDA) {
new AlertDialog.Builder(this)
.setTitle(R.string.stop_nda_logging_dialog_title)
@@ -475,7 +475,6 @@ public class LogManagerControlActivity extends AppCompatActivity {
})
.setNegativeButton(android.R.string.no, null)
.show();
} else {
new AlertDialog.Builder(this)
.setTitle(R.string.start_nda_logging_dialog_title)
@@ -499,7 +498,6 @@ public class LogManagerControlActivity extends AppCompatActivity {
.show();
}
return true;
case R.id.action_mark_unknown:
Log.i(TAG, "action_mark_unknown");
@@ -527,6 +525,18 @@ public class LogManagerControlActivity extends AppCompatActivity {
})
.setNegativeButton(android.R.string.no, null)
.show();
case R.id.export_data_menuitem:
Log.i(TAG, "export data menu item");
try {
Intent i = new Intent(
getApplicationContext(),
ExportDataActivity.class);
this.startActivity(i);
} catch (Exception ex) {
Log.i(TAG, "exception starting export data activity " + ex.toString());
}
return true;
default:
return super.onOptionsItemSelected(item);
}
@@ -551,7 +561,7 @@ public class LogManagerControlActivity extends AppCompatActivity {
// Confirmation dialog based on: https://stackoverflow.com/a/12213536/2104584
AlertDialog.Builder builder = new AlertDialog.Builder(mContext);
builder.setTitle(R.string.prune_database_title);
builder.setMessage(String.format(getString(R.string.prune_database_dialog_msg) , mLm.mDataRetentionPeriod));
builder.setMessage(String.format(getString(R.string.prune_database_dialog_msg), mLm.mDataRetentionPeriod));
builder.setPositiveButton(R.string.yes_button_title, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {

View File

@@ -617,6 +617,12 @@ public abstract class SdDataSource {
mSdData.alarmState = 2;
}
}
} else {
mSdData.mHRFaultStanding = false;
mSdData.mHRAlarmStanding = false;
mSdData.mAdaptiveHRAlarmStanding = false;
mSdData.mAverageHRAlarmStanding = false;
}
}
@@ -646,6 +652,9 @@ public abstract class SdDataSource {
mSdData.mO2SatFaultStanding = false;
mSdData.mO2SatAlarmStanding = false;
}
} else {
mSdData.mO2SatFaultStanding = false;
mSdData.mO2SatAlarmStanding = false;
}
}