diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index ebeda02..4dc780f 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -1,8 +1,8 @@ diff --git a/app/src/main/java/uk/org/openseizuredetector/LogManager.java b/app/src/main/java/uk/org/openseizuredetector/LogManager.java new file mode 100644 index 0000000..2ddc211 --- /dev/null +++ b/app/src/main/java/uk/org/openseizuredetector/LogManager.java @@ -0,0 +1,173 @@ +/* + Android_SD - Android host for Garmin or Pebble watch based seizure detectors. + See http://openseizuredetector.org for more information. + + Copyright Graham Jones, 2019. + + This file is part of Android_SD. + + Android_SD is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Android_SD is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Android_SD. If not, see . + +*/ +package uk.org.openseizuredetector; + +import android.content.Context; +import android.database.Cursor; +import android.database.DatabaseUtils; +import android.database.SQLException; +import android.database.sqlite.SQLiteDatabase; +import android.database.sqlite.SQLiteOpenHelper; +import android.text.format.Time; +import android.util.Log; + +import static android.database.sqlite.SQLiteDatabase.openOrCreateDatabase; + +/** + * LogManager is a class to handle all aspects of Data Logging within OpenSeizureDetector. + */ +public class LogManager { + private String TAG = "LogManager"; + private String mDbName = "osdData"; + private String mDbTableName = "datapoints"; + private boolean mLogRemote; + private boolean mLogRemoteMobile; + private String mOSDUname; + private String mOSDPasswd; + private int mOSDWearerId; + private String mOSDUrl; + private OsdDbHelper mOSDDb; + private Context mContext; + + public LogManager(boolean logRemote, + boolean logRemoteMobile, + String OSDUname, + String OSDPasswd, + int OSDWearerId, + String OSDUrl, + Context context) { + mLogRemote = logRemote; + mLogRemoteMobile = logRemoteMobile; + mOSDUname = OSDUname; + mOSDPasswd = OSDPasswd; + mOSDWearerId = OSDWearerId; + mOSDUrl = OSDUrl; + mContext = context; + + try { + mOSDDb = new OsdDbHelper(mDbTableName, mContext); + if (!checkTableExists(mOSDDb, mDbTableName)) { + Log.e(TAG,"ERROR - Table does not exist"); + } + } catch (SQLException e) { + Log.e(TAG, "Failed to open Database: " + e.toString()); + } + } + + + private boolean checkTableExists(OsdDbHelper osdDb, String osdTableName) { + Cursor c = null; + boolean tableExists = false; + try { + c = osdDb.getWritableDatabase().query(osdTableName, null, + null, null, null, null, null); + tableExists = true; + } + catch (Exception e) { + Log.d(TAG, osdTableName+" doesn't exist :((("); + } + return tableExists; + } + + + /** + * Write data to local database + * FIXME - I am sure we should not be using raw SQL Srings to do this! + */ + public void writeToLocalDb(SdData sdData) { + Log.v(TAG, "writeToLocalDb()"); + Time tnow = new Time(Time.getCurrentTimezone()); + tnow.setToNow(); + String dateStr = tnow.format("%Y-%m-%d"); + String SQLStr = "SQLStr"; + + try { + SQLStr = "INSERT INTO "+ mDbTableName + + "(dataTime, wearer_id, BattPC, specPow, roiRatio, avAcc, sdAcc, hr, status, dataJSON, uploaded)" + + " VALUES(" + +"CURRENT_TIMESTAMP," + + mOSDWearerId + "," + + sdData.batteryPc + "," + + sdData.specPower + "," + + 10. * sdData.roiPower / sdData.specPower + "," + + sdData.getAvAcc() + "," + + sdData.getSdAcc() + "," + + sdData.mHR + "," + + sdData.alarmState + "," + + DatabaseUtils.sqlEscapeString(sdData.toCSVString(true)) + "," + + 0 + +")"; + mOSDDb.getWritableDatabase().execSQL(SQLStr); + + } catch (SQLException e) { + Log.e(TAG,"writeToLocalDb(): Error Writing Data: " + e.toString()); + Log.e(TAG,"SQLStr was "+SQLStr); + } + + } + + public void close() { + mOSDDb.close(); + } + + public class OsdDbHelper extends SQLiteOpenHelper { + // If you change the database schema, you must increment the database version. + public static final int DATABASE_VERSION = 1; + public static final String DATABASE_NAME = "OsdData.db"; + private String mOsdTableName; + private String TAG = "OsdDbHelper"; + + public OsdDbHelper(String osdTableName, Context context) { + super(context, DATABASE_NAME, null, DATABASE_VERSION); + mOsdTableName = osdTableName; + } + public void onCreate(SQLiteDatabase db) { + Log.v(TAG,"onCreate - TableName="+mOsdTableName); + String SQLStr = "CREATE TABLE IF NOT EXISTS "+mOsdTableName+"(" + + "id INT AUTO_INCREMENT PRIMARY KEY," + + "dataTime DATETIME," + + "wearer_id INT NOT NULL," + + "BattPC FLOAT," + + "specPow FLOAT," + + "roiRatio FLOAT," + + "avAcc FLOAT," + + "sdAcc FLOAT," + + "HR FLOAT," + + "Status INT," + + "dataJSON TEXT," + + "uploaded INT" + + ");"; + + db.execSQL(SQLStr); + } + public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { + // This database is only a cache for online data, so its upgrade policy is + // to simply to discard the data and start over + db.execSQL("Drop table if exists " + mOsdTableName + ";"); + onCreate(db); + } + public void onDowngrade(SQLiteDatabase db, int oldVersion, int newVersion) { + onUpgrade(db, oldVersion, newVersion); + } + } +} diff --git a/app/src/main/java/uk/org/openseizuredetector/SdData.java b/app/src/main/java/uk/org/openseizuredetector/SdData.java index fd701e2..9e293fc 100644 --- a/app/src/main/java/uk/org/openseizuredetector/SdData.java +++ b/app/src/main/java/uk/org/openseizuredetector/SdData.java @@ -225,6 +225,27 @@ public class SdData implements Parcelable { return(retval); } + /** Return the average acceleration value in the dataset */ + public double getAvAcc() { + double sumAcc = 0.0; + for (int i = 0; i< mNsamp;i++) { + sumAcc += rawData[i]; + } + return(sumAcc/mNsamp); + } + + /** Return the standard deviation of the acceleration values */ + public double getSdAcc() { + double avAcc = 0.0; + double varAcc = 0.0; + avAcc = getAvAcc(); + for (int i = 0; i< mNsamp;i++) { + varAcc += Math.pow(rawData[i]-avAcc,2); + } + return(Math.sqrt(varAcc/(mNsamp-1))); + } + + public int describeContents() { return 0; } diff --git a/app/src/main/java/uk/org/openseizuredetector/SdServer.java b/app/src/main/java/uk/org/openseizuredetector/SdServer.java index 385e922..cd2715d 100644 --- a/app/src/main/java/uk/org/openseizuredetector/SdServer.java +++ b/app/src/main/java/uk/org/openseizuredetector/SdServer.java @@ -43,6 +43,10 @@ import android.content.DialogInterface; import android.content.Intent; import android.content.IntentFilter; import android.content.SharedPreferences; +import android.database.Cursor; +import android.database.DatabaseUtils; +import android.database.SQLException; +import android.database.sqlite.SQLiteDatabase; import android.location.Location; import android.media.AudioManager; import android.media.ToneGenerator; @@ -61,18 +65,15 @@ import android.preference.PreferenceManager; import android.support.v4.app.NotificationCompat; import android.telephony.SmsManager; import android.util.Log; -import android.view.WindowManager; -import android.widget.Toast; import java.util.Timer; -import java.util.TimerTask; import java.io.*; import java.util.*; import android.text.format.Time; import com.rohitss.uceh.UCEHandler; - +import uk.org.openseizuredetector.LogManager; /** * Based on example at: @@ -120,9 +121,17 @@ public class SdServer extends Service implements SdDataReceiver { public Time mSMSTime = null; // last time we sent an SMS Alarm (limited to one per minute) public SmsTimer mSmsTimer = null; // Timer to wait 10 seconds before sending an alert to give the user chance to cancel it. private AlertDialog.Builder mSMSAlertDialog; // Dialog shown during countdown to sending SMS. + + // Data Logging Parameters private boolean mLogAlarms = true; private boolean mLogData = false; - private File mOutFile; + private boolean mLogDataRemote = false; + private boolean mLogDataRemoteMobile = false; + private String mOSDUname = ""; + private String mOSDPasswd = ""; + private int mOSDWearerId = 0; + private String mOSDUrl = ""; + private OsdUtil mUtil; private Handler mHandler; private ToneGenerator mToneGenerator; @@ -131,6 +140,8 @@ public class SdServer extends Service implements SdDataReceiver { private final IBinder mBinder = new SdBinder(); + private LogManager mLm; + /** * class to handle binding the MainApp activity to this service * so it can access mSdData. @@ -203,6 +214,10 @@ public class SdServer extends Service implements SdDataReceiver { Log.v(TAG, "onStartCommand() - calling updatePrefs()"); updatePrefs(); + // Create our log manager. + mLm = new LogManager(mLogDataRemote, mLogDataRemoteMobile, + mOSDUname, mOSDPasswd, mOSDWearerId, mOSDUrl, this); + Log.v(TAG, "onStartCommand: Datasource =" + mSdDataSourceName); switch (mSdDataSourceName) { case "Pebble": @@ -371,6 +386,9 @@ public class SdServer extends Service implements SdDataReceiver { mToneGenerator.release(); mToneGenerator = null; + // Stop the log Manager + mLm.close(); + // stop this service. Log.v(TAG, "onDestroy(): calling stopSelf()"); mUtil.writeToSysLogFile("SdServer.onDestroy() - stopping self"); @@ -997,6 +1015,7 @@ public class SdServer extends Service implements SdDataReceiver { if (mLogData) { Log.v(TAG, "logData() - writing data to SD Card"); writeToSD(); + mLm.writeToLocalDb(mSdData); } } @@ -1053,9 +1072,20 @@ public class SdServer extends Service implements SdDataReceiver { Log.v(TAG, "updatePrefs() - mSMSNumbers = " + mSMSNumbers); mLogAlarms = SP.getBoolean("LogAlarms", true); Log.v(TAG, "updatePrefs() - mLogAlarms = " + mLogAlarms); - mLogData = SP.getBoolean("LogData", false); + mLogData = SP.getBoolean("LogData", true); Log.v(TAG, "updatePrefs() - mLogData = " + mLogData); - + mLogDataRemote = SP.getBoolean("LogDataRemote", false); + Log.v(TAG, "updatePrefs() - mLogDataRemote = " + mLogDataRemote); + mLogDataRemoteMobile = SP.getBoolean("LogDataRemoteMobile", false); + Log.v(TAG, "updatePrefs() - mLogDataRemoteMobile = " + mLogDataRemoteMobile); + mOSDUname = SP.getString("OSDUname", ""); + Log.v(TAG, "updatePrefs() - mOSDUname = " + mOSDUname); + mOSDPasswd = SP.getString("OSDPasswd", ""); + Log.v(TAG, "updatePrefs() - mOSDPasswd = " + mOSDPasswd); + mOSDWearerId = Integer.parseInt(SP.getString("OSDWearerId", "0")); + Log.v(TAG, "updatePrefs() - mOSDWearerId = " + mOSDWearerId); + mOSDUrl = SP.getString("OSDUrl", "http://openseizuredetector.org.uk/webApi"); + Log.v(TAG, "updatePrefs() - mOSDUrl = " + mOSDUrl); } catch (Exception ex) { Log.v(TAG, "updatePrefs() - Problem parsing preferences!"); mUtil.writeToSysLogFile("SdServer.updatePrefs() - Error " + ex.toString()); @@ -1120,6 +1150,9 @@ public class SdServer extends Service implements SdDataReceiver { } + + + /* * Wait a given time, then send an SMS alert - the idea is to give the user time to cancel the * alert if necessary. diff --git a/app/src/main/java/uk/org/openseizuredetector/StartupActivity.java b/app/src/main/java/uk/org/openseizuredetector/StartupActivity.java index cb3dca4..745e59e 100644 --- a/app/src/main/java/uk/org/openseizuredetector/StartupActivity.java +++ b/app/src/main/java/uk/org/openseizuredetector/StartupActivity.java @@ -408,6 +408,7 @@ public class StartupActivity extends Activity { + "http://openseizuredetector.org.uk, or the app Facebook page at https://www.facebook.com/openseizuredetector. " + "so I can get in touch if necessary.\nThank you! Graham \ngraham@openseizuredetector.org.uk " + "\n\nChanges in this version:" + + "\n V3.2.0 - Added remote data logging capability" + "\n V3.1.11 - Fixed issue that Nework data source did not display heart rate data" + "\n V3.1.10 - Provided a user option to treat a null heart rate as a fault or an alarm condition" + "\n V3.1.9 - Fixed issue with Garmin Seizure Detector not producing warnings. Added fault pips for missing heart rate data if heart rate alarm active" @@ -440,6 +441,7 @@ public class StartupActivity extends Activity { + "http://openseizuredetector.org.uk, or the app Facebook page at https://www.facebook.com/openseizuredetector. " + "so I can get in touch if necessary.\nThank you! Graham \ngraham@openseizuredetector.org.uk " + "\n\nChanges in this version:" + + "\n V3.2.0 - Added remote data logging capability" + "\n V3.1.11 - Fixed issue that Nework data source did not display heart rate data" + "\n V3.1.10 - Provided a user option to treat a null heart rate as a fault or an alarm condition" + "\n V3.1.9 - Fixed issue with Garmin Seizure Detector not producing warnings. Added fault pips for missing heart rate data if heart rate alarm active" diff --git a/app/src/main/res/xml/general_prefs.xml b/app/src/main/res/xml/general_prefs.xml index 2a7bffb..7965b12 100644 --- a/app/src/main/res/xml/general_prefs.xml +++ b/app/src/main/res/xml/general_prefs.xml @@ -14,10 +14,41 @@ android:summary="Log Alarm events to SD Card" android:title="Log Alarm events to SD Card" /> + + + + + + +