diff --git a/CHANGELOG.md b/CHANGELOG.md index 6fe69ad..0ab8660 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,7 @@ V2.6.0 - 01 Sep 2017 Added Support for Wifi data source (initially to be used for ESP8266_SD) + and Android Wear datasource V2.5.2 - 09 May 2017 Added support for Pebble App V2.5 which includes a multi-ROI mode to improve sensitivity. diff --git a/app/build.gradle b/app/build.gradle index 7ba1db3..1f72f06 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -7,8 +7,8 @@ android { defaultConfig { applicationId "uk.org.openseizuredetector" - minSdkVersion 14 - targetSdkVersion 14 + minSdkVersion 20 + targetSdkVersion 24 } buildTypes { @@ -38,6 +38,8 @@ dependencies { compile 'org.apache.commons:commons-math3:3.6.1' // google play services used for location finding for SMS alerts. compile 'com.google.android.gms:play-services-location:10.0.0' + compile 'com.google.android.gms:play-services:10.0.1' + } repositories { diff --git a/app/src/main/java/uk/org/openseizuredetector/SdDataSourceAw.java b/app/src/main/java/uk/org/openseizuredetector/SdDataSourceAw.java new file mode 100644 index 0000000..9b9e467 --- /dev/null +++ b/app/src/main/java/uk/org/openseizuredetector/SdDataSourceAw.java @@ -0,0 +1,755 @@ +/* + Android_Pebble_sd - Android alarm client for openseizuredetector.. + + See http://openseizuredetector.org for more information. + + Copyright Graham Jones, 2015, 2016 + + This file is part of pebble_sd. + + Android_Pebble_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_Pebble_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_pebble_sd. If not, see . + +*/ +package uk.org.openseizuredetector; + +import android.content.Context; +import android.content.SharedPreferences; +import android.os.Handler; +import android.preference.PreferenceManager; +import android.text.format.Time; +import android.util.Log; +import android.widget.Toast; + +import com.getpebble.android.kit.PebbleKit; +import com.getpebble.android.kit.util.PebbleDictionary; + +import java.nio.ByteBuffer; +import java.nio.ByteOrder; +import java.nio.IntBuffer; +import java.util.Timer; +import java.util.TimerTask; +import java.util.UUID; +import com.google.android.gms.wearable.MessageEvent; +import com.google.android.gms.wearable.WearableListenerService; + + + +/** + * Abstract class for a seizure detector data source. Subclasses include a pebble smart watch data source and a + * network data source. + */ +public class SdDataSourceAw extends SdDataSource { + private Handler mHandler = new Handler(); + private Timer mSettingsTimer; + private Timer mStatusTimer; + private Time mStatusTime; + private boolean mWatchAppRunningCheck = false; + private int mAppRestartTimeout = 10; // Timeout before re-starting watch app (sec) if we have not received + // data after mDataUpdatePeriod + //private Looper mServiceLooper; + private int mFaultTimerPeriod = 30; // Fault Timer Period in sec + private int mSettingsPeriod = 60; // period between requesting settings in seconds. + private PebbleKit.PebbleDataReceiver msgDataHandler = null; + + + private String TAG = "SdDataSourceAw"; + + private UUID SD_UUID = UUID.fromString("03930f26-377a-4a3d-aa3e-f3b19e421c9d"); + private int NSAMP = 512; // Number of samples in fft input dataset. + + private int KEY_DATA_TYPE = 1; + private int KEY_ALARMSTATE = 2; + private int KEY_MAXVAL = 3; + private int KEY_MAXFREQ = 4; + private int KEY_SPECPOWER = 5; + private int KEY_SETTINGS = 6; + private int KEY_ALARM_FREQ_MIN = 7; + private int KEY_ALARM_FREQ_MAX = 8; + private int KEY_WARN_TIME = 9; + private int KEY_ALARM_TIME = 10; + private int KEY_ALARM_THRESH = 11; + private int KEY_POS_MIN = 12; // position of first data point in array + private int KEY_POS_MAX = 13; // position of last data point in array. + private int KEY_SPEC_DATA = 14; // Spectrum data + private int KEY_ROIPOWER = 15; + private int KEY_NMIN = 16; + private int KEY_NMAX = 17; + private int KEY_ALARM_RATIO_THRESH = 18; + private int KEY_BATTERY_PC = 19; + //private int KEY_SET_SETTINGS =20; // Phone is asking us to update watch app settings. + private int KEY_FALL_THRESH_MIN = 21; + private int KEY_FALL_THRESH_MAX = 22; + private int KEY_FALL_WINDOW = 23; + private int KEY_FALL_ACTIVE = 24; + private int KEY_DATA_UPDATE_PERIOD = 25; + private int KEY_MUTE_PERIOD = 26; + private int KEY_MAN_ALARM_PERIOD = 27; + private int KEY_SD_MODE = 28; + private int KEY_SAMPLE_FREQ = 29; + private int KEY_RAW_DATA = 30; + private int KEY_NUM_RAW_DATA = 31; + private int KEY_DEBUG = 32; + private int KEY_DISPLAY_SPECTRUM = 33; + private int KEY_SAMPLE_PERIOD = 34; + private int KEY_VERSION_MAJOR = 35; + private int KEY_VERSION_MINOR = 36; + private int KEY_FREQ_CUTOFF = 37; + + // Values of the KEY_DATA_TYPE entry in a message + private int DATA_TYPE_RESULTS = 1; // Analysis Results + private int DATA_TYPE_SETTINGS = 2; // Settings + private int DATA_TYPE_SPEC = 3; // FFT Spectrum (or part of a spectrum) + private int DATA_TYPE_RAW = 4; // raw accelerometer data. + + // Values for SD_MODE + private int SD_MODE_FFT = 0; // The original OpenSeizureDetector mode (FFT based) + private int SD_MODE_RAW = 1; // Send raw, unprocessed data to the phone. + private int SD_MODE_FILTER = 2; // Use digital filter rather than FFT. + + private short mDebug; + private short mDisplaySpectrum; + private short mDataUpdatePeriod; + private short mMutePeriod; + private short mManAlarmPeriod; + private short mPebbleSdMode; + private short mSampleFreq; + private short mAlarmFreqMin; + private short mAlarmFreqMax; + private short mSamplePeriod; + private short mWarnTime; + private short mAlarmTime; + private short mAlarmThresh; + private short mAlarmRatioThresh; + private boolean mFallActive; + private short mFallThreshMin; + private short mFallThreshMax; + private short mFallWindow; + + // raw data storage for SD_MODE_RAW + private int MAX_RAW_DATA = 500; + private double[] rawData = new double[MAX_RAW_DATA]; + private int nRawData = 0; + + public SdDataSourceAw(Context context, Handler handler, + SdDataReceiver sdDataReceiver) { + super(context, handler, sdDataReceiver); + mName = "Android Wear"; + // Set default settings from XML files (mContext is set by super(). + PreferenceManager.setDefaultValues(mContext, + R.xml.pebble_datasource_prefs, true); + } + + + /** + * Start the datasource updating - initialises from sharedpreferences first to + * make sure any changes to preferences are taken into account. + */ + public void start() { + Log.v(TAG, "start()"); + mUtil.writeToSysLogFile("SdDataSourceAw.start()"); + updatePrefs(); + startPebbleServer(); + // Start timer to check status of pebble regularly. + mStatusTime = new Time(Time.getCurrentTimezone()); + // use a timer to check the status of the pebble app on the same frequency + // as we get app data. + if (mStatusTimer == null) { + Log.v(TAG, "start(): starting status timer"); + mUtil.writeToSysLogFile("SdDataSourceAw.start() - starting status timer"); + mStatusTimer = new Timer(); + mStatusTimer.schedule(new TimerTask() { + @Override + public void run() { + getPebbleStatus(); + } + }, 0, mDataUpdatePeriod * 1000); + } else { + Log.v(TAG, "start(): status timer already running."); + mUtil.writeToSysLogFile("SdDataSourceAw.start() - status timer already running??"); + } + // make sure we get some data when we first start. + getWatchData(); + // Start timer to retrieve pebble settings regularly. + getWatchSdSettings(); + if (mSettingsTimer == null) { + Log.v(TAG, "start(): starting settings timer"); + mUtil.writeToSysLogFile("SdDataSourceAw.start() - starting settings timer"); + mSettingsTimer = new Timer(); + mSettingsTimer.schedule(new TimerTask() { + @Override + public void run() { + //mUtil.writeToSysLogFile("SdDataSourceAw.mSettingsTimer timed out."); + getWatchSdSettings(); + } + }, 0, 1000 * mSettingsPeriod); // ask for settings less frequently than we get data + } else { + Log.v(TAG, "start(): settings timer already running."); + mUtil.writeToSysLogFile("SdDataSourceAw.start() - settings timer already running??"); + } + } + + /** + * Stop the datasource from updating + */ + public void stop() { + Log.v(TAG, "stop()"); + mUtil.writeToSysLogFile("SdDataSourceAw.stop()"); + try { + // Stop the status timer + if (mStatusTimer != null) { + Log.v(TAG, "stop(): cancelling status timer"); + mUtil.writeToSysLogFile("SdDataSourceAw.stop() - cancelling status timer"); + mStatusTimer.cancel(); + mStatusTimer.purge(); + mStatusTimer = null; + } + // Stop the settings timer + if (mSettingsTimer != null) { + Log.v(TAG, "stop(): cancelling settings timer"); + mUtil.writeToSysLogFile("SdDataSourceAw.stop() - cancelling settings timer"); + mSettingsTimer.cancel(); + mSettingsTimer.purge(); + mSettingsTimer = null; + } + // Stop pebble message handler. + Log.v(TAG, "stop(): stopping pebble server"); + mUtil.writeToSysLogFile("SdDataSourceAw.stop() - stopping pebble server"); + stopPebbleServer(); + + } catch (Exception e) { + Log.v(TAG, "Error in stop() - " + e.toString()); + mUtil.writeToSysLogFile("SdDataSourceAw.stop() - error - "+e.toString()); + } + } + + /** + * updatePrefs() - update basic settings from the SharedPreferences + * - defined in res/xml/SdDataSourceAwPrefs.xml + */ + public void updatePrefs() { + Log.v(TAG, "updatePrefs()"); + mUtil.writeToSysLogFile("SdDataSourceAw.updatePrefs()"); + SharedPreferences SP = PreferenceManager + .getDefaultSharedPreferences(mContext); + try { + // Parse the AppRestartTimeout period setting. + try { + String appRestartTimeoutStr = SP.getString("AppRestartTimeout", "10"); + mAppRestartTimeout = Integer.parseInt(appRestartTimeoutStr); + Log.v(TAG, "updatePrefs() - mAppRestartTimeout = " + mAppRestartTimeout); + } catch (Exception ex) { + Log.v(TAG, "updatePrefs() - Problem with AppRestartTimeout preference!"); + Toast toast = Toast.makeText(mContext, "Problem Parsing AppRestartTimeout Preference", Toast.LENGTH_SHORT); + toast.show(); + } + + // Parse the FaultTimer period setting. + try { + String faultTimerPeriodStr = SP.getString("FaultTimerPeriod", "30"); + mFaultTimerPeriod = Integer.parseInt(faultTimerPeriodStr); + Log.v(TAG, "updatePrefs() - mFaultTimerPeriod = " + mFaultTimerPeriod); + } catch (Exception ex) { + Log.v(TAG, "updatePrefs() - Problem with FaultTimerPeriod preference!"); + Toast toast = Toast.makeText(mContext, "Problem Parsing FaultTimerPeriod Preference", Toast.LENGTH_SHORT); + toast.show(); + } + + + // Watch Settings + String prefStr; + + prefStr = SP.getString("PebbleDebug", "SET_FROM_XML"); + mDebug = (short) Integer.parseInt(prefStr); + Log.v(TAG, "updatePrefs() Debug = " + mDebug); + + prefStr = SP.getString("PebbleDisplaySpectrum", "SET_FROM_XML"); + mDisplaySpectrum = (short) Integer.parseInt(prefStr); + Log.v(TAG, "updatePrefs() DisplaySpectrum = " + mDisplaySpectrum); + + prefStr = SP.getString("PebbleUpdatePeriod", "SET_FROM_XML"); + mDataUpdatePeriod = (short) Integer.parseInt(prefStr); + Log.v(TAG, "updatePrefs() DataUpdatePeriod = " + mDataUpdatePeriod); + + prefStr = SP.getString("MutePeriod", "SET_FROM_XML"); + mMutePeriod = (short) Integer.parseInt(prefStr); + Log.v(TAG, "updatePrefs() MutePeriod = " + mMutePeriod); + + prefStr = SP.getString("ManAlarmPeriod", "SET_FROM_XML"); + mManAlarmPeriod = (short) Integer.parseInt(prefStr); + Log.v(TAG, "updatePrefs() ManAlarmPeriod = " + mManAlarmPeriod); + + prefStr = SP.getString("PebbleSdMode", "SET_FROM_XML"); + mPebbleSdMode = (short) Integer.parseInt(prefStr); + Log.v(TAG, "updatePrefs() PebbleSdMode = " + mPebbleSdMode); + + prefStr = SP.getString("SampleFreq", "SET_FROM_XML"); + mSampleFreq = (short) Integer.parseInt(prefStr); + Log.v(TAG, "updatePrefs() SampleFreq = " + mSampleFreq); + + prefStr = SP.getString("SamplePeriod", "SET_FROM_XML"); + mSamplePeriod = (short) Integer.parseInt(prefStr); + Log.v(TAG, "updatePrefs() AnalysisPeriod = " + mSamplePeriod); + + prefStr = SP.getString("AlarmFreqMin", "SET_FROM_XML"); + mAlarmFreqMin = (short) Integer.parseInt(prefStr); + Log.v(TAG, "updatePrefs() AlarmFreqMin = " + mAlarmFreqMin); + + prefStr = SP.getString("AlarmFreqMax", "SET_FROM_XML"); + mAlarmFreqMax = (short) Integer.parseInt(prefStr); + Log.v(TAG, "updatePrefs() AlarmFreqMax = " + mAlarmFreqMax); + + prefStr = SP.getString("WarnTime", "SET_FROM_XML"); + mWarnTime = (short) Integer.parseInt(prefStr); + Log.v(TAG, "updatePrefs() WarnTime = " + mWarnTime); + + prefStr = SP.getString("AlarmTime", "SET_FROM_XML"); + mAlarmTime = (short) Integer.parseInt(prefStr); + Log.v(TAG, "updatePrefs() AlarmTime = " + mAlarmTime); + + prefStr = SP.getString("AlarmThresh", "SET_FROM_XML"); + mAlarmThresh = (short) Integer.parseInt(prefStr); + Log.v(TAG, "updatePrefs() AlarmThresh = " + mAlarmThresh); + + prefStr = SP.getString("AlarmRatioThresh", "SET_FROM_XML"); + mAlarmRatioThresh = (short) Integer.parseInt(prefStr); + Log.v(TAG, "updatePrefs() AlarmRatioThresh = " + mAlarmRatioThresh); + + mFallActive = SP.getBoolean("FallActive", false); + Log.v(TAG, "updatePrefs() FallActive = " + mFallActive); + + prefStr = SP.getString("FallThreshMin", "SET_FROM_XML"); + mFallThreshMin = (short) Integer.parseInt(prefStr); + Log.v(TAG, "updatePrefs() FallThreshMin = " + mFallThreshMin); + + prefStr = SP.getString("FallThreshMax", "SET_FROM_XML"); + mFallThreshMax = (short) Integer.parseInt(prefStr); + Log.v(TAG, "updatePrefs() FallThreshMax = " + mFallThreshMax); + + prefStr = SP.getString("FallWindow", "SET_FROM_XML"); + mFallWindow = (short) Integer.parseInt(prefStr); + Log.v(TAG, "updatePrefs() FallWindow = " + mFallWindow); + + } catch (Exception ex) { + Log.v(TAG, "updatePrefs() - Problem parsing preferences!"); + mUtil.writeToSysLogFile("SdDataSourceAw.updatePrefs() - ERROR "+ex.toString()); + Toast toast = Toast.makeText(mContext, "Problem Parsing Preferences - Something won't work - Please go back to Settings and correct it!", Toast.LENGTH_SHORT); + toast.show(); + } + } + + class AWSdCommsListener extends WearableListenerService { + String TAG = "AWSdCommsListener"; + public String SERVICE_CALLED_WEAR = "WearListClicked"; + + @Override + public void onMessageReceived(MessageEvent messageEvent) { + super.onMessageReceived(messageEvent); + + String event = messageEvent.getPath(); + + Log.d(TAG, event); + + String [] message = event.split("--"); + + if (message[0].equals(SERVICE_CALLED_WEAR)) { + Log.d(TAG,"message detected"); + Log.v(TAG, "Setting mWatchAppRunningCheck to true"); + mWatchAppRunningCheck = true; + } + } + } + + + /** + * Set this server to receive pebble data by registering it as + * A PebbleDataReceiver + */ + private void startPebbleServer() { + Log.v(TAG, "StartPebbleServer()"); + mUtil.writeToSysLogFile("SdDataSourceAw.startPebbleServer()"); + final Handler handler = new Handler(); + msgDataHandler = new PebbleKit.PebbleDataReceiver(SD_UUID) { + @Override + public void receiveData(final Context context, + final int transactionId, + final PebbleDictionary data) { + Log.v(TAG, "Received message from Pebble - data type=" + + data.getUnsignedIntegerAsLong(KEY_DATA_TYPE)); + // If we have a message, the app must be running + Log.v(TAG, "Setting mWatchAppRunningCheck to true"); + mWatchAppRunningCheck = true; + PebbleKit.sendAckToPebble(context, transactionId); + //Log.v(TAG,"Message is: "+data.toJsonString()); + if (data.getUnsignedIntegerAsLong(KEY_DATA_TYPE) + == DATA_TYPE_RESULTS) { + Log.v(TAG, "DATA_TYPE = Results"); + mSdData.dataTime.setToNow(); + Log.v(TAG, "mSdData.dataTime=" + mSdData.dataTime); + + mSdData.alarmState = data.getUnsignedIntegerAsLong( + KEY_ALARMSTATE); + mSdData.maxVal = data.getUnsignedIntegerAsLong(KEY_MAXVAL); + mSdData.maxFreq = data.getUnsignedIntegerAsLong(KEY_MAXFREQ); + mSdData.specPower = data.getUnsignedIntegerAsLong(KEY_SPECPOWER); + mSdData.roiPower = data.getUnsignedIntegerAsLong(KEY_ROIPOWER); + mSdData.alarmPhrase = "Unknown"; + mSdData.haveData = true; + mSdDataReceiver.onSdDataReceived(mSdData); + + + // Read the data that has been sent, and convert it into + // an integer array. + byte[] byteArr = data.getBytes(KEY_SPEC_DATA); + if ((byteArr != null) && (byteArr.length != 0)) { + IntBuffer intBuf = ByteBuffer.wrap(byteArr) + .order(ByteOrder.LITTLE_ENDIAN) + .asIntBuffer(); + int[] intArray = new int[intBuf.remaining()]; + intBuf.get(intArray); + for (int i = 0; i < intArray.length; i++) { + mSdData.simpleSpec[i] = intArray[i]; + } + } else { + Log.v(TAG, "***** zero length spectrum received - error!!!!"); + } + } + + if (data.getUnsignedIntegerAsLong(KEY_DATA_TYPE) + == DATA_TYPE_SETTINGS) { + Log.v(TAG, "DATA_TYPE = Settings"); + try { + mSdData.analysisPeriod = data.getUnsignedIntegerAsLong(KEY_SAMPLE_PERIOD); + mSdData.alarmFreqMin = data.getUnsignedIntegerAsLong(KEY_ALARM_FREQ_MIN); + mSdData.alarmFreqMax = data.getUnsignedIntegerAsLong(KEY_ALARM_FREQ_MAX); + mSdData.nMin = data.getUnsignedIntegerAsLong(KEY_NMIN); + mSdData.nMax = data.getUnsignedIntegerAsLong(KEY_NMAX); + mSdData.warnTime = data.getUnsignedIntegerAsLong(KEY_WARN_TIME); + mSdData.alarmTime = data.getUnsignedIntegerAsLong(KEY_ALARM_TIME); + mSdData.alarmThresh = data.getUnsignedIntegerAsLong(KEY_ALARM_THRESH); + mSdData.alarmRatioThresh = data.getUnsignedIntegerAsLong(KEY_ALARM_RATIO_THRESH); + mSdData.batteryPc = data.getUnsignedIntegerAsLong(KEY_BATTERY_PC); + mSdData.haveSettings = true; + } catch (Exception ex) { + mUtil.showToast("*** Error interpreting settings sent from watch - Please check you have " + + "the latest version of the watch app installed by using the OpenSeizureDetector " + + "menu to install the Watch App"); + mUtil.writeToSysLogFile("Error interpreting settings received from watch - wrong version " + + "of watch app installed?"); + } + } + if (data.getUnsignedIntegerAsLong(KEY_DATA_TYPE) + == DATA_TYPE_RAW) { + Log.v(TAG, "DATA_TYPE = Raw"); + long numSamples; + numSamples = data.getUnsignedIntegerAsLong(KEY_NUM_RAW_DATA); + Log.v(TAG, "numSamples = " + numSamples); + byte[] rawDataBytes = data.getBytes(KEY_RAW_DATA); + for (int i = 0; i < rawDataBytes.length - 4; i += 4) { // 4 bytes per sample + int x = (rawDataBytes[i]); + //int y = (rawDataBytes[i+2] & 0xff) | (rawDataBytes[i+3] << 8); + //int z = (rawDataBytes[i+4] & 0xff) | (rawDataBytes[i+5] << 8); + //Log.v(TAG,"x="+x+", y="+y+", z="+z); + Log.v(TAG,"x="+x); + if (nRawData < MAX_RAW_DATA) { + rawData[nRawData] = (int)Math.sqrt(x); + } else { + Log.i(TAG, "WARNING - rawData Buffer Full"); + } + + } + + + //for (AccelData reading : AccelData.fromDataArray(rawDataBytes)) { + // if (nRawData < MAX_RAW_DATA) { + // rawData[nRawData] = reading.getMagnitude(); + // nRawData++; + // } else { + // Log.i(TAG, "WARNING - rawData Buffer Full"); + // } + // } + + } + } + }; + PebbleKit.registerReceivedDataHandler(mContext, msgDataHandler); + // We struggle to connect to pebble time if app is already running, + // so stop app so we can re-connect to it. + //stopWatchApp(); + startWatchApp(); + } + + /** + * De-register this server from receiving pebble data + */ + public void stopPebbleServer() { + Log.v(TAG, "stopServer(): Stopping Pebble Server"); + Log.v(TAG, "stopServer(): msgDataHandler = " + msgDataHandler.toString()); + mUtil.writeToSysLogFile("SdDataSourceAw.stopServer()"); + try { + mContext.unregisterReceiver(msgDataHandler); + stopWatchApp(); + } catch (Exception e) { + Log.v(TAG, "stopServer() - error " + e.toString()); + mUtil.writeToSysLogFile("SdDataSourceAw.stopServer() - error " + e.toString()); + } + } + + /** + * Attempt to start the pebble_sd watch app on the pebble watch. + */ + public void startWatchApp() { + Log.v(TAG, "startWatchApp() - closing app first"); + mUtil.writeToSysLogFile("SdDataSourceAw.startWatchApp() - closing app first"); + // first close the watch app if it is running. + // FIXME - Make this work with Android Wear + Log.v(TAG, "startWatchApp() - starting watch app after 5 seconds delay..."); + // Wait 5 seconds then start the app. + Timer appStartTimer = new Timer(); + appStartTimer.schedule(new TimerTask() { + @Override + public void run() { + Log.v(TAG, "startWatchApp() - starting watch app..."); + mUtil.writeToSysLogFile("SdDataSourceAw.startWatchApp() - starting watch app"); + // FIXME - Make this work with Android Wear + + } + }, 5000); + } + + /** + * stop the pebble_sd watch app on the pebble watch. + */ + public void stopWatchApp() { + Log.v(TAG, "stopWatchApp()"); + mUtil.writeToSysLogFile("SdDataSourceAw.stopWatchApp()"); + // FIXME - Make this work with Android Wear + } + + + /** + * Send our latest settings to the watch, then request Pebble App to send + * us its latest settings so we can check it has been set up correctly.. + * Will be received as a message by the receiveData handler + */ + public void getWatchSdSettings() { + Log.v(TAG, "getWatchSdSettings() - sending required settings to pebble"); + mUtil.writeToSysLogFile("SdDataSourceAw.getWatchSdSettings()"); + sendWatchSdSettings(); + //Log.v(TAG, "getWatchSdSettings() - requesting settings from pebble"); + //mUtil.writeToSysLogFile("SdDataSourceAw.getWatchSdSettings() - and request settings from pebble"); + + // FIXME - make this work with Android Wear + } + + /** + * Send the watch settings that are stored as class member + * variables to the watch. + */ + public void sendWatchSdSettings() { + Log.v(TAG, "sendWatchSdSettings() - preparing settings dictionary.. mSampleFreq=" + mSampleFreq); + mUtil.writeToSysLogFile("SdDataSourceAw.sendWatchSdSettings()"); + + // Watch Settings + //final PebbleDictionary setDict = new PebbleDictionary(); + //setDict.addInt16(KEY_DEBUG, mDebug); + //setDict.addInt16(KEY_DISPLAY_SPECTRUM, mDisplaySpectrum); + //setDict.addInt16(KEY_DATA_UPDATE_PERIOD, mDataUpdatePeriod); + //setDict.addInt16(KEY_MUTE_PERIOD, mMutePeriod); + //setDict.addInt16(KEY_MAN_ALARM_PERIOD, mManAlarmPeriod); + //setDict.addInt16(KEY_SD_MODE, mPebbleSdMode); + //setDict.addInt16(KEY_SAMPLE_FREQ, mSampleFreq); + //setDict.addInt16(KEY_SAMPLE_PERIOD, mSamplePeriod); + //setDict.addInt16(KEY_ALARM_FREQ_MIN, mAlarmFreqMin); + //setDict.addInt16(KEY_ALARM_FREQ_MAX, mAlarmFreqMax); + //setDict.addUint16(KEY_WARN_TIME, mWarnTime); + //setDict.addUint16(KEY_ALARM_TIME, mAlarmTime); + //setDict.addUint16(KEY_ALARM_THRESH, mAlarmThresh); + //setDict.addUint16(KEY_ALARM_RATIO_THRESH, mAlarmRatioThresh); + //if (mFallActive) + // setDict.addUint16(KEY_FALL_ACTIVE, (short) 1); + //else + // setDict.addUint16(KEY_FALL_ACTIVE, (short) 0); + //setDict.addUint16(KEY_FALL_THRESH_MIN, mFallThreshMin); + //setDict.addUint16(KEY_FALL_THRESH_MAX, mFallThreshMax); + //setDict.addUint16(KEY_FALL_WINDOW, mFallWindow); + + // Send Watch Settings to Pebble + //Log.v(TAG, "sendWatchSdSettings() - setDict = " + setDict.toJsonString()); + //PebbleKit.sendDataToPebble(mContext, SD_UUID, setDict); + // FIXME - make this work on Android Wear + } + + + /** + * Compares the watch settings retrieved from the watch (stored in mSdData) + * to the required settings stored as member variables to this class. + * + * @return true if they are all the same, or false if there are discrepancies. + */ + public boolean checkWatchSettings() { + boolean settingsOk = true; + if (mDataUpdatePeriod != mSdData.mDataUpdatePeriod) { + Log.v(TAG, "checkWatchSettings - mDataUpdatePeriod Wrong"); + settingsOk = false; + } + if (mMutePeriod != mSdData.mMutePeriod) { + Log.v(TAG, "checkWatchSettings - mMutePeriod Wrong"); + settingsOk = false; + } + if (mManAlarmPeriod != mSdData.mManAlarmPeriod) { + Log.v(TAG, "checkWatchSettings - mManAlarmPeriod Wrong"); + settingsOk = false; + } + if (mSamplePeriod != mSdData.analysisPeriod) { + Log.v(TAG, "checkWatchSettings - mSamplePeriod Wrong"); + settingsOk = false; + } + if (mAlarmFreqMin != mSdData.alarmFreqMin) { + Log.v(TAG, "checkWatchSettings - mAlarmFreqMin Wrong"); + settingsOk = false; + } + if (mAlarmFreqMax != mSdData.alarmFreqMax) { + Log.v(TAG, "checkWatchSettings - mAlarmFreqMax Wrong"); + settingsOk = false; + } + if (mWarnTime != mSdData.warnTime) { + Log.v(TAG, "checkWatchSettings - mWarnTime Wrong"); + settingsOk = false; + } + if (mAlarmTime != mSdData.alarmTime) { + Log.v(TAG, "checkWatchSettings - mAlarmTime Wrong"); + settingsOk = false; + } + if (mAlarmThresh != mSdData.alarmThresh) { + Log.v(TAG, "checkWatchSettings - mAlarmThresh Wrong"); + settingsOk = false; + } + if (mAlarmRatioThresh != mSdData.alarmRatioThresh) { + Log.v(TAG, "checkWatchSettings - mAlarmRatioThresh Wrong"); + settingsOk = false; + } + if (mFallActive != mSdData.mFallActive) { + Log.v(TAG, "checkWatchSettings - mFallActive Wrong"); + settingsOk = false; + } + if (mFallThreshMin != mSdData.mFallThreshMin) { + Log.v(TAG, "checkWatchSettings - mFallThreshMin Wrong"); + settingsOk = false; + } + if (mFallThreshMax != mSdData.mFallThreshMax) { + Log.v(TAG, "checkWatchSettings - mFallThreshMax Wrong"); + settingsOk = false; + } + if (mFallWindow != mSdData.mFallWindow) { + Log.v(TAG, "checkWatchSettings - mFallWindow Wrong"); + settingsOk = false; + } + + return settingsOk; + } + + /** + * Request Watch App to send us its latest data. + * Will be received as a message by the receiveData handler + */ + public void getWatchData() { + Log.v(TAG, "getData() - requesting data from watch"); + mUtil.writeToSysLogFile("SdDataSourceAw.getData() - requesting data from pebble"); + // FIXME - make this work with Android Wear + } + + + /** + * Checks the status of the connection to the pebble watch, + * and sets class variables for use by other functions. + * If the watch app is not running, it attempts to re-start it. + */ + public void getPebbleStatus() { + Time tnow = new Time(Time.getCurrentTimezone()); + long tdiff; + tnow.setToNow(); + // get time since the last data was received from the Pebble watch. + tdiff = (tnow.toMillis(false) - mStatusTime.toMillis(false)); + Log.v(TAG, "getStatus() - mWatchAppRunningCheck=" + mWatchAppRunningCheck + " tdiff=" + tdiff); + // Check we are actually connected to the pebble. + mSdData.pebbleConnected = PebbleKit.isWatchConnected(mContext); + if (!mSdData.pebbleConnected) mWatchAppRunningCheck = false; + // And is the pebble_sd app running? + // set mWatchAppRunningCheck has been false for more than 10 seconds + // the app is not talking to us + // mWatchAppRunningCheck is set to true in the receiveData handler. + if (!mWatchAppRunningCheck && + (tdiff > (mDataUpdatePeriod + mAppRestartTimeout) * 1000)) { + Log.v(TAG, "getStatus() - tdiff = " + tdiff); + mSdData.pebbleAppRunning = false; + //Log.v(TAG, "getStatus() - Pebble App Not Running - Attempting to Re-Start"); + //mUtil.writeToSysLogFile("SdDataSourceAw.getStatus() - Pebble App not Running - Attempting to Re-Start"); + //startWatchApp(); + //mStatusTime = tnow; // set status time to now so we do not re-start app repeatedly. + //getWatchSdSettings(); + // Only make audible warning beep if we have not received data for more than mFaultTimerPeriod seconds. + if (tdiff > (mDataUpdatePeriod + mFaultTimerPeriod) * 1000) { + Log.v(TAG, "getStatus() - Watch App Not Running - Attempting to Re-Start"); + mUtil.writeToSysLogFile("SdDataSourceAw.getStatus() - Pebble App not Running - Attempting to Re-Start"); + startWatchApp(); + mStatusTime.setToNow(); + mSdDataReceiver.onSdDataFault(mSdData); + } else { + Log.v(TAG, "getStatus() - Waiting for mFaultTimerPeriod before issuing audible warning..."); + } + } else { + mSdData.pebbleAppRunning = true; + } + + // if we have confirmation that the app is running, reset the + // status time to now and initiate another check. + if (mWatchAppRunningCheck) { + mWatchAppRunningCheck = false; + mStatusTime.setToNow(); + } + + if (!mSdData.haveSettings) { + Log.v(TAG, "getStatus() - no settings received yet - requesting"); + getWatchSdSettings(); + getWatchData(); + } + + if (mPebbleSdMode == SD_MODE_RAW) { + analyseRawData(); + } + } + + /** + * analyseRawData() - called when raw data is received. + * FIXME - this does not do anything at the moment so raw data is + * ignored! + */ + private void analyseRawData() { + Log.v(TAG,"analyserawData()"); + //DoubleFFT_1D fft = new DoubleFFT_1D(MAX_RAW_DATA); + //fft.realForward(rawData); + // FIXME - rawData should really be a circular buffer. + nRawData = 0; + } + + + + + +} + + + + + diff --git a/app/src/main/java/uk/org/openseizuredetector/SdDataSourceNetworkPassive.java b/app/src/main/java/uk/org/openseizuredetector/SdDataSourceNetworkPassive.java index 5092e7c..421c4a5 100644 --- a/app/src/main/java/uk/org/openseizuredetector/SdDataSourceNetworkPassive.java +++ b/app/src/main/java/uk/org/openseizuredetector/SdDataSourceNetworkPassive.java @@ -186,7 +186,7 @@ public class SdDataSourceNetworkPassive extends SdDataSource { // make sure we get some data when we first start. //getData(); // Start timer to retrieve pebble settings regularly. - //getPebbleSdSettings(); + //getWatchSdSettings(); if (mSettingsTimer == null) { Log.v(TAG, "start(): starting settings timer"); mUtil.writeToSysLogFile("SdDataSourceNetworkPassive.start() - starting settings timer"); @@ -194,7 +194,7 @@ public class SdDataSourceNetworkPassive extends SdDataSource { mSettingsTimer.schedule(new TimerTask() { @Override public void run() { - //mUtil.writeToSysLogFile("SdDataSourceNetworkPassive.mSettingsTimer timed out.");getPebbleSdSettings(); + //mUtil.writeToSysLogFile("SdDataSourceNetworkPassive.mSettingsTimer timed out.");getWatchSdSettings(); } }, 0, 1000 * mSettingsPeriod); // ask for settings less frequently than we get data } else { @@ -524,11 +524,11 @@ public class SdDataSourceNetworkPassive extends SdDataSource { * Will be received as a message by the receiveData handler */ public void getPebbleSdSettings() { - Log.v(TAG, "getPebbleSdSettings() - sending required settings to pebble"); - mUtil.writeToSysLogFile("SdDataSourceNetworkPassive.getPebbleSdSettings()"); + Log.v(TAG, "getWatchSdSettings() - sending required settings to pebble"); + mUtil.writeToSysLogFile("SdDataSourceNetworkPassive.getWatchSdSettings()"); sendPebbleSdSettings(); - //Log.v(TAG, "getPebbleSdSettings() - requesting settings from pebble"); - //mUtil.writeToSysLogFile("SdDataSourceNetworkPassive.getPebbleSdSettings() - and request settings from pebble"); + //Log.v(TAG, "getWatchSdSettings() - requesting settings from pebble"); + //mUtil.writeToSysLogFile("SdDataSourceNetworkPassive.getWatchSdSettings() - and request settings from pebble"); PebbleDictionary data = new PebbleDictionary(); data.addUint8(KEY_SETTINGS, (byte) 1); PebbleKit.sendDataToPebble( @@ -543,7 +543,7 @@ public class SdDataSourceNetworkPassive extends SdDataSource { */ public void sendPebbleSdSettings() { Log.v(TAG, "sendPebblSdSettings() - preparing settings dictionary.. mSampleFreq=" + mSampleFreq); - mUtil.writeToSysLogFile("SdDataSourceNetworkPassive.sendPebbleSdSettings()"); + mUtil.writeToSysLogFile("SdDataSourceNetworkPassive.sendWatchSdSettings()"); // Watch Settings final PebbleDictionary setDict = new PebbleDictionary(); @@ -570,7 +570,7 @@ public class SdDataSourceNetworkPassive extends SdDataSource { setDict.addUint16(KEY_FALL_WINDOW, mFallWindow); // Send Watch Settings to Pebble - Log.v(TAG, "sendPebbleSdSettings() - setDict = " + setDict.toJsonString()); + Log.v(TAG, "sendWatchSdSettings() - setDict = " + setDict.toJsonString()); PebbleKit.sendDataToPebble(mContext, SD_UUID, setDict); } @@ -686,7 +686,7 @@ public class SdDataSourceNetworkPassive extends SdDataSource { //mUtil.writeToSysLogFile("SdDataSourceNetworkPassive.getStatus() - Pebble App not Running - Attempting to Re-Start"); //startWatchApp(); //mPebbleStatusTime = tnow; // set status time to now so we do not re-start app repeatedly. - //getPebbleSdSettings(); + //getWatchSdSettings(); // Only make audible warning beep if we have not received data for more than mFaultTimerPeriod seconds. if (tdiff > (mDataUpdatePeriod + mFaultTimerPeriod) * 1000) { Log.v(TAG, "getStatus() - Pebble App Not Running - Attempting to Re-Start"); diff --git a/app/src/main/java/uk/org/openseizuredetector/SdDataSourcePebble.java b/app/src/main/java/uk/org/openseizuredetector/SdDataSourcePebble.java index 89bee94..77c69cf 100644 --- a/app/src/main/java/uk/org/openseizuredetector/SdDataSourcePebble.java +++ b/app/src/main/java/uk/org/openseizuredetector/SdDataSourcePebble.java @@ -526,11 +526,11 @@ public class SdDataSourcePebble extends SdDataSource { * Will be received as a message by the receiveData handler */ public void getPebbleSdSettings() { - Log.v(TAG, "getPebbleSdSettings() - sending required settings to pebble"); - mUtil.writeToSysLogFile("SdDataSourcePebble.getPebbleSdSettings()"); + Log.v(TAG, "getWatchSdSettings() - sending required settings to pebble"); + mUtil.writeToSysLogFile("SdDataSourcePebble.getWatchSdSettings()"); sendPebbleSdSettings(); - //Log.v(TAG, "getPebbleSdSettings() - requesting settings from pebble"); - //mUtil.writeToSysLogFile("SdDataSourcePebble.getPebbleSdSettings() - and request settings from pebble"); + //Log.v(TAG, "getWatchSdSettings() - requesting settings from pebble"); + //mUtil.writeToSysLogFile("SdDataSourcePebble.getWatchSdSettings() - and request settings from pebble"); PebbleDictionary data = new PebbleDictionary(); data.addUint8(KEY_SETTINGS, (byte) 1); PebbleKit.sendDataToPebble( @@ -545,7 +545,7 @@ public class SdDataSourcePebble extends SdDataSource { */ public void sendPebbleSdSettings() { Log.v(TAG, "sendPebblSdSettings() - preparing settings dictionary.. mSampleFreq=" + mSampleFreq); - mUtil.writeToSysLogFile("SdDataSourcePebble.sendPebbleSdSettings()"); + mUtil.writeToSysLogFile("SdDataSourcePebble.sendWatchSdSettings()"); // Watch Settings final PebbleDictionary setDict = new PebbleDictionary(); @@ -572,7 +572,7 @@ public class SdDataSourcePebble extends SdDataSource { setDict.addUint16(KEY_FALL_WINDOW, mFallWindow); // Send Watch Settings to Pebble - Log.v(TAG, "sendPebbleSdSettings() - setDict = " + setDict.toJsonString()); + Log.v(TAG, "sendWatchSdSettings() - setDict = " + setDict.toJsonString()); PebbleKit.sendDataToPebble(mContext, SD_UUID, setDict); } @@ -688,7 +688,7 @@ public class SdDataSourcePebble extends SdDataSource { //mUtil.writeToSysLogFile("SdDataSourcePebble.getStatus() - Pebble App not Running - Attempting to Re-Start"); //startWatchApp(); //mPebbleStatusTime = tnow; // set status time to now so we do not re-start app repeatedly. - //getPebbleSdSettings(); + //getWatchSdSettings(); // Only make audible warning beep if we have not received data for more than mFaultTimerPeriod seconds. if (tdiff > (mDataUpdatePeriod + mFaultTimerPeriod) * 1000) { Log.v(TAG, "getStatus() - Pebble App Not Running - Attempting to Re-Start"); diff --git a/app/src/main/java/uk/org/openseizuredetector/SdServer.java b/app/src/main/java/uk/org/openseizuredetector/SdServer.java index 5b43d48..dd73adf 100644 --- a/app/src/main/java/uk/org/openseizuredetector/SdServer.java +++ b/app/src/main/java/uk/org/openseizuredetector/SdServer.java @@ -199,6 +199,11 @@ public class SdServer extends Service implements SdDataReceiver, SdLocationRecei mUtil.writeToSysLogFile("SdServer.onStartCommand() - creating SdDataSourcePebble"); mSdDataSource = new SdDataSourcePebble(this.getApplicationContext(), mHandler, this); break; + case "AndroidWear": + Log.v(TAG, "Selecting Android Wear DataSource"); + mUtil.writeToSysLogFile("SdServer.onStartCommand() - creating SdDataSourceAw"); + mSdDataSource = new SdDataSourceAw(this.getApplicationContext(), mHandler, this); + break; case "Network": Log.v(TAG, "Selecting Network DataSource"); mUtil.writeToSysLogFile("SdServer.onStartCommand() - creating SdDataSourceNetwork"); diff --git a/app/src/main/res/values/datasource_list.xml b/app/src/main/res/values/datasource_list.xml index 5c60912..7d521fe 100644 --- a/app/src/main/res/values/datasource_list.xml +++ b/app/src/main/res/values/datasource_list.xml @@ -2,11 +2,13 @@ "Pebble Watch" + "Android Wear" "Network" "Network (passive)" "Pebble" + "AndroidWear" "Network" "NetworkPassive"