/*
* Copyright (C) 2014 TU Darmstadt, Hessen, Germany.
* Department of Computer Science Databases and Distributed Systems
*
* This program 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.
*
* This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
*/
/**
*
*/
package de.tudarmstadt.dvs.myhealthassistant.myhealthhub.services;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.TimeZone;
import java.util.Vector;
import android.app.Service;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.Binder;
import android.os.Handler;
import android.os.IBinder;
import android.preference.PreferenceManager;
import android.util.Log;
import de.tudarmstadt.dvs.myhealthassistant.myhealthhub.Preferences;
import de.tudarmstadt.dvs.myhealthassistant.myhealthhub.commontools.EventUtils;
import de.tudarmstadt.dvs.myhealthassistant.myhealthhub.events.AbstractChannel;
import de.tudarmstadt.dvs.myhealthassistant.myhealthhub.events.Event;
import de.tudarmstadt.dvs.myhealthassistant.myhealthhub.events.sensorreadings.SensorReadingEvent;
import de.tudarmstadt.dvs.myhealthassistant.myhealthhub.events.sensorreadings.environmental.AbstractEnvironmentalEvent;
import de.tudarmstadt.dvs.myhealthassistant.myhealthhub.events.sensorreadings.environmental.EnvironmentActivityEvent;
import de.tudarmstadt.dvs.myhealthassistant.myhealthhub.xml.ECARule;
import de.tudarmstadt.dvs.myhealthassistant.myhealthhub.xml.ExtendedECARule;
import de.tudarmstadt.dvs.myhealthassistant.myhealthhub.xml.XMLtoECARule;
/**
* @author Christian Seeger
*
*/
public class EnvironmentalActivityDetection extends Service {
/* for debugging */
private boolean D = false;
private String TAG = "EnvironmentalActivityDetection";
/* event bus */
private final static String PRODUCER_ID = "de.tudarmstadt.dvs.myhealthhub.EnvironmentalActivityDetection";
protected String myHealthHubReceiver = AbstractChannel.RECEIVER;
/* Define event channel and event RECEIVER */
// TODO: does not work anymore
private final IntentFilter readingChannel = new IntentFilter(AbstractEnvironmentalEvent.EVENT_TYPE);
private readingEventReceiver mReadingEventReceiver;
private SensorUtilityList sensorUtilityList;
private HashMap<Integer, Runnable> runningTemporaryRules;
Handler temporaryThreadsHandler;
private HashMap<String, Vector<ECARule>> listOfRules;
private HashMap<String, Vector<ECARule>> listOfTemporaryRules;
@Override
public void onCreate() {
if (D) Log.d(TAG, "Initilize event RECEIVER.");
mReadingEventReceiver = new readingEventReceiver();
getApplication().registerReceiver(mReadingEventReceiver, readingChannel);
sensorUtilityList = new SensorUtilityList();
temporaryThreadsHandler = new Handler();
runningTemporaryRules = new HashMap<Integer, Runnable>();
listOfRules = new HashMap<String, Vector<ECARule>>();
listOfTemporaryRules = new HashMap<String, Vector<ECARule>>();
parseAndLoadXMLRules();
}
@Override
public IBinder onBind(Intent arg0) {
return mBinder;
}
private final IBinder mBinder = new ServiceBinder();
public class ServiceBinder extends Binder {
public void reloadXMLRules() {
parseAndLoadXMLRules();
}
}
private void parseAndLoadXMLRules() {
String xmlFile = PreferenceManager.getDefaultSharedPreferences(this).getString(Preferences.XML_FILE_ENV_RULES, null);
if(D)Log.d(TAG, "Parsing file: "+xmlFile);
XMLtoECARule parser = new XMLtoECARule();
//ECARule[] rules = parser.parseFile("/sdcard/myHealthAssistant/rules/rules.xml");
ECARule[] rules = parser.parseFile(xmlFile);
if(rules == null) {
if(D) Log.i(TAG, "No rules added.");
return;
}
if(D)Log.d(TAG, "Number of rules parsed: "+rules.length);
// Create threads for rules. An alternative: HashMap<Eventtype, List<ECARule>>
for (final ECARule rule : rules) {
if(D)Log.i(TAG, "Adding rule #"+rule.id+" for action: "+rule.action);
addToListOfRules(rule);
}
}
private void addToListOfRules(ECARule rule) {
// check whether there's already an entry for event type
if(listOfRules.containsKey(rule.eventType)) {
Vector<ECARule> rules = listOfRules.get(rule.eventType);
rules.add(rule);
} else {
Vector<ECARule> rules = new Vector<ECARule>();
rules.add(rule);
listOfRules.put(rule.eventType, rules);
}
}
private void addToListOfTemporaryRules(ECARule rule) {
// check whether there's already an entry for event type
if(listOfTemporaryRules.containsKey(rule.eventType)) {
Vector<ECARule> rules = listOfTemporaryRules.get(rule.eventType);
rules.add(rule);
} else {
Vector<ECARule> rules = new Vector<ECARule>();
for(ECARule existingRule : rules) {
if(existingRule.id == rule.id) return;
}
rules.add(rule);
listOfTemporaryRules.put(rule.eventType, rules);
}
}
private void removeFromListOfTemporaryRules(ECARule rule) {
if(D)Log.d(TAG, "Remove rule #"+rule.id+" from temporary rules list.");
// check whether event type is in list
if(listOfTemporaryRules.containsKey(rule.eventType)) {
Vector<ECARule> rules = listOfTemporaryRules.get(rule.eventType);
// is specific rule in list
if(rules.remove(rule)) {
if(D)Log.d(TAG, "Rule #"+rule.id+" was removed from listOfRules.");
}
}
}
private boolean triggerECA(AbstractEnvironmentalEvent evt, ECARule rule) {
// Check event type, needs to be specified in rule
if (!evt.getEventType().equals(rule.eventType)) {
return false;
}
// Check location
if (rule.location != null) {
if(!evt.getLocation().equals(rule.location)) return false;
}
// Check object
if (rule.object != null) {
if(!rule.object.equals(evt.getObject())) return false;
}
return checkCondition(evt, rule);
}
private boolean checkCondition(AbstractEnvironmentalEvent evt, ECARule rule) {
if(D)Log.d(TAG, "checkCondition");
// Operator
switch (rule.operator) {
case ECARule.OP_EQUALS:
if(D)Log.d(TAG, "Equals operator");
// Left term
switch (rule.leftTerm) {
case ECARule.LEFTTERM_VALUE:
if (evt.getValue().equals(rule.rightTerm)) {
if(D)Log.d(TAG, evt.getValue()+ " Equals "+rule.rightTerm);
triggerAction(evt, rule);
return true;
}
}
break;
}
return false;
}
SimpleDateFormat dateFormatGmt = new SimpleDateFormat("dd:MM:yyyy HH:mm:ss");
private void triggerAction(AbstractEnvironmentalEvent evt, ECARule rule) {
dateFormatGmt.setTimeZone(TimeZone.getTimeZone("GMT"));
String date = (dateFormatGmt.format(new Date())+"");
if(D)Log.d(TAG, "triggerAction");
switch(rule.action) {
case ECARule.ACTION_EVENT_SHOWERING:
injectEvent(new EnvironmentActivityEvent(
sensorUtilityList.getEventID(SensorReadingEvent.ACTIVITY, evt.getObject()),
date,
PRODUCER_ID,
evt,
EnvironmentActivityEvent.SHOWERING,
-1));
break;
case ECARule.ACTION_EVENT_ENTERING:
injectEvent( new EnvironmentActivityEvent(
sensorUtilityList.getEventID(SensorReadingEvent.ACTIVITY, evt.getObject()),
date,
PRODUCER_ID,
evt,
EnvironmentActivityEvent.ENTERING,
-1));
break;
case ECARule.ACTION_EVENT_LEAVING:
injectEvent(new EnvironmentActivityEvent(
sensorUtilityList.getEventID(SensorReadingEvent.ACTIVITY, evt.getObject()),
date,
PRODUCER_ID,
evt,
EnvironmentActivityEvent.LEAVING,
-1));
break;
case ECARule.ACTION_EVENT_TOOTH_BRUSHING:
injectEvent(new EnvironmentActivityEvent(
sensorUtilityList.getEventID(SensorReadingEvent.ACTIVITY, evt.getObject()),
date,
PRODUCER_ID,
evt,
EnvironmentActivityEvent.TEETH_BRUSHING,
-1));
break;
case ECARule.ACTION_EVENT_EATING:
injectEvent(new EnvironmentActivityEvent(
sensorUtilityList.getEventID(SensorReadingEvent.ACTIVITY, evt.getObject()),
date,
PRODUCER_ID,
evt,
EnvironmentActivityEvent.EATING,
-1));
break;
case ECARule.ACTION_EVENT_AIRING:
injectEvent(new EnvironmentActivityEvent(
sensorUtilityList.getEventID(SensorReadingEvent.ACTIVITY, evt.getObject()),
date,
PRODUCER_ID,
evt,
EnvironmentActivityEvent.AIRING,
-1));
break;
case ECARule.ACTION_EXTENDED_RULE:
//createTemporaryThread((ExtendedECARule) rule);
addTemporaryRule((ExtendedECARule) rule);
break;
}
}
private void addTemporaryRule(final ExtendedECARule extendedECARule) {
final ECARule rule = extendedECARule.secondRule;
// Check whether rule is already active
if(runningTemporaryRules.containsKey(rule.id)) {
// reset timer
if(D)Log.d(TAG, "Temporary thread is already running. Resetting timer.");
Runnable r = runningTemporaryRules.get(rule.id);
temporaryThreadsHandler.removeCallbacks(r);
temporaryThreadsHandler.postDelayed(r, extendedECARule.maxTimeDifference);
return;
}
// Add temporary rule to listOfRules
addToListOfTemporaryRules(rule);
// Initialize runnable for removing temporary rule
Runnable r=new Runnable() {
public void run() {
// StopProducer thread, remove it from list
removeFromListOfTemporaryRules(rule);
runningTemporaryRules.remove(rule.id);
}
};
// Add runnable to runningTemporaryRules
runningTemporaryRules.put(rule.id, r);
temporaryThreadsHandler.postDelayed(r, extendedECARule.maxTimeDifference);
}
/** Event RECEIVER implemented as a Android BroadcastReceiver */
private class readingEventReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
// Get event type and the event itself
String eventType = intent.getStringExtra("event_type");
if(D)Log.d(TAG, "Incoming event of type "+eventType);
if(!(intent.getParcelableExtra(Event.PARCELABLE_EXTRA_EVENT) instanceof AbstractEnvironmentalEvent)) {
Log.d(TAG, "Not an instance of AbstractEnvironmentalEvent: "+eventType);
return;
}
AbstractEnvironmentalEvent evt = intent.getParcelableExtra(Event.PARCELABLE_EXTRA_EVENT);
// Check for matching rules
if(listOfRules.containsKey(eventType)) {
if(D)Log.d(TAG, "List of rules contains event type "+eventType);
Vector<ECARule> rules = listOfRules.get(eventType);
for(ECARule rule : rules) {
triggerECA(evt, rule);
}
} else {
if(D)Log.d(TAG, "List of rules does not contain event type "+eventType);
}
// Check for matching temporary rules
if(listOfTemporaryRules.containsKey(eventType)) {
Vector<ECARule> rules = listOfTemporaryRules.get(eventType);
if(D)Log.d(TAG, "### "+rules);
for(ECARule rule : rules) {
// remove from lists if action was triggered
if(triggerECA(evt, rule)) {
rules.remove(rule);
runningTemporaryRules.remove(rule.id);
}
}
}
// Generate OCCUPANCY event based on sensor type and location
//generateOccupancyEvent(evt);
}
}
private void generateOccupancyEvent(AbstractEnvironmentalEvent evt) {
// OccupancyEvent objEvt = new OccupancyEvent(
// sensorUtilityList.getEventID(OccupancyEvent.PARCELABLE_EXTRA_EVENT_TYPE, evt.getObject()),
// Calendar.getInstance().getTime().toGMTString(),
// PRODUCER_ID,
// evt.getSensorType(),
// evt.getTimeOfMeasurement(),
// evt.getLocation(),
// evt.getObject());
// injectEvent(objEvt);
}
private void injectEvent(SensorReadingEvent evt) {
if(D)Log.d(TAG, "Inject event of type: "+evt.getEventType());
// Send event
Intent i = new Intent();
i.putExtra(Event.PARCELABLE_EXTRA_EVENT_TYPE, evt.getEventType());
i.putExtra(Event.PARCELABLE_EXTRA_EVENT, evt);
i.setAction(AbstractChannel.RECEIVER);
getApplicationContext().sendBroadcast(i);
}
/**
* Maintains instances of EventUtils for the individual
* @author Chris
*
*/
private class SensorUtilityList {
private EventUtils eventUtil;
private HashMap<String, EventUtils> eventUtilsMap = new HashMap<String, EventUtils>();
public String getEventID(String eventType, String sensorID) {
eventUtil = eventUtilsMap.get(sensorID);
if(eventUtil != null) {
return eventUtil.getEventID();
} else {
eventUtilsMap.put(sensorID, new EventUtils(eventType, sensorID));
return eventUtilsMap.get(sensorID).getEventID();
}
}
};
}