Added logging to local database

This commit is contained in:
Graham Jones
2019-11-10 22:03:34 +00:00
parent 384bb7c9a3
commit 14fce1b6e1
8 changed files with 284 additions and 11 deletions

View File

@@ -1,8 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="uk.org.openseizuredetector"
android:versionCode="65"
android:versionName="3.1.11"
android:versionCode="66"
android:versionName="3.2.0"
>
<!--android:allowBackup="false"-->

View File

@@ -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 <http://www.gnu.org/licenses/>.
*/
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);
}
}
}

View File

@@ -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;
}

View File

@@ -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", "<username>");
Log.v(TAG, "updatePrefs() - mOSDUname = " + mOSDUname);
mOSDPasswd = SP.getString("OSDPasswd", "<passwd>");
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.

View File

@@ -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"

View File

@@ -14,10 +14,41 @@
android:summary="Log Alarm events to SD Card"
android:title="Log Alarm events to SD Card" />
<CheckBoxPreference
android:defaultValue="false"
android:defaultValue="true"
android:key="LogData"
android:summary="Log Data to SD Card Regularly"
android:title="Log Data to SD Card" />
<CheckBoxPreference
android:defaultValue="false"
android:key="LogDataRemote"
android:summary="Log Data to Central OpenSeizureDetector Database"
android:title="Log Data Remotely" />
<CheckBoxPreference
android:defaultValue="false"
android:key="LogDataRemoteMobile"
android:summary="Use mobile internet to log remote data"
android:title="Use Mobile Internet" />
<EditTextPreference
android:defaultValue="user_name"
android:key="OSDUname"
android:summary="Username for remote data logging."
android:title="Remote Username" />
<EditTextPreference
android:defaultValue="password"
android:key="OSDPasswd"
android:summary="Password for remote data logging."
android:title="Remote Password" />
<EditTextPreference
android:defaultValue="0"
android:key="OSDWearerId"
android:summary="Wearer ID"
android:title="Wearer ID of the person wearing the watch (from OSD Web API)" />
<EditTextPreference
android:defaultValue="http://openseizuredetector.org.uk/webApi/"
android:key="OSDUrl"
android:summary="URL for remote data logging."
android:title="Remote URL" />
<CheckBoxPreference
android:defaultValue="false"
android:key="PreventSleep"

View File

@@ -9,7 +9,7 @@ buildscript {
google()
}
dependencies {
classpath 'com.android.tools.build:gradle:3.5.1'
classpath 'com.android.tools.build:gradle:3.5.2'
}
}
allprojects {

13
getDb.sh Executable file
View File

@@ -0,0 +1,13 @@
#!/bin/sh
# This script copies the osdData database off the connected phone to a file called osdData.db in the current
# working directory.
# from https://stackoverflow.com/a/30377688
#
adb shell run-as uk.org.openseizuredetector chmod 777 /data/data/uk.org.openseizuredetector/databases
adb shell run-as uk.org.openseizuredetector chmod 777 /data/data/uk.org.openseizuredetector/databases/OsdData.db
adb shell run-as uk.org.openseizuredetector cp /data/data/uk.org.openseizuredetector/databases/OsdData.db /sdcard
adb pull /sdcard/OsdData.db ./OsdData.db
adb shell rm /sdcard/OsdData.db