Fixed build dependency issues by moving hal core into hal-core folder/subproject
This commit is contained in:
parent
f1da2c5a4d
commit
ded778fd11
138 changed files with 195 additions and 183 deletions
464
hal-core/src/se/hal/ControllerManager.java
Normal file
464
hal-core/src/se/hal/ControllerManager.java
Normal file
|
|
@ -0,0 +1,464 @@
|
|||
package se.hal;
|
||||
|
||||
import se.hal.intf.*;
|
||||
import se.hal.struct.Event;
|
||||
import se.hal.struct.Sensor;
|
||||
import zutil.db.DBConnection;
|
||||
import zutil.log.LogUtil;
|
||||
import zutil.plugin.PluginManager;
|
||||
import zutil.ui.Configurator;
|
||||
import zutil.ui.Configurator.PostConfigurationActionListener;
|
||||
import zutil.ui.Configurator.PreConfigurationActionListener;
|
||||
|
||||
import java.sql.PreparedStatement;
|
||||
import java.sql.SQLException;
|
||||
import java.util.*;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
/**
|
||||
* This class manages all SensorController and EventController objects
|
||||
*/
|
||||
@SuppressWarnings("RedundantCast")
|
||||
public class ControllerManager implements HalSensorReportListener,
|
||||
HalEventReportListener,
|
||||
PreConfigurationActionListener,
|
||||
PostConfigurationActionListener {
|
||||
private static final Logger logger = LogUtil.getLogger();
|
||||
private static ControllerManager instance;
|
||||
|
||||
|
||||
/** All available sensor plugins **/
|
||||
private List<Class<? extends HalSensorConfig>> availableSensors = new ArrayList<>();
|
||||
/** List of all registered sensors **/
|
||||
private List<Sensor> registeredSensors = Collections.synchronizedList(new ArrayList<Sensor>());
|
||||
/** List of auto detected sensors **/
|
||||
private List<Sensor> detectedSensors = Collections.synchronizedList(new ArrayList<Sensor>());
|
||||
/** List of sensors that are currently being reconfigured **/
|
||||
private List<Sensor> limboSensors = Collections.synchronizedList(new LinkedList<Sensor>());
|
||||
|
||||
|
||||
/** All available event plugins **/
|
||||
private List<Class<? extends HalEventConfig>> availableEvents = new ArrayList<>();
|
||||
/** List of all registered events **/
|
||||
private List<Event> registeredEvents = Collections.synchronizedList(new ArrayList<Event>());
|
||||
/** List of auto detected events **/
|
||||
private List<Event> detectedEvents = Collections.synchronizedList(new ArrayList<Event>());
|
||||
/** List of all registered events **/
|
||||
private List<Event> limboEvents = Collections.synchronizedList(new LinkedList<Event>());
|
||||
|
||||
|
||||
/** A map of all instantiated controllers **/
|
||||
private HashMap<Class,HalAbstractController> controllerMap = new HashMap<>();
|
||||
|
||||
|
||||
/////////////////////////////// SENSORS ///////////////////////////////////
|
||||
|
||||
/**
|
||||
* Register a Sensor instance on the manager.
|
||||
* The manager will start to save reported data for the registered Sensor.
|
||||
*/
|
||||
public void register(Sensor sensor) {
|
||||
if(sensor.getDeviceConfig() == null) {
|
||||
logger.warning("Sensor config is null: "+ sensor);
|
||||
return;
|
||||
}
|
||||
if(!availableSensors.contains(sensor.getDeviceConfig().getClass())) {
|
||||
logger.warning("Sensor data plugin not available: "+ sensor.getDeviceConfig().getClass());
|
||||
return;
|
||||
}
|
||||
|
||||
logger.info("Registering new sensor(id: "+ sensor.getId() +"): "+ sensor.getDeviceConfig().getClass());
|
||||
Class<? extends HalSensorController> c = sensor.getController();
|
||||
HalSensorController controller = getControllerInstance(c);
|
||||
|
||||
if(controller != null)
|
||||
controller.register(sensor.getDeviceConfig());
|
||||
registeredSensors.add(sensor);
|
||||
detectedSensors.remove(findSensor(sensor.getDeviceConfig(), detectedSensors)); // Remove if this device was detected
|
||||
}
|
||||
|
||||
/**
|
||||
* Deregisters a Sensor from the manager.
|
||||
* Data reported on the Sensor will no longer be saved but already saved data will not be modified.
|
||||
* The Controller that owns the Sensor will be deallocated if it has no more registered devices.
|
||||
*/
|
||||
public void deregister(Sensor sensor){
|
||||
if(sensor.getDeviceConfig() == null) {
|
||||
logger.warning("Sensor config is null: "+ sensor);
|
||||
return;
|
||||
}
|
||||
|
||||
Class<? extends HalSensorController> c = sensor.getController();
|
||||
HalSensorController controller = (HalSensorController) controllerMap.get(c);
|
||||
if (controller != null) {
|
||||
logger.info("Deregistering sensor(id: "+ sensor.getId() +"): "+ sensor.getDeviceConfig().getClass());
|
||||
controller.deregister(sensor.getDeviceConfig());
|
||||
registeredSensors.remove(sensor);
|
||||
removeControllerIfEmpty(controller);
|
||||
} else {
|
||||
logger.warning("Controller not instantiated:"+ sensor.getController());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers a Sensor class type as usable by the manager
|
||||
*/
|
||||
public void addAvailableSensor(Class<? extends HalSensorConfig> sensorClass) {
|
||||
if ( ! availableSensors.contains(sensorClass))
|
||||
availableSensors.add(sensorClass);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return a List of all available Sensors that can be registered to this manager
|
||||
*/
|
||||
public List<Class<? extends HalSensorConfig>> getAvailableSensors(){
|
||||
return availableSensors;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return a List of Sensor instances that have been registered to this manager
|
||||
*/
|
||||
public List<Sensor> getRegisteredSensors(){
|
||||
return registeredSensors;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return a List of Sensor instances that have been reported but not registered on the manager
|
||||
*/
|
||||
public List<Sensor> getDetectedSensors(){
|
||||
return detectedSensors;
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes all auto detected sensors.
|
||||
*/
|
||||
public void clearDetectedSensors(){
|
||||
detectedSensors.clear();
|
||||
}
|
||||
|
||||
/**
|
||||
* Called by Controllers to report received Sensor data
|
||||
*/
|
||||
@Override
|
||||
public void reportReceived(HalSensorConfig sensorConfig, HalSensorData sensorData) {
|
||||
try{
|
||||
DBConnection db = HalContext.getDB();
|
||||
Sensor sensor = findSensor(sensorConfig, registeredSensors);
|
||||
|
||||
if (sensor != null) {
|
||||
logger.finest("Received report from sensor("+sensorConfig.getClass().getSimpleName()+"): "+ sensorConfig);
|
||||
PreparedStatement stmt =
|
||||
db.getPreparedStatement("INSERT INTO sensor_data_raw (timestamp, sensor_id, data) VALUES(?, ?, ?)");
|
||||
stmt.setLong(1, sensorData.getTimestamp());
|
||||
stmt.setLong(2, sensor.getId());
|
||||
stmt.setDouble(3, sensorData.getData());
|
||||
DBConnection.exec(stmt);
|
||||
}
|
||||
else { // unknown sensor
|
||||
logger.finest("Received report from unregistered sensor" +
|
||||
"("+sensorConfig.getClass().getSimpleName()+"): "+ sensorConfig);
|
||||
sensor = findSensor(sensorConfig, detectedSensors);
|
||||
if(sensor == null) {
|
||||
sensor = new Sensor();
|
||||
detectedSensors.add(sensor);
|
||||
}
|
||||
sensor.setDeviceConfig(sensorConfig);
|
||||
}
|
||||
sensor.setDeviceData(sensorData);
|
||||
// call listeners
|
||||
for(HalDeviceReportListener<Sensor> listener : sensor.getReportListeners())
|
||||
listener.receivedReport(sensor);
|
||||
|
||||
}catch (SQLException e){
|
||||
logger.log(Level.WARNING, "Unable to store sensor report", e);
|
||||
}
|
||||
}
|
||||
|
||||
private static Sensor findSensor(HalSensorConfig sensorData, List<Sensor> list){
|
||||
for (int i=0; i<list.size(); ++i) { // Don't use foreach for concurrency reasons
|
||||
Sensor s = list.get(i);
|
||||
if (sensorData.equals(s.getDeviceConfig())) {
|
||||
return s;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
//////////////////////////////// EVENTS ///////////////////////////////////
|
||||
|
||||
/**
|
||||
* Register a Event instance on the manager.
|
||||
* The manager will start to save reported data for the registered Event.
|
||||
*/
|
||||
public void register(Event event) {
|
||||
if(event.getDeviceConfig() == null) {
|
||||
logger.warning("Event config is null: "+ event);
|
||||
return;
|
||||
}
|
||||
if(!availableEvents.contains(event.getDeviceConfig().getClass())) {
|
||||
logger.warning("Event data plugin not available: "+ event.getDeviceConfig().getClass());
|
||||
return;
|
||||
}
|
||||
|
||||
logger.info("Registering new event(id: "+ event.getId() +"): "+ event.getDeviceConfig().getClass());
|
||||
Class<? extends HalEventController> c = event.getController();
|
||||
HalEventController controller = getControllerInstance(c);
|
||||
|
||||
if(controller != null)
|
||||
controller.register(event.getDeviceConfig());
|
||||
registeredEvents.add(event);
|
||||
detectedEvents.remove(findEvent(event.getDeviceConfig(), detectedEvents)); // Remove if this device was detected
|
||||
}
|
||||
|
||||
/**
|
||||
* Deregisters a Event from the manager.
|
||||
* Data reported on the Event will no longer be saved but already saved data will not be modified.
|
||||
* The Controller that owns the Event will be deallocated if it has no more registered devices.
|
||||
*/
|
||||
public void deregister(Event event){
|
||||
if(event.getDeviceConfig() == null) {
|
||||
logger.warning("Event config is null: "+ event);
|
||||
return;
|
||||
}
|
||||
|
||||
Class<? extends HalEventController> c = event.getController();
|
||||
HalEventController controller = (HalEventController) controllerMap.get(c);
|
||||
if (controller != null) {
|
||||
logger.info("Deregistering event(id: "+ event.getId() +"): "+ event.getDeviceConfig().getClass());
|
||||
controller.deregister(event.getDeviceConfig());
|
||||
registeredEvents.remove(event);
|
||||
removeControllerIfEmpty(controller);
|
||||
} else {
|
||||
logger.warning("Controller not instantiated: "+ event.getController());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers a Event class type as usable by the manager
|
||||
*/
|
||||
public void addAvailableEvent(Class<? extends HalEventConfig> eventClass) {
|
||||
if ( ! availableEvents.contains(eventClass))
|
||||
availableEvents.add(eventClass);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return a List of all available Events that can be registered to this manager
|
||||
*/
|
||||
public List<Class<? extends HalEventConfig>> getAvailableEvents(){
|
||||
return availableEvents;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return a List of Sensor instances that have been registered to this manager
|
||||
*/
|
||||
public List<Event> getRegisteredEvents(){
|
||||
return registeredEvents;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return a List of Event instances that have been reported but not registered on the manager
|
||||
*/
|
||||
public List<Event> getDetectedEvents(){
|
||||
return detectedEvents;
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes all auto detected events.
|
||||
*/
|
||||
public void clearDetectedEvents(){
|
||||
detectedEvents.clear();
|
||||
}
|
||||
|
||||
/**
|
||||
* Called by Controllers to report received Event data
|
||||
*/
|
||||
@Override
|
||||
public void reportReceived(HalEventConfig eventConfig, HalEventData eventData) {
|
||||
try {
|
||||
DBConnection db = HalContext.getDB();
|
||||
Event event = findEvent(eventConfig, registeredEvents);
|
||||
|
||||
if (event != null) {
|
||||
logger.finest("Received report from event("+eventConfig.getClass().getSimpleName()+"): "+ eventConfig);
|
||||
PreparedStatement stmt =
|
||||
db.getPreparedStatement("INSERT INTO event_data_raw (timestamp, event_id, data) VALUES(?, ?, ?)");
|
||||
stmt.setLong(1, eventData.getTimestamp());
|
||||
stmt.setLong(2, event.getId());
|
||||
stmt.setDouble(3, eventData.getData());
|
||||
DBConnection.exec(stmt);
|
||||
}
|
||||
else { // unknown sensor
|
||||
logger.info("Received report from unregistered event" +
|
||||
"("+eventConfig.getClass().getSimpleName()+"): "+ eventConfig);
|
||||
event = findEvent(eventConfig, detectedEvents);
|
||||
if(event == null) {
|
||||
event = new Event();
|
||||
detectedEvents.add(event);
|
||||
}
|
||||
event.setDeviceConfig(eventConfig);
|
||||
}
|
||||
event.setDeviceData(eventData);
|
||||
// call listeners
|
||||
for(HalDeviceReportListener<Event> listener : event.getReportListeners())
|
||||
listener.receivedReport(event);
|
||||
|
||||
}catch (SQLException e){
|
||||
logger.log(Level.WARNING, "Unable to store event report", e);
|
||||
}
|
||||
}
|
||||
|
||||
private static Event findEvent(HalEventConfig eventData, List<Event> list){
|
||||
for (int i=0; i<list.size(); ++i) { // Don't use foreach for concurrency reasons
|
||||
Event e = list.get(i);
|
||||
if (eventData.equals(e.getDeviceConfig())) {
|
||||
return e;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public void send(Event event){
|
||||
HalEventController controller = getControllerInstance(event.getController());
|
||||
if(controller != null) {
|
||||
controller.send(event.getDeviceConfig(), event.getDeviceData());
|
||||
reportReceived(event.getDeviceConfig(), event.getDeviceData()); // save action to db
|
||||
}
|
||||
else
|
||||
logger.warning("No controller found for event id: "+ event.getId());
|
||||
}
|
||||
|
||||
/////////////////////////////// GENERAL ///////////////////////////////////
|
||||
|
||||
/**
|
||||
* @return all instantiated controllers.
|
||||
*/
|
||||
public List<HalAbstractController> getControllers() {
|
||||
return new ArrayList(controllerMap.values());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void preConfigurationAction(Configurator configurator, Object obj) {
|
||||
if(obj instanceof HalSensorConfig) {
|
||||
Sensor sensor = findSensor((HalSensorConfig) obj, registeredSensors);
|
||||
if(sensor != null){
|
||||
deregister(sensor);
|
||||
limboSensors.add(sensor);
|
||||
}
|
||||
}
|
||||
else if(obj instanceof HalEventConfig) {
|
||||
Event event = findEvent((HalEventConfig) obj, registeredEvents);
|
||||
if(event != null){
|
||||
deregister(event);
|
||||
limboEvents.add(event);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void postConfigurationAction(Configurator configurator, Object obj) {
|
||||
if(obj instanceof HalSensorConfig) {
|
||||
Sensor sensor = findSensor((HalSensorConfig) obj, limboSensors);
|
||||
if(sensor != null){
|
||||
register(sensor);
|
||||
limboSensors.remove(sensor);
|
||||
}
|
||||
}
|
||||
else if(obj instanceof HalEventConfig) {
|
||||
Event event = findEvent((HalEventConfig) obj, limboEvents);
|
||||
if(event != null){
|
||||
register(event);
|
||||
limboEvents.remove(event);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private <T extends HalAbstractController> T getControllerInstance(Class<T> c){
|
||||
HalAbstractController controller;
|
||||
|
||||
if (controllerMap.containsKey(c)) {
|
||||
controller = controllerMap.get(c);
|
||||
} else {
|
||||
try {
|
||||
// Instantiate controller
|
||||
controller = c.newInstance();
|
||||
|
||||
if (controller instanceof HalAutoScannableController &&
|
||||
! ((HalAutoScannableController) controller).isAvailable()) {
|
||||
logger.warning("Controller is not ready: " + c.getName());
|
||||
return null;
|
||||
}
|
||||
|
||||
logger.info("Instantiating new controller: " + c.getName());
|
||||
controller.initialize();
|
||||
|
||||
if(controller instanceof HalSensorController) {
|
||||
((HalSensorController) controller).setListener(this);
|
||||
}
|
||||
if(controller instanceof HalEventController) {
|
||||
((HalEventController) controller).setListener(this);
|
||||
}
|
||||
|
||||
controllerMap.put(c, controller);
|
||||
} catch (Exception e){
|
||||
logger.log(Level.SEVERE, "Unable to instantiate controller: " + c.getName(), e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
return (T) controller;
|
||||
}
|
||||
|
||||
private void removeControllerIfEmpty(Object controller){
|
||||
if (controller instanceof HalAutoScannableController)
|
||||
return; // Don't do anything if controller is scannable
|
||||
|
||||
int size = Integer.MAX_VALUE;
|
||||
if(controller instanceof HalSensorController)
|
||||
size = ((HalSensorController) controller).size();
|
||||
else if(controller instanceof HalEventController)
|
||||
size = ((HalEventController) controller).size();
|
||||
|
||||
if(size < 0){
|
||||
// Remove controller as it has no more registered sensors
|
||||
logger.info("Closing controller as it has no more registered devices: " + controller.getClass().getName());
|
||||
controllerMap.remove(controller.getClass());
|
||||
|
||||
if(controller instanceof HalSensorController)
|
||||
((HalSensorController) controller).close();
|
||||
else if(controller instanceof HalEventController)
|
||||
((HalEventController) controller).close();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public static void initialize(PluginManager pluginManager){
|
||||
ControllerManager manager = new ControllerManager();
|
||||
|
||||
for (Iterator<Class<? extends HalSensorConfig>> it = pluginManager.getClassIterator(HalSensorConfig.class);
|
||||
it.hasNext(); ){
|
||||
manager.addAvailableSensor(it.next());
|
||||
}
|
||||
|
||||
for (Iterator<Class<? extends HalEventConfig>> it = pluginManager.getClassIterator(HalEventConfig.class);
|
||||
it.hasNext(); ){
|
||||
manager.addAvailableEvent(it.next());
|
||||
}
|
||||
|
||||
for (Iterator<Class<? extends HalAutoScannableController>> it = pluginManager.getClassIterator(HalAutoScannableController.class);
|
||||
it.hasNext(); ){
|
||||
Class controller = it.next();
|
||||
|
||||
if (controller.isAssignableFrom(HalAbstractController.class))
|
||||
manager.getControllerInstance(controller); // Instantiate controller
|
||||
}
|
||||
|
||||
instance = manager;
|
||||
}
|
||||
|
||||
|
||||
public static ControllerManager getInstance(){
|
||||
return instance;
|
||||
}
|
||||
}
|
||||
246
hal-core/src/se/hal/HalContext.java
Normal file
246
hal-core/src/se/hal/HalContext.java
Normal file
|
|
@ -0,0 +1,246 @@
|
|||
package se.hal;
|
||||
|
||||
import se.hal.struct.User;
|
||||
import zutil.ObjectUtil;
|
||||
import zutil.db.DBConnection;
|
||||
import zutil.db.DBUpgradeHandler;
|
||||
import zutil.db.SQLResultHandler;
|
||||
import zutil.db.handler.PropertiesSQLResult;
|
||||
import zutil.io.file.FileUtil;
|
||||
import zutil.log.LogUtil;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileReader;
|
||||
import java.sql.*;
|
||||
import java.util.*;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
|
||||
public class HalContext {
|
||||
private static final Logger logger = LogUtil.getLogger();
|
||||
|
||||
// Constants
|
||||
public static final String PROPERTY_DB_VERSION = "hal.db_version";
|
||||
public static final String PROPERTY_HTTP_PORT = "hal.http_port";
|
||||
public static final String PROPERTY_MAP_BACKGROUND_IMAGE = "hal.map_bgimage";
|
||||
|
||||
private static final String CONF_FILE = "hal.conf";
|
||||
private static final String DB_FILE = "hal.db";
|
||||
private static final String DEFAULT_DB_FILE = "hal-default.db";
|
||||
|
||||
// Variables
|
||||
private static DBConnection db; // TODO: Should probably be a db pool as we have multiple threads accessing the DB
|
||||
|
||||
private static HashMap<String,String> registeredConf = new HashMap<>();
|
||||
private static Properties fileConf = new Properties();
|
||||
private static Properties dbConf = new Properties();;
|
||||
|
||||
|
||||
static {
|
||||
// Set default values to get Hal up and running
|
||||
fileConf.setProperty(PROPERTY_HTTP_PORT, "" + 8080);
|
||||
}
|
||||
|
||||
|
||||
public static void initialize(){
|
||||
try {
|
||||
// Read conf
|
||||
if (FileUtil.find(CONF_FILE) != null) {
|
||||
FileReader in = new FileReader(CONF_FILE);
|
||||
fileConf.load(in);
|
||||
in.close();
|
||||
} else {
|
||||
logger.info("No hal.conf file found");
|
||||
}
|
||||
|
||||
if (FileUtil.find(DEFAULT_DB_FILE) == null){
|
||||
logger.severe("Unable to find default DB: " + DEFAULT_DB_FILE);
|
||||
System.exit(1);
|
||||
}
|
||||
|
||||
// Init DB
|
||||
File dbFile = FileUtil.find(DB_FILE);
|
||||
db = new DBConnection(DBConnection.DBMS.SQLite, DB_FILE);
|
||||
|
||||
if(dbFile == null){
|
||||
logger.info("No database file found, creating new DB...");
|
||||
} else {
|
||||
dbConf = db.exec("SELECT * FROM conf", new PropertiesSQLResult());
|
||||
}
|
||||
|
||||
// Upgrade DB needed?
|
||||
DBConnection referenceDB = new DBConnection(DBConnection.DBMS.SQLite, DEFAULT_DB_FILE);
|
||||
Properties defaultDBConf =
|
||||
referenceDB.exec("SELECT * FROM conf", new PropertiesSQLResult());
|
||||
|
||||
// Check DB version
|
||||
final int defaultDBVersion = Integer.parseInt(defaultDBConf.getProperty(PROPERTY_DB_VERSION));
|
||||
final int dbVersion = (dbConf.getProperty(PROPERTY_DB_VERSION) != null ?
|
||||
Integer.parseInt(dbConf.getProperty(PROPERTY_DB_VERSION)) :
|
||||
-1);
|
||||
logger.info("DB version: "+ dbVersion);
|
||||
|
||||
if(defaultDBVersion > dbVersion ) {
|
||||
logger.info("Starting DB upgrade from v" + dbVersion + " to v" + defaultDBVersion + "...");
|
||||
if(dbFile != null){
|
||||
File backupDB = FileUtil.getNextFile(dbFile);
|
||||
logger.fine("Backing up DB to: "+ backupDB);
|
||||
FileUtil.copy(dbFile, backupDB);
|
||||
}
|
||||
|
||||
logger.fine(String.format("Upgrading DB (from: v%s, to: v%s)...", dbVersion, defaultDBVersion));
|
||||
final DBUpgradeHandler handler = new DBUpgradeHandler(referenceDB);
|
||||
handler.addIgnoredTable("db_version_history");
|
||||
handler.addIgnoredTable("sqlite_sequence"); // sqlite internal
|
||||
handler.setTargetDB(db);
|
||||
|
||||
logger.fine("Performing pre-upgrade activities");
|
||||
|
||||
// Read upgrade path preferences from the reference database
|
||||
referenceDB.exec("SELECT * FROM db_version_history"
|
||||
+ " WHERE db_version <= " + defaultDBVersion
|
||||
+ " AND db_version > " + dbVersion,
|
||||
new SQLResultHandler<Object>() {
|
||||
@Override
|
||||
public Object handleQueryResult(Statement stmt, ResultSet result) throws SQLException {
|
||||
while(result.next()){
|
||||
if(result.getBoolean("force_upgrade")){
|
||||
logger.fine("Forced upgrade enabled");
|
||||
handler.setForcedDBUpgrade(true); //set to true if any of the intermediate db version requires it.
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
});
|
||||
|
||||
handler.upgrade();
|
||||
|
||||
logger.fine("Performing post-upgrade activities");
|
||||
|
||||
// Read upgrade path preferences from the reference database
|
||||
referenceDB.exec("SELECT * FROM db_version_history"
|
||||
+ " WHERE db_version <= " + defaultDBVersion
|
||||
+ " AND db_version > " + dbVersion,
|
||||
new SQLResultHandler<Object>() {
|
||||
@Override
|
||||
public Object handleQueryResult(Statement stmt, ResultSet result) throws SQLException {
|
||||
boolean clearExternalAggrData = false;
|
||||
boolean clearInternalAggrData = false;
|
||||
|
||||
while(result.next()){
|
||||
if(result.getBoolean("clear_external_aggr_data"))
|
||||
clearExternalAggrData = true;
|
||||
if(result.getBoolean("clear_internal_aggr_data"))
|
||||
clearInternalAggrData = true;
|
||||
}
|
||||
|
||||
if(clearExternalAggrData){
|
||||
logger.fine("Clearing external aggregate data");
|
||||
db.exec("DELETE FROM sensor_data_aggr WHERE sensor_id = "
|
||||
+ "(SELECT sensor.id FROM user, sensor WHERE user.external == 1 AND sensor.user_id = user.id)");
|
||||
}
|
||||
if(clearInternalAggrData){
|
||||
logger.fine("Clearing local aggregate data");
|
||||
db.exec("DELETE FROM sensor_data_aggr WHERE sensor_id IN "
|
||||
+ "(SELECT sensor.id FROM user, sensor WHERE user.external == 0 AND sensor.user_id = user.id)");
|
||||
//update all internal sensors aggregation version to indicate for peers that they need to re-sync all data
|
||||
db.exec("UPDATE sensor SET aggr_version = (aggr_version+1) WHERE id = "
|
||||
+ "(SELECT sensor.id FROM user, sensor WHERE user.external == 0 AND sensor.user_id = user.id)");
|
||||
}
|
||||
return null;
|
||||
}
|
||||
});
|
||||
|
||||
// Check if there is a local user
|
||||
User localUser = User.getLocalUser(db);
|
||||
if (localUser == null){
|
||||
logger.fine("Creating local user.");
|
||||
localUser = new User();
|
||||
localUser.setExternal(false);
|
||||
localUser.save(db);
|
||||
}
|
||||
|
||||
logger.info("DB upgrade done");
|
||||
setProperty(PROPERTY_DB_VERSION, defaultDBConf.getProperty(PROPERTY_DB_VERSION));
|
||||
}
|
||||
referenceDB.close();
|
||||
} catch (Exception e){
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
public static Map<String,String> getProperties() {
|
||||
HashMap map = new HashMap();
|
||||
map.putAll(registeredConf);
|
||||
map.putAll(dbConf);
|
||||
map.putAll(fileConf);
|
||||
return map;
|
||||
|
||||
}
|
||||
public static void registerProperty(String key) {
|
||||
registeredConf.put(key, "");
|
||||
}
|
||||
|
||||
public static boolean containsProperty(String key) {
|
||||
return !ObjectUtil.isEmpty(getStringProperty(key));
|
||||
}
|
||||
public static String getStringProperty(String key){
|
||||
registerProperty(key);
|
||||
|
||||
String value = null;
|
||||
if (fileConf != null)
|
||||
value = fileConf.getProperty(key);
|
||||
if (dbConf != null && value == null)
|
||||
value = dbConf.getProperty(key);
|
||||
return value;
|
||||
}
|
||||
public static String getStringProperty(String key, String defaultValue){
|
||||
if (!HalContext.containsProperty(key))
|
||||
return defaultValue;
|
||||
return getStringProperty(key);
|
||||
}
|
||||
|
||||
public static int getIntegerProperty(String key){
|
||||
return Integer.parseInt(getStringProperty(key));
|
||||
}
|
||||
public static int getIntegerProperty(String key, int defaultValue){
|
||||
if (!HalContext.containsProperty(key))
|
||||
return defaultValue;
|
||||
return getIntegerProperty(key);
|
||||
}
|
||||
|
||||
public static boolean getBooleanProperty(String key) {
|
||||
return Boolean.parseBoolean(getStringProperty(key));
|
||||
}
|
||||
public static boolean getBooleanProperty(String key, boolean defaultValue) {
|
||||
if (!HalContext.containsProperty(key))
|
||||
return defaultValue;
|
||||
return getBooleanProperty(key);
|
||||
}
|
||||
|
||||
public static void setProperty(String key, String value) throws SQLException {
|
||||
PreparedStatement stmt = db.getPreparedStatement("REPLACE INTO conf (key, value) VALUES (?, ?)");
|
||||
stmt.setObject(1, key);
|
||||
stmt.setObject(2, value);
|
||||
DBConnection.exec(stmt);
|
||||
dbConf.setProperty(key, value);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
public static DBConnection getDB(){
|
||||
return db;
|
||||
}
|
||||
|
||||
/**
|
||||
* For testing purposes.
|
||||
*/
|
||||
public static void setDB(DBConnection db){
|
||||
HalContext.db = db;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
166
hal-core/src/se/hal/HalServer.java
Normal file
166
hal-core/src/se/hal/HalServer.java
Normal file
|
|
@ -0,0 +1,166 @@
|
|||
package se.hal;
|
||||
|
||||
|
||||
import se.hal.intf.HalDaemon;
|
||||
import se.hal.intf.HalWebPage;
|
||||
import se.hal.intf.HalJsonPage;
|
||||
import se.hal.page.*;
|
||||
import se.hal.struct.Event;
|
||||
import se.hal.struct.PluginConfig;
|
||||
import se.hal.struct.Sensor;
|
||||
import se.hal.struct.TriggerFlow;
|
||||
import zutil.db.DBConnection;
|
||||
import zutil.io.file.FileUtil;
|
||||
import zutil.log.LogUtil;
|
||||
import zutil.net.http.HttpServer;
|
||||
import zutil.net.http.page.HttpFilePage;
|
||||
import zutil.net.http.page.HttpRedirectPage;
|
||||
import zutil.plugin.PluginData;
|
||||
import zutil.plugin.PluginManager;
|
||||
|
||||
import java.sql.SQLException;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.ScheduledExecutorService;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
/**
|
||||
* Main class for Hal
|
||||
*/
|
||||
public class HalServer {
|
||||
private static final Logger logger = LogUtil.getLogger();
|
||||
|
||||
private static ScheduledExecutorService daemonExecutor;
|
||||
private static List<HalDaemon> daemons = new ArrayList<>();
|
||||
|
||||
private static HttpServer http;
|
||||
private static List<HalWebPage> pages = new ArrayList<>();
|
||||
|
||||
private static PluginManager pluginManager;
|
||||
|
||||
|
||||
|
||||
public static void main(String[] args) throws InterruptedException {
|
||||
try {
|
||||
// init logging
|
||||
LogUtil.readConfiguration("logging.properties");
|
||||
|
||||
// init DB and other configurations
|
||||
HalContext.initialize();
|
||||
DBConnection db = HalContext.getDB();
|
||||
|
||||
// ------------------------------------
|
||||
// Init Plugins
|
||||
// ------------------------------------
|
||||
|
||||
logger.info("Looking for plugins.");
|
||||
pluginManager = new PluginManager();
|
||||
|
||||
// Disable plugins based on settings
|
||||
for (PluginData plugin : getPlugins()) {
|
||||
PluginConfig pluginConfig = PluginConfig.getPluginConfig(db, plugin.getName());
|
||||
|
||||
if (pluginConfig != null && !pluginConfig.isEnabled()) {
|
||||
logger.info("Disabling plugin '" + plugin.getName() + "'.");
|
||||
plugin.setEnabled(false);
|
||||
}
|
||||
}
|
||||
|
||||
// ------------------------------------
|
||||
// Init Managers
|
||||
// ------------------------------------
|
||||
|
||||
logger.info("Initializing managers.");
|
||||
|
||||
HalAlertManager.initialize();
|
||||
ControllerManager.initialize(pluginManager);
|
||||
TriggerManager.initialize(pluginManager);
|
||||
|
||||
// ------------------------------------
|
||||
// Import sensors,events and triggers
|
||||
// ------------------------------------
|
||||
|
||||
logger.info("Initializing Sensors and Events.");
|
||||
|
||||
for (Sensor sensor : Sensor.getLocalSensors(db)) {
|
||||
ControllerManager.getInstance().register(sensor);
|
||||
}
|
||||
for (Event event : Event.getLocalEvents(db)) {
|
||||
ControllerManager.getInstance().register(event);
|
||||
}
|
||||
// Import triggers
|
||||
for (TriggerFlow flow : TriggerFlow.getTriggerFlows(db)) {
|
||||
TriggerManager.getInstance().register(flow);
|
||||
}
|
||||
|
||||
// ------------------------------------
|
||||
// Init daemons
|
||||
// ------------------------------------
|
||||
|
||||
logger.info("Initializing daemons.");
|
||||
|
||||
// We set only one thread for easier troubleshooting
|
||||
daemonExecutor = Executors.newScheduledThreadPool(1);
|
||||
for (Iterator<HalDaemon> it = pluginManager.getSingletonIterator(HalDaemon.class); it.hasNext(); ) {
|
||||
HalDaemon daemon = it.next();
|
||||
logger.info("Registering daemon: " + daemon.getClass());
|
||||
registerDaemon(daemon);
|
||||
}
|
||||
|
||||
// ------------------------------------
|
||||
// Init http server
|
||||
// ------------------------------------
|
||||
|
||||
logger.info("Initializing HTTP Server.");
|
||||
|
||||
HalWebPage.getRootNav().createSubNav("Sensors");
|
||||
HalWebPage.getRootNav().createSubNav("Events").setWeight(100);
|
||||
HalWebPage.getRootNav().createSubNav("Settings").setWeight(200);
|
||||
|
||||
http = new HttpServer(HalContext.getIntegerProperty(HalContext.PROPERTY_HTTP_PORT));
|
||||
http.setDefaultPage(new HttpFilePage(FileUtil.find("resource/web/")));
|
||||
http.setPage("/", new HttpRedirectPage("/map"));
|
||||
http.setPage(HalAlertManager.getInstance().getUrl(), HalAlertManager.getInstance());
|
||||
for (Iterator<HalWebPage> it = pluginManager.getSingletonIterator(HalJsonPage.class); it.hasNext(); )
|
||||
registerPage(it.next());
|
||||
for (Iterator<HalWebPage> it = pluginManager.getSingletonIterator(HalWebPage.class); it.hasNext(); )
|
||||
registerPage(it.next());
|
||||
http.start();
|
||||
|
||||
} catch (Exception e) {
|
||||
logger.log(Level.SEVERE, "Startup failed.", e);
|
||||
System.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public static void setPluginEnabled(String name, boolean enabled) throws SQLException {
|
||||
DBConnection db = HalContext.getDB();
|
||||
PluginConfig pluginConfig = PluginConfig.getPluginConfig(db, name);
|
||||
|
||||
if (pluginConfig == null)
|
||||
pluginConfig = new PluginConfig(name);
|
||||
|
||||
logger.info("Plugin '" + name + "' has been " + (enabled ? "enabled" : "disabled") + ", change will take affect after restart.");
|
||||
pluginManager.getPluginData(name).setEnabled(enabled);
|
||||
|
||||
pluginConfig.setEnabled(enabled);
|
||||
pluginConfig.save(db);
|
||||
}
|
||||
|
||||
public static List<PluginData> getPlugins() {
|
||||
return pluginManager.toArray();
|
||||
}
|
||||
|
||||
|
||||
public static void registerDaemon(HalDaemon daemon){
|
||||
daemons.add(daemon);
|
||||
daemon.initiate(daemonExecutor);
|
||||
}
|
||||
|
||||
public static void registerPage(HalWebPage page){
|
||||
pages.add(page);
|
||||
http.setPage(page.getId(), page);
|
||||
}
|
||||
}
|
||||
109
hal-core/src/se/hal/TriggerManager.java
Normal file
109
hal-core/src/se/hal/TriggerManager.java
Normal file
|
|
@ -0,0 +1,109 @@
|
|||
package se.hal;
|
||||
|
||||
import se.hal.intf.HalAction;
|
||||
import se.hal.intf.HalTrigger;
|
||||
import se.hal.struct.TriggerFlow;
|
||||
import zutil.log.LogUtil;
|
||||
import zutil.plugin.PluginManager;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.ScheduledExecutorService;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
|
||||
/**
|
||||
* Handles all triggers registered on Hal
|
||||
*/
|
||||
public class TriggerManager {
|
||||
private static final Logger logger = LogUtil.getLogger();
|
||||
private static final long EVALUATION_INTERVAL = 5 * 1000;
|
||||
private static TriggerManager instance;
|
||||
|
||||
private ArrayList<Class<? extends HalTrigger>> availableTriggers = new ArrayList<>();
|
||||
private ArrayList<Class<? extends HalAction>> availableActions = new ArrayList<>();
|
||||
|
||||
private ArrayList<TriggerFlow> triggerFlows = new ArrayList<>();
|
||||
private ScheduledExecutorService executor;
|
||||
|
||||
|
||||
|
||||
public void setEvaluationInterval(long interval) {
|
||||
if (executor != null)
|
||||
executor.shutdownNow();
|
||||
executor = Executors.newScheduledThreadPool(1);
|
||||
executor.scheduleAtFixedRate(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
evaluateAndExecute();
|
||||
} catch (Exception e){ logger.log(Level.SEVERE, "Trigger Evaluation Thread has Crashed", e); }
|
||||
}
|
||||
}, 0, interval, TimeUnit.MILLISECONDS);
|
||||
}
|
||||
|
||||
|
||||
public void addAvailableTrigger(Class<? extends HalTrigger> clazz) {
|
||||
if ( ! availableTriggers.contains(clazz))
|
||||
availableTriggers.add(clazz);
|
||||
}
|
||||
public List<Class<? extends HalTrigger>> getAvailableTriggers() {
|
||||
return availableTriggers;
|
||||
}
|
||||
|
||||
public void addAvailableAction(Class<? extends HalAction> clazz) {
|
||||
if ( ! availableActions.contains(clazz))
|
||||
availableActions.add(clazz);
|
||||
}
|
||||
public List<Class<? extends HalAction>> getAvailableActions() {
|
||||
return availableActions;
|
||||
}
|
||||
|
||||
public void register(TriggerFlow flow){
|
||||
if ( ! triggerFlows.contains(flow))
|
||||
triggerFlows.add(flow);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Main execution method.
|
||||
* This method will go through all flows and evaluate them. If the
|
||||
* evaluation of a trigger returns true then its execute method will be called.
|
||||
*/
|
||||
public synchronized void evaluateAndExecute() {
|
||||
for (int i = 0; i < triggerFlows.size(); i++) { // avoid foreach as triggerFlows can change while we are running
|
||||
TriggerFlow flow = triggerFlows.get(i);
|
||||
if (flow.evaluate()) {
|
||||
logger.fine("Flow "+ flow.getId() +" evaluated true, executing actions");
|
||||
flow.execute();
|
||||
flow.reset();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public static void initialize(PluginManager pluginManager) {
|
||||
TriggerManager manager = new TriggerManager();
|
||||
|
||||
for (Iterator<Class<? extends HalTrigger>> it = pluginManager.getClassIterator(HalTrigger.class);
|
||||
it.hasNext(); ) {
|
||||
manager.addAvailableTrigger(it.next());
|
||||
}
|
||||
|
||||
for (Iterator<Class<? extends HalAction>> it = pluginManager.getClassIterator(HalAction.class);
|
||||
it.hasNext(); ) {
|
||||
manager.addAvailableAction(it.next());
|
||||
}
|
||||
|
||||
manager.setEvaluationInterval(EVALUATION_INTERVAL);
|
||||
instance = manager;
|
||||
}
|
||||
|
||||
public static TriggerManager getInstance(){
|
||||
return instance;
|
||||
}
|
||||
}
|
||||
55
hal-core/src/se/hal/action/SendEventAction.java
Normal file
55
hal-core/src/se/hal/action/SendEventAction.java
Normal file
|
|
@ -0,0 +1,55 @@
|
|||
package se.hal.action;
|
||||
|
||||
import se.hal.ControllerManager;
|
||||
import se.hal.HalContext;
|
||||
import se.hal.intf.HalAction;
|
||||
import se.hal.intf.HalEventData;
|
||||
import se.hal.struct.Event;
|
||||
import zutil.db.DBConnection;
|
||||
import zutil.log.LogUtil;
|
||||
import zutil.ui.Configurator;
|
||||
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public class SendEventAction implements HalAction {
|
||||
private static final Logger logger = LogUtil.getLogger();
|
||||
|
||||
@Configurator.Configurable("Event Device ID")
|
||||
private int eventId;
|
||||
@Configurator.Configurable("Data to Send")
|
||||
private double data;
|
||||
|
||||
|
||||
@Override
|
||||
public void execute() {
|
||||
try {
|
||||
DBConnection db = HalContext.getDB();
|
||||
Event event = Event.getEvent(db, eventId);
|
||||
if (event != null) {
|
||||
HalEventData dataObj = event.getDeviceConfig().getEventDataClass().newInstance();
|
||||
dataObj.setData(data);
|
||||
event.setDeviceData(dataObj);
|
||||
// Send
|
||||
ControllerManager.getInstance().send(event);
|
||||
}
|
||||
else
|
||||
logger.warning("Unable to find event with id: "+ eventId);
|
||||
} catch (Exception e) {
|
||||
logger.log(Level.SEVERE, null, e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public String toString(){
|
||||
DBConnection db = HalContext.getDB();
|
||||
Event event = null;
|
||||
try{ event = Event.getEvent(db, eventId); } catch (Exception e){} //ignore exception
|
||||
return "Send event: "+ eventId +
|
||||
" ("+(event!=null ? event.getName() : null)+")" +
|
||||
" with data: "+ data;
|
||||
}
|
||||
}
|
||||
293
hal-core/src/se/hal/daemon/SensorDataAggregatorDaemon.java
Normal file
293
hal-core/src/se/hal/daemon/SensorDataAggregatorDaemon.java
Normal file
|
|
@ -0,0 +1,293 @@
|
|||
package se.hal.daemon;
|
||||
|
||||
import se.hal.HalContext;
|
||||
import se.hal.intf.HalDaemon;
|
||||
import se.hal.intf.HalSensorConfig.AggregationMethod;
|
||||
import se.hal.page.HalAlertManager;
|
||||
import se.hal.page.HalAlertManager.AlertLevel;
|
||||
import se.hal.page.HalAlertManager.AlertTTL;
|
||||
import se.hal.page.HalAlertManager.HalAlert;
|
||||
import se.hal.struct.Sensor;
|
||||
import se.hal.util.UTCTimePeriod;
|
||||
import se.hal.util.UTCTimeUtility;
|
||||
import zutil.db.DBConnection;
|
||||
import zutil.db.SQLResultHandler;
|
||||
import zutil.db.handler.SimpleSQLResult;
|
||||
import zutil.log.LogUtil;
|
||||
|
||||
import java.sql.PreparedStatement;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.sql.Statement;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.ScheduledExecutorService;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
public class SensorDataAggregatorDaemon implements HalDaemon {
|
||||
private static final Logger logger = LogUtil.getLogger();
|
||||
|
||||
public enum AggregationPeriodLength{
|
||||
SECOND,
|
||||
MINUTE,
|
||||
FIVE_MINUTES,
|
||||
FIFTEEN_MINUTES,
|
||||
HOUR,
|
||||
DAY,
|
||||
WEEK,
|
||||
MONTH,
|
||||
YEAR
|
||||
}
|
||||
|
||||
private HashMap<Long, HalAlert> alertMap = new HashMap<>();
|
||||
|
||||
|
||||
public void initiate(ScheduledExecutorService executor){
|
||||
executor.scheduleAtFixedRate(this, 0, UTCTimeUtility.FIVE_MINUTES_IN_MS, TimeUnit.MILLISECONDS);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run(){
|
||||
try {
|
||||
List<Sensor> sensorList = Sensor.getLocalSensors(HalContext.getDB());
|
||||
for(Sensor sensor : sensorList){
|
||||
logger.fine("Aggregating sensor_id: " + sensor.getId());
|
||||
aggregateSensor(sensor);
|
||||
}
|
||||
logger.fine("Aggregation Done");
|
||||
} catch (Exception e) {
|
||||
logger.log(Level.SEVERE, "Thread has crashed", e);
|
||||
}
|
||||
}
|
||||
|
||||
public void aggregateSensor(Sensor sensor) {
|
||||
if(sensor.getDeviceConfig() == null){
|
||||
logger.fine("The sensor config is not available - ignoring it");
|
||||
return;
|
||||
}
|
||||
logger.fine("The sensor is of type: " + sensor.getDeviceConfig().getClass().getName());
|
||||
|
||||
long aggregationStartTime = System.currentTimeMillis();
|
||||
|
||||
logger.fine("Aggregating raw data up to a day old into five minute periods");
|
||||
aggregateRawData(sensor, AggregationPeriodLength.FIVE_MINUTES, UTCTimeUtility.DAY_IN_MS, 5, aggregationStartTime);
|
||||
|
||||
logger.fine("Aggregating raw data up to a week old into one hour periods");
|
||||
aggregateRawData(sensor, AggregationPeriodLength.HOUR, UTCTimeUtility.WEEK_IN_MS, 60, aggregationStartTime);
|
||||
|
||||
logger.fine("Aggregating raw data into one day periods");
|
||||
aggregateRawData(sensor, AggregationPeriodLength.DAY, UTCTimeUtility.INFINITY, 60*24, aggregationStartTime);
|
||||
|
||||
logger.fine("Aggregating raw data into one week periods");
|
||||
aggregateRawData(sensor, AggregationPeriodLength.WEEK, UTCTimeUtility.INFINITY, 60*24*7, aggregationStartTime);
|
||||
}
|
||||
|
||||
/**
|
||||
* Aggregate data from the raw DB table to the aggregated table
|
||||
* @param sensor The sensor for to aggregate data
|
||||
* @param ageLimitInMs Only aggregate up to this age
|
||||
*/
|
||||
private void aggregateRawData(Sensor sensor, AggregationPeriodLength aggrPeriodLength, long ageLimitInMs, int expectedSampleCount, long aggregationStartTime){
|
||||
long sensorId = sensor.getId();
|
||||
AggregationMethod aggrMethod = sensor.getDeviceConfig().getAggregationMethod();
|
||||
DBConnection db = HalContext.getDB();
|
||||
PreparedStatement stmt;
|
||||
try {
|
||||
// DB timestamps
|
||||
long dbMaxRawTimestamp = getLatestRawTimestamp(db, sensor);
|
||||
long dbMaxAggrEndTimestamp = getLatestAggrEndTimestamp(db, sensor, aggrPeriodLength);
|
||||
// Periods
|
||||
long periodLatestEndTimestamp = new UTCTimePeriod(aggregationStartTime, aggrPeriodLength).getPreviosPeriod().getEndTimestamp();
|
||||
long periodOldestStartTimestamp = new UTCTimePeriod(aggregationStartTime-ageLimitInMs, aggrPeriodLength).getStartTimestamp();
|
||||
|
||||
// Check if the sensor has stopped responding for 15 min or 3 times the data interval if so alert the user
|
||||
if (aggrPeriodLength == AggregationPeriodLength.FIVE_MINUTES) {
|
||||
long dataInterval = sensor.getDeviceConfig().getDataInterval();
|
||||
if (dataInterval < UTCTimeUtility.FIVE_MINUTES_IN_MS)
|
||||
dataInterval = UTCTimeUtility.FIVE_MINUTES_IN_MS;
|
||||
if (dbMaxRawTimestamp > 0 &&
|
||||
dbMaxRawTimestamp + (dataInterval * 3) < System.currentTimeMillis()) {
|
||||
logger.fine("Sensor \"" + sensorId + "\" stopped sending data at: "+ dbMaxRawTimestamp);
|
||||
|
||||
if (alertMap.containsKey(sensor.getId()))
|
||||
alertMap.get(sensor.getId()).dismiss();
|
||||
|
||||
HalAlert alert = new HalAlert(AlertLevel.WARNING,
|
||||
"Sensor \"" + sensor.getName() + "\" stopped responding",
|
||||
"at <span class=\"timestamp\">"+dbMaxRawTimestamp+"</span>",
|
||||
AlertTTL.DISMISSED);
|
||||
alertMap.put(sensor.getId(), alert);
|
||||
HalAlertManager.getInstance().addAlert(alert);
|
||||
}
|
||||
else {
|
||||
// Sensor has responded remove alert
|
||||
if (alertMap.containsKey(sensor.getId()))
|
||||
alertMap.get(sensor.getId()).dismiss();
|
||||
}
|
||||
}
|
||||
|
||||
// Is there any new data to evaluate?
|
||||
if(dbMaxRawTimestamp < dbMaxAggrEndTimestamp || dbMaxRawTimestamp < periodOldestStartTimestamp){
|
||||
logger.fine("No new data to evaluate - aggregation is up to date");
|
||||
return;
|
||||
}
|
||||
|
||||
// Start processing
|
||||
logger.fine("evaluating period: "+
|
||||
(dbMaxAggrEndTimestamp+1) + "=>" + periodLatestEndTimestamp +
|
||||
" (" + UTCTimeUtility.getDateString(dbMaxAggrEndTimestamp+1) + "=>" +
|
||||
UTCTimeUtility.getDateString(periodLatestEndTimestamp) + ") " +
|
||||
"with expected sample count: " + expectedSampleCount);
|
||||
stmt = db.getPreparedStatement("SELECT *, 1 AS confidence FROM sensor_data_raw"
|
||||
+" WHERE sensor_id == ?"
|
||||
+ " AND timestamp > ?"
|
||||
+ " AND timestamp <= ? "
|
||||
+ " AND timestamp >= ? "
|
||||
+" ORDER BY timestamp ASC");
|
||||
stmt.setLong(1, sensorId);
|
||||
stmt.setLong(2, dbMaxAggrEndTimestamp);
|
||||
stmt.setLong(3, periodLatestEndTimestamp);
|
||||
stmt.setLong(4, periodOldestStartTimestamp);
|
||||
DBConnection.exec(stmt, new DataAggregator(sensorId, aggrPeriodLength, expectedSampleCount, aggrMethod, aggregationStartTime));
|
||||
} catch (SQLException e) {
|
||||
logger.log(Level.SEVERE, null, e);
|
||||
}
|
||||
}
|
||||
|
||||
private long getLatestAggrEndTimestamp(DBConnection db, Sensor sensor, AggregationPeriodLength aggrPeriodLength) throws SQLException {
|
||||
PreparedStatement stmt = db.getPreparedStatement("SELECT MAX(timestamp_end) FROM sensor_data_aggr"
|
||||
+ " WHERE sensor_id == ?"
|
||||
+ " AND timestamp_end-timestamp_start == ?");
|
||||
stmt.setLong(1, sensor.getId());
|
||||
switch(aggrPeriodLength){
|
||||
case SECOND: stmt.setLong(2, UTCTimeUtility.SECOND_IN_MS-1); break;
|
||||
case MINUTE: stmt.setLong(2, UTCTimeUtility.MINUTE_IN_MS-1); break;
|
||||
case FIVE_MINUTES: stmt.setLong(2, UTCTimeUtility.FIVE_MINUTES_IN_MS-1); break;
|
||||
case FIFTEEN_MINUTES: stmt.setLong(2, UTCTimeUtility.FIFTEEN_MINUTES_IN_MS-1); break;
|
||||
case HOUR: stmt.setLong(2, UTCTimeUtility.HOUR_IN_MS-1); break;
|
||||
case DAY: stmt.setLong(2, UTCTimeUtility.DAY_IN_MS-1); break;
|
||||
case WEEK: stmt.setLong(2, UTCTimeUtility.WEEK_IN_MS-1); break;
|
||||
default:
|
||||
throw new IllegalArgumentException("aggregation period length is not supported.");
|
||||
}
|
||||
Long timestamp = DBConnection.exec(stmt, new SimpleSQLResult<Long>());
|
||||
return (timestamp == null ? 0l : timestamp);
|
||||
}
|
||||
private long getLatestRawTimestamp(DBConnection db, Sensor sensor) throws SQLException {
|
||||
PreparedStatement stmt = db.getPreparedStatement(
|
||||
"SELECT MAX(timestamp) FROM sensor_data_raw WHERE sensor_id == ?");
|
||||
stmt.setLong(1, sensor.getId());
|
||||
Long timestamp = DBConnection.exec(stmt, new SimpleSQLResult<Long>());
|
||||
return (timestamp == null ? 0l : timestamp);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Internal class for aggregating data to the aggregated DB table
|
||||
*/
|
||||
private class DataAggregator implements SQLResultHandler<Object>{
|
||||
private final long sensorId;
|
||||
private final AggregationPeriodLength aggrPeriodLength;
|
||||
private final int expectedSampleCount;
|
||||
private final AggregationMethod aggrMethod;
|
||||
private final long aggregationStartTime;
|
||||
|
||||
public DataAggregator(long sensorId, AggregationPeriodLength aggrPeriodLength, int expectedSampleCount, AggregationMethod aggrMethod, long aggregationStartTime) {
|
||||
this.sensorId = sensorId;
|
||||
this.aggrPeriodLength = aggrPeriodLength;
|
||||
this.expectedSampleCount = expectedSampleCount;
|
||||
this.aggrMethod = aggrMethod;
|
||||
this.aggregationStartTime = aggregationStartTime;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object handleQueryResult(Statement stmt, ResultSet result) throws SQLException {
|
||||
try{
|
||||
HalContext.getDB().getConnection().setAutoCommit(false);
|
||||
|
||||
UTCTimePeriod currentPeriod = null;
|
||||
float sum = 0;
|
||||
float confidenceSum = 0;
|
||||
int samples = 0;
|
||||
long highestSequenceId = Sensor.getHighestSequenceId(sensorId);
|
||||
PreparedStatement preparedInsertStmt = HalContext.getDB().getPreparedStatement(
|
||||
"INSERT INTO sensor_data_aggr" +
|
||||
"(sensor_id, sequence_id, timestamp_start, timestamp_end, data, confidence) " +
|
||||
"VALUES(?, ?, ?, ?, ?, ?)");
|
||||
while(result.next()){
|
||||
if(sensorId != result.getInt("sensor_id")){
|
||||
throw new IllegalArgumentException("found entry for aggregation for the wrong sensorId " +
|
||||
"(expecting: "+sensorId+", but was: "+result.getInt("sensor_id")+")");
|
||||
}
|
||||
|
||||
long timestamp = result.getLong("timestamp");
|
||||
UTCTimePeriod dataPeriod = new UTCTimePeriod(timestamp, this.aggrPeriodLength);
|
||||
|
||||
if(currentPeriod == null)
|
||||
currentPeriod = dataPeriod;
|
||||
|
||||
if(!dataPeriod.equals(currentPeriod)){
|
||||
saveData(preparedInsertStmt, confidenceSum, sum, samples, currentPeriod, ++highestSequenceId);
|
||||
|
||||
// Reset variables
|
||||
currentPeriod = dataPeriod;
|
||||
confidenceSum = 0;
|
||||
sum = 0;
|
||||
samples = 0;
|
||||
}
|
||||
sum += result.getFloat("data");
|
||||
confidenceSum += result.getFloat("confidence");
|
||||
++samples;
|
||||
}
|
||||
|
||||
//check if the last period is complete and also should be aggregated
|
||||
if(currentPeriod != null &&
|
||||
currentPeriod.getEndTimestamp() <= new UTCTimePeriod(aggregationStartTime, aggrPeriodLength).getPreviosPeriod().getEndTimestamp()){
|
||||
saveData(preparedInsertStmt, confidenceSum, sum, samples, currentPeriod, ++highestSequenceId);
|
||||
}
|
||||
|
||||
DBConnection.execBatch(preparedInsertStmt);
|
||||
HalContext.getDB().getConnection().commit();
|
||||
|
||||
}catch(Exception e){
|
||||
HalContext.getDB().getConnection().rollback();
|
||||
throw e;
|
||||
}finally{
|
||||
HalContext.getDB().getConnection().setAutoCommit(true);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private void saveData(PreparedStatement preparedInsertStmt, float confidenceSum, float sum, int samples, UTCTimePeriod currentPeriod, long sequenceId) throws SQLException{
|
||||
float aggrConfidence = -1;
|
||||
float data = -1;
|
||||
switch(aggrMethod){
|
||||
case SUM:
|
||||
data = sum;
|
||||
aggrConfidence = confidenceSum / (float)this.expectedSampleCount;
|
||||
break;
|
||||
case AVERAGE:
|
||||
data = sum/samples;
|
||||
aggrConfidence = 1; // ignore confidence for average
|
||||
break;
|
||||
}
|
||||
logger.finer("Saved period: " + currentPeriod +
|
||||
", data: " + data +
|
||||
", confidence: " + aggrConfidence +
|
||||
", samples: " + samples +
|
||||
", aggrMethod: " + aggrMethod);
|
||||
|
||||
preparedInsertStmt.setLong(1, sensorId);
|
||||
preparedInsertStmt.setLong(2, sequenceId);
|
||||
preparedInsertStmt.setLong(3, currentPeriod.getStartTimestamp());
|
||||
preparedInsertStmt.setLong(4, currentPeriod.getEndTimestamp());
|
||||
preparedInsertStmt.setFloat(5, data);
|
||||
preparedInsertStmt.setFloat(6, aggrConfidence);
|
||||
preparedInsertStmt.addBatch();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
124
hal-core/src/se/hal/daemon/SensorDataCleanupDaemon.java
Normal file
124
hal-core/src/se/hal/daemon/SensorDataCleanupDaemon.java
Normal file
|
|
@ -0,0 +1,124 @@
|
|||
package se.hal.daemon;
|
||||
|
||||
import se.hal.HalContext;
|
||||
import se.hal.daemon.SensorDataAggregatorDaemon.AggregationPeriodLength;
|
||||
import se.hal.intf.HalDaemon;
|
||||
import se.hal.struct.Sensor;
|
||||
import se.hal.util.UTCTimeUtility;
|
||||
import zutil.db.DBConnection;
|
||||
import zutil.db.SQLResultHandler;
|
||||
import zutil.log.LogUtil;
|
||||
|
||||
import java.sql.PreparedStatement;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.sql.Statement;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.ScheduledExecutorService;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
public class SensorDataCleanupDaemon implements HalDaemon {
|
||||
private static final Logger logger = LogUtil.getLogger();
|
||||
|
||||
public void initiate(ScheduledExecutorService executor){
|
||||
executor.scheduleAtFixedRate(this, 5000, UTCTimeUtility.FIVE_MINUTES_IN_MS, TimeUnit.MILLISECONDS);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run(){
|
||||
try {
|
||||
List<Sensor> sensorList = Sensor.getSensors(HalContext.getDB());
|
||||
for(Sensor sensor : sensorList){
|
||||
logger.fine("Deleting old aggregated data for sensor id: " + sensor.getId());
|
||||
cleanupSensor(sensor);
|
||||
}
|
||||
logger.fine("Data cleanup done");
|
||||
} catch (Exception e) {
|
||||
logger.log(Level.SEVERE, "Thread has crashed", e);
|
||||
}
|
||||
}
|
||||
|
||||
public void cleanupSensor(Sensor sensor) {
|
||||
if (sensor.getUser() != null) {
|
||||
cleanupSensorData(sensor.getId(), AggregationPeriodLength.FIVE_MINUTES, UTCTimeUtility.DAY_IN_MS); //clear 5-minute data older than a day
|
||||
cleanupSensorData(sensor.getId(), AggregationPeriodLength.HOUR, UTCTimeUtility.WEEK_IN_MS); //clear 1-hour data older than a week
|
||||
//cleanupSensorData(sensor.getId(), AggregationPeriodLength.day, TimeUtility.INFINITY); //clear 1-day data older than infinity
|
||||
//cleanupSensorData(sensor.getId(), AggregationPeriodLength.week, TimeUtility.INFINITY); //clear 1-week data older than infinity
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Will clear periods if they are too old.
|
||||
*
|
||||
* @param sensorId
|
||||
* @Param clearPeriodlength Will clear periods with this length
|
||||
* @param olderThan Data must be older than this many ms to be cleared from the DB
|
||||
*/
|
||||
private void cleanupSensorData(long sensorId, AggregationPeriodLength cleanupPeriodlength, long olderThan){
|
||||
DBConnection db = HalContext.getDB();
|
||||
PreparedStatement stmt = null;
|
||||
try {
|
||||
|
||||
stmt = db.getPreparedStatement("SELECT * FROM sensor_data_aggr"
|
||||
+" WHERE sensor_id == ? "
|
||||
+ "AND timestamp_end-timestamp_start == ?"
|
||||
+ "AND timestamp_end < ?");
|
||||
stmt.setLong(1, sensorId);
|
||||
switch(cleanupPeriodlength){
|
||||
case SECOND: stmt.setLong(2, UTCTimeUtility.SECOND_IN_MS-1); break;
|
||||
case MINUTE: stmt.setLong(2, UTCTimeUtility.MINUTE_IN_MS-1); break;
|
||||
case FIVE_MINUTES: stmt.setLong(2, UTCTimeUtility.FIVE_MINUTES_IN_MS-1); break;
|
||||
case FIFTEEN_MINUTES: stmt.setLong(2, UTCTimeUtility.FIFTEEN_MINUTES_IN_MS-1); break;
|
||||
case HOUR: stmt.setLong(2, UTCTimeUtility.HOUR_IN_MS-1); break;
|
||||
case DAY: stmt.setLong(2, UTCTimeUtility.DAY_IN_MS-1); break;
|
||||
case WEEK: stmt.setLong(2, UTCTimeUtility.WEEK_IN_MS-1); break;
|
||||
default: logger.warning("cleanup period length is not supported."); return;
|
||||
}
|
||||
stmt.setLong(3, System.currentTimeMillis()-olderThan);
|
||||
DBConnection.exec(stmt, new AggregateDataDeleter(sensorId));
|
||||
} catch (SQLException e) {
|
||||
logger.log(Level.SEVERE, null, e);
|
||||
}
|
||||
}
|
||||
|
||||
private class AggregateDataDeleter implements SQLResultHandler<Long>{
|
||||
private long sensorId = -1;
|
||||
|
||||
public AggregateDataDeleter(long sensorId){
|
||||
this.sensorId = sensorId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Long handleQueryResult(Statement stmt, ResultSet result) throws SQLException {
|
||||
long count = 0;
|
||||
try{
|
||||
HalContext.getDB().getConnection().setAutoCommit(false);
|
||||
PreparedStatement preparedDeleteStmt = HalContext.getDB().getPreparedStatement("DELETE FROM sensor_data_aggr WHERE sensor_id == ? AND sequence_id == ?");
|
||||
while(result.next()){
|
||||
if(sensorId != result.getInt("sensor_id")){
|
||||
throw new IllegalArgumentException("Found entry for aggregation for the wrong sensorId (expecting: "+sensorId+", but was: "+result.getInt("sensor_id")+")");
|
||||
}
|
||||
logger.finer("Deleting sensor(id: "+ sensorId +") aggregate entry timestamp: "+ result.getLong("timestamp_start") +" - "+ result.getLong("timestamp_end") + " (" + UTCTimeUtility.timeInMsToString(1+result.getLong("timestamp_end")-result.getLong("timestamp_start")) + ")");
|
||||
preparedDeleteStmt.setInt(1, result.getInt("sensor_id"));
|
||||
preparedDeleteStmt.setLong(2, result.getLong("sequence_id"));
|
||||
preparedDeleteStmt.addBatch();
|
||||
count++;
|
||||
}
|
||||
|
||||
DBConnection.execBatch(preparedDeleteStmt);
|
||||
HalContext.getDB().getConnection().commit();
|
||||
}catch(Exception e){
|
||||
HalContext.getDB().getConnection().rollback();
|
||||
throw e;
|
||||
}finally{
|
||||
HalContext.getDB().getConnection().setAutoCommit(true);
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
49
hal-core/src/se/hal/intf/HalAbstractController.java
Normal file
49
hal-core/src/se/hal/intf/HalAbstractController.java
Normal file
|
|
@ -0,0 +1,49 @@
|
|||
/*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2020 Ziver Koc
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
package se.hal.intf;
|
||||
|
||||
public interface HalAbstractController {
|
||||
|
||||
/**
|
||||
* The framework might create dummy objects so any type of
|
||||
* resource initialization should be handled in this method
|
||||
* and not in the constructor.
|
||||
*/
|
||||
void initialize() throws Exception;
|
||||
|
||||
|
||||
/**
|
||||
* @return the number of registered devices.
|
||||
*/
|
||||
int size();
|
||||
|
||||
|
||||
/**
|
||||
* Close any resources associated with this controller.
|
||||
* This method could be called multiple times, first time
|
||||
* should be handled as normal any subsequent calls should be ignored.
|
||||
*/
|
||||
void close();
|
||||
}
|
||||
14
hal-core/src/se/hal/intf/HalAction.java
Normal file
14
hal-core/src/se/hal/intf/HalAction.java
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
package se.hal.intf;
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Defines a action that will be executed
|
||||
*/
|
||||
public interface HalAction{
|
||||
|
||||
/**
|
||||
* Executes this specific action
|
||||
*/
|
||||
void execute();
|
||||
}
|
||||
14
hal-core/src/se/hal/intf/HalAutoScannableController.java
Normal file
14
hal-core/src/se/hal/intf/HalAutoScannableController.java
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
package se.hal.intf;
|
||||
|
||||
/**
|
||||
* A interface that indicates that the implementing
|
||||
* controller can be auto started when HalServer starts up.
|
||||
*/
|
||||
public interface HalAutoScannableController {
|
||||
|
||||
/**
|
||||
* Indicates if the controller has all the configuration
|
||||
* data and resources needed to be able to initialize correctly
|
||||
*/
|
||||
boolean isAvailable();
|
||||
}
|
||||
30
hal-core/src/se/hal/intf/HalBot.java
Normal file
30
hal-core/src/se/hal/intf/HalBot.java
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
/*
|
||||
* Copyright (c) 2015 Ziver
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
package se.hal.intf;
|
||||
|
||||
public interface HalBot {
|
||||
|
||||
void initialize();
|
||||
|
||||
String respond(String question);
|
||||
}
|
||||
17
hal-core/src/se/hal/intf/HalDaemon.java
Normal file
17
hal-core/src/se/hal/intf/HalDaemon.java
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
package se.hal.intf;
|
||||
|
||||
import java.util.concurrent.ScheduledExecutorService;
|
||||
|
||||
/**
|
||||
* Defines a stand alone process that will run parallel to Hal
|
||||
*/
|
||||
public interface HalDaemon extends Runnable{
|
||||
|
||||
/**
|
||||
* Initialize the daemon.
|
||||
*
|
||||
* @param executor The sceduler that the daemon should register to.
|
||||
*/
|
||||
void initiate(ScheduledExecutorService executor);
|
||||
|
||||
}
|
||||
25
hal-core/src/se/hal/intf/HalDeviceData.java
Normal file
25
hal-core/src/se/hal/intf/HalDeviceData.java
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
package se.hal.intf;
|
||||
|
||||
/**
|
||||
* Interface representing one report from an event
|
||||
*/
|
||||
public abstract class HalDeviceData {
|
||||
|
||||
private long timestamp = -1;
|
||||
|
||||
|
||||
public long getTimestamp(){
|
||||
return timestamp;
|
||||
}
|
||||
public void setTimestamp(long timestamp){
|
||||
this.timestamp = timestamp;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return serialized event data converted to double that will be saved in DB.
|
||||
*/
|
||||
public abstract double getData();
|
||||
|
||||
public abstract void setData(double data);
|
||||
}
|
||||
14
hal-core/src/se/hal/intf/HalDeviceReportListener.java
Normal file
14
hal-core/src/se/hal/intf/HalDeviceReportListener.java
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
package se.hal.intf;
|
||||
|
||||
import se.hal.struct.AbstractDevice;
|
||||
|
||||
/**
|
||||
* A listener interface that will be called when the
|
||||
* Event or Sensor that it is registered to receives a report
|
||||
*
|
||||
* @param <T> is the device type
|
||||
*/
|
||||
public interface HalDeviceReportListener<T extends AbstractDevice> {
|
||||
|
||||
void receivedReport(T device);
|
||||
}
|
||||
21
hal-core/src/se/hal/intf/HalEventConfig.java
Normal file
21
hal-core/src/se/hal/intf/HalEventConfig.java
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
package se.hal.intf;
|
||||
|
||||
/**
|
||||
* Interface representing event type specific configuration data.
|
||||
*/
|
||||
public interface HalEventConfig {
|
||||
|
||||
Class<? extends HalEventController> getEventControllerClass();
|
||||
|
||||
/**
|
||||
* @return the class that should be instantiated and used for data received from this event
|
||||
*/
|
||||
Class<? extends HalEventData> getEventDataClass();
|
||||
|
||||
/**
|
||||
* This method needs to be implemented.
|
||||
* NOTE: it should not compare data and timestamp, only static or unique data for the event type.
|
||||
*/
|
||||
boolean equals(Object obj);
|
||||
|
||||
}
|
||||
26
hal-core/src/se/hal/intf/HalEventController.java
Normal file
26
hal-core/src/se/hal/intf/HalEventController.java
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
package se.hal.intf;
|
||||
|
||||
public interface HalEventController extends HalAbstractController {
|
||||
|
||||
/**
|
||||
* Will register an event type to be handled by this controller
|
||||
*/
|
||||
void register(HalEventConfig eventConfig);
|
||||
|
||||
/**
|
||||
* Deregisters an event from this controller, the controller
|
||||
* will no longer handle that type of event
|
||||
*/
|
||||
void deregister(HalEventConfig eventConfig);
|
||||
|
||||
/**
|
||||
* @param eventConfig the event configuration to target when sending
|
||||
* @param eventData the data to send
|
||||
*/
|
||||
void send(HalEventConfig eventConfig, HalEventData eventData);
|
||||
|
||||
/**
|
||||
* Set a listener that will receive all reports from the the registered Events
|
||||
*/
|
||||
void setListener(HalEventReportListener listener);
|
||||
}
|
||||
8
hal-core/src/se/hal/intf/HalEventData.java
Normal file
8
hal-core/src/se/hal/intf/HalEventData.java
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
package se.hal.intf;
|
||||
|
||||
/**
|
||||
* Interface representing one report from an event
|
||||
*/
|
||||
public abstract class HalEventData extends HalDeviceData{
|
||||
|
||||
}
|
||||
10
hal-core/src/se/hal/intf/HalEventReportListener.java
Normal file
10
hal-core/src/se/hal/intf/HalEventReportListener.java
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
package se.hal.intf;
|
||||
|
||||
/**
|
||||
* Listener to be called by the {@link HalEventController} to report that a event has been received.
|
||||
*/
|
||||
public interface HalEventReportListener {
|
||||
|
||||
void reportReceived(HalEventConfig e, HalEventData d);
|
||||
|
||||
}
|
||||
64
hal-core/src/se/hal/intf/HalJsonPage.java
Normal file
64
hal-core/src/se/hal/intf/HalJsonPage.java
Normal file
|
|
@ -0,0 +1,64 @@
|
|||
package se.hal.intf;
|
||||
|
||||
import zutil.log.LogUtil;
|
||||
import zutil.net.http.HttpHeader;
|
||||
import zutil.net.http.HttpPrintStream;
|
||||
import zutil.parser.DataNode;
|
||||
import zutil.parser.Templator;
|
||||
import zutil.parser.json.JSONWriter;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Map;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
/**
|
||||
* A interface defining a Hal json endpoint
|
||||
*/
|
||||
public abstract class HalJsonPage extends HalWebPage {
|
||||
private static final Logger logger = LogUtil.getLogger();
|
||||
|
||||
public HalJsonPage(String id) {
|
||||
super(id);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void respond(HttpPrintStream out,
|
||||
HttpHeader headers,
|
||||
Map<String, Object> session,
|
||||
Map<String, String> cookie,
|
||||
Map<String, String> request) throws IOException {
|
||||
|
||||
out.setHeader("Content-Type", "application/json");
|
||||
out.setHeader("Access-Control-Allow-Origin", "*");
|
||||
out.setHeader("Pragma", "no-cache");
|
||||
|
||||
JSONWriter writer = new JSONWriter(out);
|
||||
try{
|
||||
writer.write(
|
||||
jsonRespond(session, cookie, request));
|
||||
} catch (Exception e){
|
||||
logger.log(Level.SEVERE, null, e);
|
||||
DataNode root = new DataNode(DataNode.DataType.Map);
|
||||
root.set("error", e.getMessage());
|
||||
writer.write(root);
|
||||
}
|
||||
writer.close();
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public Templator httpRespond(
|
||||
Map<String, Object> session,
|
||||
Map<String, String> cookie,
|
||||
Map<String, String> request) throws Exception {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
protected abstract DataNode jsonRespond(
|
||||
Map<String, Object> session,
|
||||
Map<String, String> cookie,
|
||||
Map<String, String> request) throws Exception;
|
||||
}
|
||||
39
hal-core/src/se/hal/intf/HalSensorConfig.java
Normal file
39
hal-core/src/se/hal/intf/HalSensorConfig.java
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
package se.hal.intf;
|
||||
|
||||
/**
|
||||
* Interface representing sensor type specific configuration data.
|
||||
*/
|
||||
public interface HalSensorConfig {
|
||||
enum AggregationMethod{
|
||||
SUM,
|
||||
AVERAGE
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return the intended data reporting interval in milliseconds.
|
||||
*/
|
||||
long getDataInterval();
|
||||
|
||||
/**
|
||||
* @return which aggregation method that should be used to aggregate the reported data.
|
||||
*/
|
||||
AggregationMethod getAggregationMethod();
|
||||
|
||||
|
||||
/**
|
||||
* @return the Controller class where SensorData should be registered on
|
||||
*/
|
||||
Class<? extends HalSensorController> getSensorControllerClass();
|
||||
|
||||
/**
|
||||
* @return the class that should be instantiated and used for data received from this sensor
|
||||
*/
|
||||
Class<? extends HalSensorData> getSensorDataClass();
|
||||
|
||||
/**
|
||||
* NOTE: it should only static or unique data for the sensor type.
|
||||
* This method is used to associate reported data with registered sensors
|
||||
*/
|
||||
boolean equals(Object obj);
|
||||
}
|
||||
20
hal-core/src/se/hal/intf/HalSensorController.java
Normal file
20
hal-core/src/se/hal/intf/HalSensorController.java
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
package se.hal.intf;
|
||||
|
||||
public interface HalSensorController extends HalAbstractController {
|
||||
|
||||
/**
|
||||
* Will register a sensor type to be handled by this controller
|
||||
*/
|
||||
void register(HalSensorConfig sensorConfig);
|
||||
|
||||
/**
|
||||
* Deregisters a sensor from this controller, the controller
|
||||
* will no longer handle that type of sensor
|
||||
*/
|
||||
void deregister(HalSensorConfig sensorConfig);
|
||||
|
||||
/**
|
||||
* Set a listener that will receive all reports from the the registered Sensors
|
||||
*/
|
||||
void setListener(HalSensorReportListener listener);
|
||||
}
|
||||
8
hal-core/src/se/hal/intf/HalSensorData.java
Normal file
8
hal-core/src/se/hal/intf/HalSensorData.java
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
package se.hal.intf;
|
||||
|
||||
/**
|
||||
* Interface representing one data report from a sensor.
|
||||
*/
|
||||
public abstract class HalSensorData extends HalDeviceData{
|
||||
|
||||
}
|
||||
10
hal-core/src/se/hal/intf/HalSensorReportListener.java
Normal file
10
hal-core/src/se/hal/intf/HalSensorReportListener.java
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
package se.hal.intf;
|
||||
|
||||
/**
|
||||
* Listener to be called by the {@link HalSensorController} to report that sensor data has been received.
|
||||
*/
|
||||
public interface HalSensorReportListener {
|
||||
|
||||
void reportReceived(HalSensorConfig s, HalSensorData d);
|
||||
|
||||
}
|
||||
33
hal-core/src/se/hal/intf/HalSpeechToText.java
Normal file
33
hal-core/src/se/hal/intf/HalSpeechToText.java
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
/*
|
||||
* Copyright (c) 2013 ezivkoc
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
package se.hal.intf;
|
||||
|
||||
/**
|
||||
* A generic interface for Speech to Text analysis
|
||||
*/
|
||||
public interface HalSpeechToText {
|
||||
|
||||
void initialize();
|
||||
|
||||
String listen();
|
||||
}
|
||||
33
hal-core/src/se/hal/intf/HalTextToSpeech.java
Normal file
33
hal-core/src/se/hal/intf/HalTextToSpeech.java
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
/*
|
||||
* Copyright (c) 2013 ezivkoc
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
package se.hal.intf;
|
||||
|
||||
/**
|
||||
* A Common interface for Text to Speech generation
|
||||
*/
|
||||
public interface HalTextToSpeech {
|
||||
|
||||
void initialize();
|
||||
|
||||
void speak(String msg);
|
||||
}
|
||||
22
hal-core/src/se/hal/intf/HalTrigger.java
Normal file
22
hal-core/src/se/hal/intf/HalTrigger.java
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
package se.hal.intf;
|
||||
|
||||
|
||||
/**
|
||||
* A interface that declares a trigger/condition that
|
||||
* needs to be validated before an action can be run
|
||||
*/
|
||||
public interface HalTrigger{
|
||||
|
||||
/**
|
||||
* Evaluates if this trigger has passed. If the trigger is
|
||||
* true then this method will return true until the {@link #reset()}
|
||||
* method is called.
|
||||
*/
|
||||
boolean evaluate();
|
||||
|
||||
/**
|
||||
* Reset the evaluation to false.
|
||||
*/
|
||||
void reset();
|
||||
|
||||
}
|
||||
84
hal-core/src/se/hal/intf/HalWebPage.java
Normal file
84
hal-core/src/se/hal/intf/HalWebPage.java
Normal file
|
|
@ -0,0 +1,84 @@
|
|||
package se.hal.intf;
|
||||
|
||||
import se.hal.HalContext;
|
||||
import se.hal.page.HalAlertManager;
|
||||
import se.hal.struct.User;
|
||||
import zutil.db.DBConnection;
|
||||
import zutil.io.file.FileUtil;
|
||||
import zutil.net.http.HttpHeader;
|
||||
import zutil.net.http.HttpPage;
|
||||
import zutil.net.http.HttpPrintStream;
|
||||
import zutil.parser.Templator;
|
||||
import zutil.parser.json.JSONWriter;
|
||||
import zutil.ui.Navigation;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
public abstract class HalWebPage implements HttpPage{
|
||||
private static final String TEMPLATE = "resource/web/main_index.tmpl";
|
||||
private static Navigation rootNav = Navigation.createRootNav();
|
||||
private static Navigation userNav = Navigation.createRootNav();
|
||||
|
||||
private String pageId;
|
||||
private boolean showSubNav;
|
||||
|
||||
public HalWebPage(String id){
|
||||
this.pageId = id;
|
||||
this.showSubNav = true;
|
||||
}
|
||||
|
||||
public String getId(){
|
||||
return pageId;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void respond(HttpPrintStream out, HttpHeader header,
|
||||
Map<String, Object> session, Map<String, String> cookie,
|
||||
Map<String, String> request) throws IOException {
|
||||
|
||||
try {
|
||||
DBConnection db = HalContext.getDB();
|
||||
|
||||
Templator tmpl = new Templator(FileUtil.find(TEMPLATE));
|
||||
tmpl.set("user", User.getLocalUser(db));
|
||||
tmpl.set("showSubNav", showSubNav);
|
||||
if (showSubNav) {
|
||||
List<Navigation> breadcrumb = Navigation.getBreadcrumb(Navigation.getPagedNavigation(header));
|
||||
if (!breadcrumb.isEmpty())
|
||||
tmpl.set("subNav", breadcrumb.get(1).createPagedNavInstance(header).getSubNavs());
|
||||
}
|
||||
tmpl.set("rootNav", rootNav.createPagedNavInstance(header).getSubNavs());
|
||||
tmpl.set("userNav", userNav.createPagedNavInstance(header).getSubNavs());
|
||||
tmpl.set("content", httpRespond(session, cookie, request));
|
||||
tmpl.set("alerts", HalAlertManager.getInstance().generateAlerts()); // do last so we don't miss any alerts
|
||||
out.print(tmpl.compile());
|
||||
} catch (Exception e) {
|
||||
throw new IOException(e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Defines if the sub-navigation should be shown on the page
|
||||
*/
|
||||
protected void showSubNav(boolean show) {
|
||||
this.showSubNav = show;
|
||||
}
|
||||
|
||||
public static Navigation getRootNav(){
|
||||
return rootNav;
|
||||
}
|
||||
public static Navigation getUserNav(){
|
||||
return userNav;
|
||||
}
|
||||
|
||||
|
||||
public abstract Templator httpRespond(
|
||||
Map<String, Object> session,
|
||||
Map<String, String> cookie,
|
||||
Map<String, String> request)
|
||||
throws Exception;
|
||||
|
||||
}
|
||||
122
hal-core/src/se/hal/page/EventConfigWebPage.java
Normal file
122
hal-core/src/se/hal/page/EventConfigWebPage.java
Normal file
|
|
@ -0,0 +1,122 @@
|
|||
package se.hal.page;
|
||||
|
||||
import se.hal.ControllerManager;
|
||||
import se.hal.HalContext;
|
||||
import se.hal.intf.HalWebPage;
|
||||
import se.hal.page.HalAlertManager.AlertLevel;
|
||||
import se.hal.page.HalAlertManager.AlertTTL;
|
||||
import se.hal.page.HalAlertManager.HalAlert;
|
||||
import se.hal.struct.ClassConfigurationData;
|
||||
import se.hal.struct.Event;
|
||||
import se.hal.struct.User;
|
||||
import zutil.ObjectUtil;
|
||||
import zutil.db.DBConnection;
|
||||
import zutil.io.file.FileUtil;
|
||||
import zutil.log.LogUtil;
|
||||
import zutil.parser.Templator;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Map;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
public class EventConfigWebPage extends HalWebPage {
|
||||
private static final Logger logger = LogUtil.getLogger();
|
||||
private static final String TEMPLATE = "resource/web/event_config.tmpl";
|
||||
|
||||
private ArrayList<ClassConfigurationData> eventConfigurations;
|
||||
|
||||
|
||||
public EventConfigWebPage() {
|
||||
super("event_config");
|
||||
super.getRootNav().createSubNav("Settings").createSubNav(this.getId(), "Event Settings").setWeight(200);
|
||||
|
||||
eventConfigurations = new ArrayList<>();
|
||||
for(Class c : ControllerManager.getInstance().getAvailableEvents())
|
||||
eventConfigurations.add(new ClassConfigurationData(c));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Templator httpRespond(
|
||||
Map<String, Object> session,
|
||||
Map<String, String> cookie,
|
||||
Map<String, String> request)
|
||||
throws Exception{
|
||||
|
||||
DBConnection db = HalContext.getDB();
|
||||
User localUser = User.getLocalUser(db);
|
||||
|
||||
// Save new input
|
||||
if(request.containsKey("action")){
|
||||
int id = (ObjectUtil.isEmpty(request.get("id")) ? -1 : Integer.parseInt(request.get("id")));
|
||||
Event event;
|
||||
|
||||
switch(request.get("action")) {
|
||||
// Local events
|
||||
case "create_local_event":
|
||||
logger.info("Creating new event: " + request.get("name"));
|
||||
event = new Event();
|
||||
event.setName(request.get("name"));
|
||||
event.setType(request.get("type"));
|
||||
event.setUser(localUser);
|
||||
event.getDeviceConfigurator().setValues(request).applyConfiguration();
|
||||
event.save(db);
|
||||
ControllerManager.getInstance().register(event);
|
||||
|
||||
HalAlertManager.getInstance().addAlert(new HalAlert(
|
||||
AlertLevel.SUCCESS, "Successfully created new event: " + event.getName(), AlertTTL.ONE_VIEW));
|
||||
break;
|
||||
|
||||
case "modify_local_event":
|
||||
event = Event.getEvent(db, id);
|
||||
if (event != null) {
|
||||
logger.info("Modifying event: " + event.getName());
|
||||
event.setName(request.get("name"));
|
||||
event.setType(request.get("type"));
|
||||
event.setUser(localUser);
|
||||
event.getDeviceConfigurator().setValues(request).applyConfiguration();
|
||||
event.save(db);
|
||||
|
||||
HalAlertManager.getInstance().addAlert(new HalAlert(
|
||||
AlertLevel.SUCCESS, "Successfully saved event: "+event.getName(), AlertTTL.ONE_VIEW));
|
||||
} else {
|
||||
logger.warning("Unknown event id: " + id);
|
||||
HalAlertManager.getInstance().addAlert(new HalAlert(
|
||||
AlertLevel.ERROR, "Unknown event id: " + id, AlertTTL.ONE_VIEW));
|
||||
}
|
||||
break;
|
||||
|
||||
case "remove_local_event":
|
||||
event = Event.getEvent(db, id);
|
||||
if (event != null) {
|
||||
logger.info("Removing event: " + event.getName());
|
||||
ControllerManager.getInstance().deregister(event);
|
||||
event.delete(db);
|
||||
|
||||
HalAlertManager.getInstance().addAlert(new HalAlert(
|
||||
AlertLevel.SUCCESS, "Successfully removed event: "+event.getName(), AlertTTL.ONE_VIEW));
|
||||
} else {
|
||||
logger.warning("Unknown event id: " + id);
|
||||
HalAlertManager.getInstance().addAlert(new HalAlert(
|
||||
AlertLevel.ERROR, "Unknown event id: "+id, AlertTTL.ONE_VIEW));
|
||||
}
|
||||
break;
|
||||
|
||||
case "remove_all_detected_events":
|
||||
ControllerManager.getInstance().clearDetectedEvents();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Output
|
||||
Templator tmpl = new Templator(FileUtil.find(TEMPLATE));
|
||||
tmpl.set("user", localUser);
|
||||
tmpl.set("localEvents", Event.getLocalEvents(db));
|
||||
tmpl.set("localEventConf", eventConfigurations);
|
||||
tmpl.set("detectedEvents", ControllerManager.getInstance().getDetectedEvents());
|
||||
tmpl.set("availableEvents", ControllerManager.getInstance().getAvailableEvents());
|
||||
|
||||
return tmpl;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
87
hal-core/src/se/hal/page/EventOverviewWebPage.java
Normal file
87
hal-core/src/se/hal/page/EventOverviewWebPage.java
Normal file
|
|
@ -0,0 +1,87 @@
|
|||
package se.hal.page;
|
||||
|
||||
import se.hal.ControllerManager;
|
||||
import se.hal.HalContext;
|
||||
import se.hal.intf.HalWebPage;
|
||||
import se.hal.struct.Event;
|
||||
import se.hal.struct.devicedata.SwitchEventData;
|
||||
import se.hal.util.DeviceNameComparator;
|
||||
import se.hal.util.HistoryDataListSqlResult;
|
||||
import se.hal.util.HistoryDataListSqlResult.HistoryData;
|
||||
import zutil.ObjectUtil;
|
||||
import zutil.db.DBConnection;
|
||||
import zutil.io.file.FileUtil;
|
||||
import zutil.log.LogUtil;
|
||||
import zutil.parser.Templator;
|
||||
|
||||
import java.sql.PreparedStatement;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
public class EventOverviewWebPage extends HalWebPage {
|
||||
private static final Logger logger = LogUtil.getLogger();
|
||||
private static final int HISTORY_LIMIT = 200;
|
||||
private static final String OVERVIEW_TEMPLATE = "resource/web/event_overview.tmpl";
|
||||
private static final String DETAIL_TEMPLATE = "resource/web/event_detail.tmpl";
|
||||
|
||||
|
||||
public EventOverviewWebPage(){
|
||||
super("event_overview");
|
||||
super.getRootNav().createSubNav("Events").createSubNav(this.getId(), "Overview");
|
||||
}
|
||||
|
||||
@Override
|
||||
public Templator httpRespond(
|
||||
Map<String, Object> session,
|
||||
Map<String, String> cookie,
|
||||
Map<String, String> request)
|
||||
throws Exception{
|
||||
|
||||
DBConnection db = HalContext.getDB();
|
||||
|
||||
if (request.containsKey("action")) {
|
||||
int id = (ObjectUtil.isEmpty(request.get("action_id")) ? -1 : Integer.parseInt(request.get("action_id")));
|
||||
|
||||
// change event data
|
||||
SwitchEventData eventData = new SwitchEventData();
|
||||
if (request.containsKey("enabled") && "on".equals(request.get("enabled")))
|
||||
eventData.turnOn();
|
||||
else
|
||||
eventData.turnOff();
|
||||
|
||||
logger.info("Modifying Event(" + id + ") state: " + eventData.toString());
|
||||
Event event = Event.getEvent(db, id);
|
||||
event.setDeviceData(eventData);
|
||||
ControllerManager.getInstance().send(event);
|
||||
}
|
||||
|
||||
int id = (ObjectUtil.isEmpty(request.get("id")) ? -1 : Integer.parseInt(request.get("id")));
|
||||
|
||||
// Save new input
|
||||
if (id >= 0) {
|
||||
Event event = Event.getEvent(db, id);
|
||||
|
||||
// get history data
|
||||
PreparedStatement stmt = db.getPreparedStatement(
|
||||
"SELECT * FROM event_data_raw WHERE event_id == ? ORDER BY timestamp DESC LIMIT ?");
|
||||
stmt.setLong(1, event.getId());
|
||||
stmt.setLong(2, HISTORY_LIMIT);
|
||||
List<HistoryData> history = DBConnection.exec(stmt, new HistoryDataListSqlResult());
|
||||
|
||||
Templator tmpl = new Templator(FileUtil.find(DETAIL_TEMPLATE));
|
||||
tmpl.set("event", event);
|
||||
tmpl.set("history", history);
|
||||
return tmpl;
|
||||
}
|
||||
else {
|
||||
Event[] events = Event.getLocalEvents(db).toArray(new Event[0]);
|
||||
Arrays.sort(events, DeviceNameComparator.getInstance());
|
||||
|
||||
Templator tmpl = new Templator(FileUtil.find(OVERVIEW_TEMPLATE));
|
||||
tmpl.set("events", events);
|
||||
return tmpl;
|
||||
}
|
||||
}
|
||||
}
|
||||
163
hal-core/src/se/hal/page/HalAlertManager.java
Normal file
163
hal-core/src/se/hal/page/HalAlertManager.java
Normal file
|
|
@ -0,0 +1,163 @@
|
|||
package se.hal.page;
|
||||
|
||||
import zutil.io.file.FileUtil;
|
||||
import zutil.log.LogUtil;
|
||||
import zutil.net.http.HttpHeader;
|
||||
import zutil.net.http.HttpPage;
|
||||
import zutil.net.http.HttpPrintStream;
|
||||
import zutil.parser.Templator;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.*;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
public class HalAlertManager implements HttpPage {
|
||||
private static final Logger logger = LogUtil.getLogger();
|
||||
private static final String TMPL_PATH = "resource/web/main_alerts.tmpl";
|
||||
private static final String PAGE_NAME = "alert";
|
||||
private static HalAlertManager instance;
|
||||
|
||||
public enum AlertLevel{
|
||||
ERROR,
|
||||
WARNING,
|
||||
SUCCESS,
|
||||
INFO
|
||||
}
|
||||
public enum AlertTTL{
|
||||
ONE_VIEW,
|
||||
DISMISSED
|
||||
}
|
||||
|
||||
private List<HalAlert> alerts = new LinkedList<>();
|
||||
|
||||
|
||||
private HalAlertManager(){}
|
||||
|
||||
public String getUrl(){
|
||||
return "/" + PAGE_NAME;
|
||||
}
|
||||
|
||||
public void addAlert(HalAlert alert) {
|
||||
alerts.remove(alert); // We don't want to flood the user with duplicate alerts
|
||||
alerts.add(alert);
|
||||
}
|
||||
|
||||
|
||||
public Templator generateAlerts() {
|
||||
try {
|
||||
// clone alert list and update ttl of alerts
|
||||
List<HalAlert> alertsClone = new ArrayList<>(alerts.size());
|
||||
for(Iterator<HalAlert> it = alerts.iterator(); it.hasNext(); ) {
|
||||
HalAlert alert = it.next();
|
||||
alertsClone.add(alert);
|
||||
alert.ttl--;
|
||||
|
||||
if (alert.ttl <= 0) { // if alert is to old, remove it
|
||||
logger.fine("Alert dismissed with end of life, alert id: "+ alert.id);
|
||||
it.remove();
|
||||
}
|
||||
}
|
||||
|
||||
Templator tmpl = new Templator(FileUtil.find(TMPL_PATH));
|
||||
tmpl.set("serviceUrl", getUrl());
|
||||
tmpl.set("alerts", alertsClone);
|
||||
return tmpl;
|
||||
} catch (IOException e) {
|
||||
logger.log(Level.SEVERE, null, e);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void respond(HttpPrintStream out,
|
||||
HttpHeader headers,
|
||||
Map<String, Object> session,
|
||||
Map<String, String> cookie,
|
||||
Map<String, String> request) throws IOException {
|
||||
|
||||
if (request.containsKey("action")) {
|
||||
if (request.get("action").equals("dismiss")) {
|
||||
// parse alert id
|
||||
int id = Integer.parseInt(request.get("id"));
|
||||
// Find alert
|
||||
for(Iterator<HalAlert> it = alerts.iterator(); it.hasNext(); ) {
|
||||
HalAlert alert = it.next();
|
||||
if (alert.getId() == id) {
|
||||
logger.fine("User dismissed alert id: "+ id);
|
||||
it.remove();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
public static void initialize(){
|
||||
instance = new HalAlertManager();
|
||||
}
|
||||
public static HalAlertManager getInstance(){
|
||||
return instance;
|
||||
}
|
||||
|
||||
|
||||
public static class HalAlert {
|
||||
private static int nextId = 0;
|
||||
|
||||
private int id;
|
||||
private AlertLevel level;
|
||||
private String title;
|
||||
private String msg;
|
||||
private int ttl;
|
||||
|
||||
|
||||
public HalAlert(AlertLevel level, String title, AlertTTL ttl) {
|
||||
this(level, title, null, ttl);
|
||||
}
|
||||
public HalAlert(AlertLevel level, String title, String msg, AlertTTL ttl) {
|
||||
this.id = nextId++;
|
||||
this.level = level;
|
||||
this.title = title;
|
||||
this.msg = msg;
|
||||
setTTL(ttl);
|
||||
}
|
||||
|
||||
|
||||
public int getId() {
|
||||
return id;
|
||||
}
|
||||
public AlertLevel getLevel() {
|
||||
return level;
|
||||
}
|
||||
public boolean isError(){ return level == AlertLevel.ERROR; }
|
||||
public boolean isWarning(){ return level == AlertLevel.WARNING; }
|
||||
public boolean isSuccess(){ return level == AlertLevel.SUCCESS; }
|
||||
public boolean isInfo(){ return level == AlertLevel.INFO; }
|
||||
public String getTitle() {
|
||||
return title;
|
||||
}
|
||||
public String getMessage() {
|
||||
return msg;
|
||||
}
|
||||
|
||||
public void setTTL(AlertTTL ttl) {
|
||||
switch (ttl){
|
||||
case ONE_VIEW: this.ttl = 1; break;
|
||||
case DISMISSED: this.ttl = Integer.MAX_VALUE; break;
|
||||
}
|
||||
}
|
||||
public void dismiss(){
|
||||
ttl = -1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (obj instanceof HalAlert)
|
||||
return level == ((HalAlert) obj).level &&
|
||||
title.equals(((HalAlert) obj).title);
|
||||
return super.equals(obj);
|
||||
}
|
||||
}
|
||||
}
|
||||
88
hal-core/src/se/hal/page/MapJsonPage.java
Normal file
88
hal-core/src/se/hal/page/MapJsonPage.java
Normal file
|
|
@ -0,0 +1,88 @@
|
|||
package se.hal.page;
|
||||
|
||||
import se.hal.HalContext;
|
||||
import se.hal.intf.HalJsonPage;
|
||||
import se.hal.struct.AbstractDevice;
|
||||
import se.hal.struct.Event;
|
||||
import se.hal.struct.Sensor;
|
||||
import zutil.db.DBConnection;
|
||||
import zutil.log.LogUtil;
|
||||
import zutil.net.http.HttpHeader;
|
||||
import zutil.net.http.HttpPrintStream;
|
||||
import zutil.parser.DataNode;
|
||||
|
||||
import java.sql.SQLException;
|
||||
import java.util.Map;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
/**
|
||||
* TODO: This json endpoint might not be needed as we have SensorJsonPage?
|
||||
*/
|
||||
public class MapJsonPage extends HalJsonPage {
|
||||
private static final Logger logger = LogUtil.getLogger();
|
||||
|
||||
public MapJsonPage() {
|
||||
super("data/map");
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public DataNode jsonRespond(
|
||||
Map<String, Object> session,
|
||||
Map<String, String> cookie,
|
||||
Map<String, String> request) throws Exception {
|
||||
|
||||
DBConnection db = HalContext.getDB();
|
||||
DataNode root = new DataNode(DataNode.DataType.Map);
|
||||
|
||||
if ("getdata".equals(request.get("action"))) {
|
||||
getDeviceNode(db, root);
|
||||
} else if ("save".equals(request.get("action"))) {
|
||||
int id = Integer.parseInt(request.get("id"));
|
||||
AbstractDevice device = null;
|
||||
|
||||
logger.info("Saving Sensor coordinates.");
|
||||
|
||||
if ("sensor".equals(request.get("type")))
|
||||
device = Sensor.getSensor(db, id);
|
||||
else if ("event".equals(request.get("type")))
|
||||
device = Event.getEvent(db, id);
|
||||
|
||||
device.setX(Float.parseFloat(request.get("x")));
|
||||
device.setY(Float.parseFloat(request.get("y")));
|
||||
device.save(db);
|
||||
}
|
||||
return root;
|
||||
}
|
||||
|
||||
|
||||
private void getDeviceNode(DBConnection db, DataNode root) throws SQLException {
|
||||
DataNode sensorsNode = new DataNode(DataNode.DataType.List);
|
||||
|
||||
for (Sensor sensor : Sensor.getLocalSensors(db)) {
|
||||
DataNode sensorNode = getDeviceNode(sensor);
|
||||
sensorNode.set("data", ""+sensor.getDeviceData());
|
||||
sensorsNode.add(sensorNode);
|
||||
}
|
||||
root.set("sensors", sensorsNode);
|
||||
|
||||
DataNode eventsNode = new DataNode(DataNode.DataType.List);
|
||||
|
||||
for (Event event : Event.getLocalEvents(db)) {
|
||||
DataNode eventNode = getDeviceNode(event);
|
||||
eventNode.set("data", ""+event.getDeviceData());
|
||||
eventsNode.add(eventNode);
|
||||
}
|
||||
root.set("events", eventsNode);
|
||||
}
|
||||
|
||||
private DataNode getDeviceNode(AbstractDevice device) {
|
||||
DataNode deviceNode = new DataNode(DataNode.DataType.Map);
|
||||
deviceNode.set("id", device.getId());
|
||||
deviceNode.set("name", device.getName());
|
||||
deviceNode.set("x", device.getX());
|
||||
deviceNode.set("y", device.getY());
|
||||
return deviceNode;
|
||||
}
|
||||
|
||||
}
|
||||
91
hal-core/src/se/hal/page/MapWebPage.java
Normal file
91
hal-core/src/se/hal/page/MapWebPage.java
Normal file
|
|
@ -0,0 +1,91 @@
|
|||
package se.hal.page;
|
||||
|
||||
import se.hal.HalContext;
|
||||
import se.hal.intf.HalWebPage;
|
||||
import zutil.io.file.FileUtil;
|
||||
import zutil.net.http.HttpHeader;
|
||||
import zutil.net.http.HttpPrintStream;
|
||||
import zutil.net.http.multipart.MultipartField;
|
||||
import zutil.net.http.multipart.MultipartFileField;
|
||||
import zutil.net.http.multipart.MultipartParser;
|
||||
import zutil.parser.Base64Decoder;
|
||||
import zutil.parser.Base64Encoder;
|
||||
import zutil.parser.Templator;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.sql.SQLException;
|
||||
import java.util.Map;
|
||||
|
||||
public class MapWebPage extends HalWebPage {
|
||||
private static final String TEMPLATE = "resource/web/map.tmpl";
|
||||
|
||||
private String bgType;
|
||||
private byte[] bgImage;
|
||||
|
||||
|
||||
public MapWebPage() {
|
||||
super("map");
|
||||
super.getRootNav().createSubNav(this.getId(), "Map").setWeight(-100);
|
||||
super.showSubNav(false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void respond(HttpPrintStream out, HttpHeader header,
|
||||
Map<String, Object> session, Map<String, String> cookie,
|
||||
Map<String, String> request) throws IOException {
|
||||
|
||||
if ("POST".equals(header.getRequestType())) {
|
||||
for (MultipartField field : new MultipartParser(header)) {
|
||||
if (field instanceof MultipartFileField) {
|
||||
MultipartFileField file = (MultipartFileField) field;
|
||||
String ext = FileUtil.getFileExtension(file.getFilename());
|
||||
if (ext.equals("jpg") || ext.equals("png") || ext.equals("svg") || ext.equals("gif")) {
|
||||
try {
|
||||
saveBgImage(ext, file.getContent());
|
||||
out.println("Upload successful: " + file.getFilename());
|
||||
} catch (SQLException e) {
|
||||
e.printStackTrace();
|
||||
out.println("Upload error: " + e.getMessage());
|
||||
}
|
||||
bgImage = null; // reload image from db
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (request.containsKey("bgimage")) {
|
||||
if (bgImage == null)
|
||||
loadBgImage();
|
||||
if (bgImage == null)
|
||||
out.setResponseStatusCode(404);
|
||||
else {
|
||||
out.setHeader("Content-Type", "image/" + bgType);
|
||||
out.setHeader("Content-Length", "" + bgImage.length);
|
||||
out.write(bgImage);
|
||||
}
|
||||
} else { // Run default Hal behaviour
|
||||
super.respond(out, header, session, cookie, request);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Templator httpRespond(Map<String, Object> session,
|
||||
Map<String, String> cookie,
|
||||
Map<String, String> request) throws Exception {
|
||||
|
||||
Templator tmpl = new Templator(FileUtil.find(TEMPLATE));
|
||||
return tmpl;
|
||||
}
|
||||
|
||||
|
||||
private void loadBgImage() {
|
||||
String property = HalContext.getStringProperty(HalContext.PROPERTY_MAP_BACKGROUND_IMAGE);
|
||||
if (property != null) {
|
||||
String[] split = property.split(",", 2);
|
||||
bgType = split[0];
|
||||
bgImage = Base64Decoder.decodeToByte(split[1]);
|
||||
}
|
||||
}
|
||||
|
||||
private void saveBgImage(String type, byte[] data) throws SQLException {
|
||||
HalContext.setProperty(HalContext.PROPERTY_MAP_BACKGROUND_IMAGE, type + "," + Base64Encoder.encode(data));
|
||||
}
|
||||
}
|
||||
50
hal-core/src/se/hal/page/PluginConfigWebPage.java
Normal file
50
hal-core/src/se/hal/page/PluginConfigWebPage.java
Normal file
|
|
@ -0,0 +1,50 @@
|
|||
package se.hal.page;
|
||||
|
||||
import se.hal.ControllerManager;
|
||||
import se.hal.HalContext;
|
||||
import se.hal.HalServer;
|
||||
import se.hal.intf.HalWebPage;
|
||||
import se.hal.page.HalAlertManager.AlertLevel;
|
||||
import se.hal.page.HalAlertManager.AlertTTL;
|
||||
import se.hal.page.HalAlertManager.HalAlert;
|
||||
import se.hal.struct.devicedata.SwitchEventData;
|
||||
import zutil.ObjectUtil;
|
||||
import zutil.db.DBConnection;
|
||||
import zutil.io.file.FileUtil;
|
||||
import zutil.parser.Templator;
|
||||
import zutil.plugin.PluginManager;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
public class PluginConfigWebPage extends HalWebPage {
|
||||
private static final String TEMPLATE = "resource/web/plugin_config.tmpl";
|
||||
|
||||
|
||||
public PluginConfigWebPage(){
|
||||
super("plugins");
|
||||
super.getRootNav().createSubNav("Settings").createSubNav(this.getId(), "Plugins").setWeight(500);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Templator httpRespond(
|
||||
Map<String, Object> session,
|
||||
Map<String, String> cookie,
|
||||
Map<String, String> request)
|
||||
throws Exception{
|
||||
|
||||
if (request.containsKey("action")) {
|
||||
String name = request.get("action_id");
|
||||
HalServer.setPluginEnabled(name,
|
||||
(request.containsKey("enabled") && "on".equals(request.get("enabled"))));
|
||||
|
||||
HalAlertManager.getInstance().addAlert(new HalAlert(
|
||||
AlertLevel.SUCCESS, "Successfully updated plugin " + name + ", change will take affect after restart.", AlertTTL.ONE_VIEW));
|
||||
}
|
||||
|
||||
Templator tmpl = new Templator(FileUtil.find(TEMPLATE));
|
||||
tmpl.set("plugins", HalServer.getPlugins());
|
||||
tmpl.set("controllers", ControllerManager.getInstance().getControllers());
|
||||
return tmpl;
|
||||
}
|
||||
}
|
||||
34
hal-core/src/se/hal/page/PropertyConfigWebPage.java
Normal file
34
hal-core/src/se/hal/page/PropertyConfigWebPage.java
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
package se.hal.page;
|
||||
|
||||
import se.hal.HalContext;
|
||||
import se.hal.intf.HalWebPage;
|
||||
import zutil.db.DBConnection;
|
||||
import zutil.io.file.FileUtil;
|
||||
import zutil.parser.Templator;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
public class PropertyConfigWebPage extends HalWebPage {
|
||||
private static final String TEMPLATE = "resource/web/property_config.tmpl";
|
||||
|
||||
|
||||
public PropertyConfigWebPage(){
|
||||
super("properties");
|
||||
super.getRootNav().createSubNav("Settings").createSubNav(this.getId(), "Properties");
|
||||
}
|
||||
|
||||
@Override
|
||||
public Templator httpRespond(
|
||||
Map<String, Object> session,
|
||||
Map<String, String> cookie,
|
||||
Map<String, String> request)
|
||||
throws Exception{
|
||||
|
||||
Map<String,String> properties = HalContext.getProperties();
|
||||
|
||||
Templator tmpl = new Templator(FileUtil.find(TEMPLATE));
|
||||
tmpl.set("properties", properties.entrySet());
|
||||
return tmpl;
|
||||
|
||||
}
|
||||
}
|
||||
187
hal-core/src/se/hal/page/SensorConfigWebPage.java
Normal file
187
hal-core/src/se/hal/page/SensorConfigWebPage.java
Normal file
|
|
@ -0,0 +1,187 @@
|
|||
package se.hal.page;
|
||||
|
||||
import se.hal.ControllerManager;
|
||||
import se.hal.HalContext;
|
||||
import se.hal.intf.HalWebPage;
|
||||
import se.hal.page.HalAlertManager.AlertLevel;
|
||||
import se.hal.page.HalAlertManager.AlertTTL;
|
||||
import se.hal.page.HalAlertManager.HalAlert;
|
||||
import se.hal.struct.ClassConfigurationData;
|
||||
import se.hal.struct.Sensor;
|
||||
import se.hal.struct.User;
|
||||
import zutil.ObjectUtil;
|
||||
import zutil.db.DBConnection;
|
||||
import zutil.io.file.FileUtil;
|
||||
import zutil.log.LogUtil;
|
||||
import zutil.parser.Templator;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Map;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
public class SensorConfigWebPage extends HalWebPage {
|
||||
private static final Logger logger = LogUtil.getLogger();
|
||||
private static final String TEMPLATE = "resource/web/sensor_config.tmpl";
|
||||
|
||||
private ArrayList<ClassConfigurationData> sensorConfigurations;
|
||||
|
||||
|
||||
public SensorConfigWebPage() {
|
||||
super("sensor_config");
|
||||
super.getRootNav().createSubNav("Settings").createSubNav(this.getId(), "Sensor Settings").setWeight(100);
|
||||
|
||||
sensorConfigurations = new ArrayList<>();
|
||||
for(Class c : ControllerManager.getInstance().getAvailableSensors())
|
||||
sensorConfigurations.add(new ClassConfigurationData(c));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Templator httpRespond(
|
||||
Map<String, Object> session,
|
||||
Map<String, String> cookie,
|
||||
Map<String, String> request)
|
||||
throws Exception{
|
||||
|
||||
DBConnection db = HalContext.getDB();
|
||||
User localUser = User.getLocalUser(db);
|
||||
|
||||
// Save new input
|
||||
if(request.containsKey("action")){
|
||||
int id = (ObjectUtil.isEmpty(request.get("id")) ? -1 : Integer.parseInt(request.get("id")));
|
||||
Sensor sensor;
|
||||
User user;
|
||||
|
||||
switch(request.get("action")) {
|
||||
// Local Sensors
|
||||
case "create_local_sensor":
|
||||
logger.info("Creating sensor: " + request.get("name"));
|
||||
sensor = new Sensor();
|
||||
sensor.setName(request.get("name"));
|
||||
sensor.setType(request.get("type"));
|
||||
sensor.setSynced(Boolean.parseBoolean(request.get("sync")));
|
||||
sensor.setUser(localUser);
|
||||
sensor.getDeviceConfigurator().setValues(request).applyConfiguration();
|
||||
sensor.save(db);
|
||||
ControllerManager.getInstance().register(sensor);
|
||||
|
||||
HalAlertManager.getInstance().addAlert(new HalAlert(
|
||||
AlertLevel.SUCCESS, "Successfully created new sensor: "+sensor.getName(), AlertTTL.ONE_VIEW));
|
||||
break;
|
||||
|
||||
case "modify_local_sensor":
|
||||
sensor = Sensor.getSensor(db, id);
|
||||
if(sensor != null){
|
||||
logger.info("Modifying sensor: " + sensor.getName());
|
||||
sensor.setName(request.get("name"));
|
||||
sensor.setType(request.get("type"));
|
||||
sensor.setSynced(Boolean.parseBoolean(request.get("sync")));
|
||||
sensor.getDeviceConfigurator().setValues(request).applyConfiguration();
|
||||
sensor.save(db);
|
||||
|
||||
HalAlertManager.getInstance().addAlert(new HalAlert(
|
||||
AlertLevel.SUCCESS, "Successfully saved sensor: "+sensor.getName(), AlertTTL.ONE_VIEW));
|
||||
} else {
|
||||
logger.warning("Unknown sensor id: " + id);
|
||||
HalAlertManager.getInstance().addAlert(new HalAlert(
|
||||
AlertLevel.ERROR, "Unknown sensor id: " + id, AlertTTL.ONE_VIEW));
|
||||
}
|
||||
break;
|
||||
|
||||
case "remove_local_sensor":
|
||||
sensor = Sensor.getSensor(db, id);
|
||||
if(sensor != null) {
|
||||
logger.warning("Removing sensor: " + sensor.getName());
|
||||
ControllerManager.getInstance().deregister(sensor);
|
||||
sensor.delete(db);
|
||||
|
||||
HalAlertManager.getInstance().addAlert(new HalAlert(
|
||||
AlertLevel.SUCCESS, "Successfully removed sensor: "+sensor.getName(), AlertTTL.ONE_VIEW));
|
||||
} else {
|
||||
logger.warning("Unknown sensor id: " + id);
|
||||
HalAlertManager.getInstance().addAlert(new HalAlert(
|
||||
AlertLevel.ERROR, "Unknown sensor id: " + id, AlertTTL.ONE_VIEW));
|
||||
}
|
||||
break;
|
||||
|
||||
case "remove_all_detected_sensors":
|
||||
ControllerManager.getInstance().clearDetectedSensors();
|
||||
break;
|
||||
|
||||
// External Users
|
||||
case "create_external_user":
|
||||
logger.info("Creating external user: " + request.get("hostname"));
|
||||
user = new User();
|
||||
user.setHostname(request.get("hostname"));
|
||||
user.setPort(Integer.parseInt(request.get("port")));
|
||||
user.setExternal(true);
|
||||
user.save(db);
|
||||
|
||||
HalAlertManager.getInstance().addAlert(new HalAlert(
|
||||
AlertLevel.SUCCESS, "Successfully created new external user with host: "+user.getHostname(), AlertTTL.ONE_VIEW));
|
||||
break;
|
||||
|
||||
case "modify_external_user":
|
||||
user = User.getUser(db, id);
|
||||
if(user != null){
|
||||
logger.info("Modifying external user: " + user.getHostname());
|
||||
user.setHostname(request.get("hostname"));
|
||||
user.setPort(Integer.parseInt(request.get("port")));
|
||||
user.save(db);
|
||||
|
||||
HalAlertManager.getInstance().addAlert(new HalAlert(
|
||||
AlertLevel.SUCCESS, "Successfully saved external user with host: "+user.getHostname(), AlertTTL.ONE_VIEW));
|
||||
} else {
|
||||
logger.warning("Unknown user id: " + id);
|
||||
HalAlertManager.getInstance().addAlert(new HalAlert(
|
||||
AlertLevel.ERROR, "Unknown user id: " + id, AlertTTL.ONE_VIEW));
|
||||
}
|
||||
break;
|
||||
case "remove_external_user":
|
||||
user = User.getUser(db, id);
|
||||
if (user != null) {
|
||||
logger.info("Removing external user: " + user.getHostname());
|
||||
user.delete(db);
|
||||
|
||||
HalAlertManager.getInstance().addAlert(new HalAlert(
|
||||
AlertLevel.SUCCESS, "Successfully removed user with host: "+user.getHostname(), AlertTTL.ONE_VIEW));
|
||||
} else {
|
||||
logger.warning("Unknown user id: " + id);
|
||||
HalAlertManager.getInstance().addAlert(new HalAlert(
|
||||
AlertLevel.ERROR, "Unknown user id: "+id, AlertTTL.ONE_VIEW));
|
||||
}
|
||||
break;
|
||||
|
||||
// External Sensors
|
||||
case "modify_external_sensor":
|
||||
sensor = Sensor.getSensor(db, id);
|
||||
if(sensor != null){
|
||||
logger.warning("Modifying external sensor: " + sensor.getName());
|
||||
sensor.setSynced(Boolean.parseBoolean(request.get("sync")));
|
||||
sensor.save(db);
|
||||
|
||||
HalAlertManager.getInstance().addAlert(new HalAlert(
|
||||
AlertLevel.SUCCESS, "Successfully saved external sensor: "+sensor.getName(), AlertTTL.ONE_VIEW));
|
||||
} else {
|
||||
logger.warning("Unknown user id: " + id);
|
||||
HalAlertManager.getInstance().addAlert(new HalAlert(
|
||||
AlertLevel.ERROR, "Unknown sensor id: "+id, AlertTTL.ONE_VIEW));
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Output
|
||||
Templator tmpl = new Templator(FileUtil.find(TEMPLATE));
|
||||
tmpl.set("user", localUser);
|
||||
tmpl.set("localSensors", Sensor.getLocalSensors(db));
|
||||
tmpl.set("localSensorConf", sensorConfigurations);
|
||||
tmpl.set("detectedSensors", ControllerManager.getInstance().getDetectedSensors());
|
||||
tmpl.set("extUsers", User.getExternalUsers(db));
|
||||
tmpl.set("extSensor", Sensor.getExternalSensors(db));
|
||||
tmpl.set("availableSensors", ControllerManager.getInstance().getAvailableSensors());
|
||||
|
||||
return tmpl;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
137
hal-core/src/se/hal/page/SensorJsonPage.java
Normal file
137
hal-core/src/se/hal/page/SensorJsonPage.java
Normal file
|
|
@ -0,0 +1,137 @@
|
|||
package se.hal.page;
|
||||
|
||||
import se.hal.HalContext;
|
||||
import se.hal.daemon.SensorDataAggregatorDaemon;
|
||||
import se.hal.intf.HalJsonPage;
|
||||
import se.hal.struct.Sensor;
|
||||
import se.hal.util.AggregateDataListSqlResult;
|
||||
import se.hal.util.UTCTimeUtility;
|
||||
import zutil.ArrayUtil;
|
||||
import zutil.db.DBConnection;
|
||||
import zutil.log.LogUtil;
|
||||
import zutil.net.http.HttpHeader;
|
||||
import zutil.net.http.HttpPrintStream;
|
||||
import zutil.parser.DataNode;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
/**
|
||||
* Available HTTP Get Request parameters:
|
||||
* <pre>
|
||||
* Sensor filtering parameters:
|
||||
* id: comma separated numeric id for specific sensors
|
||||
* type: sensor data type name
|
||||
*
|
||||
* Data filtering parameters:
|
||||
* aggr: Aggregation periods, needs to be provided to retrieve data. Possible values: minute,hour,day,week
|
||||
* </pre>
|
||||
*/
|
||||
public class SensorJsonPage extends HalJsonPage {
|
||||
private static final Logger logger = LogUtil.getLogger();
|
||||
|
||||
|
||||
public SensorJsonPage() {
|
||||
super("data/sensor");
|
||||
}
|
||||
|
||||
@Override
|
||||
public DataNode jsonRespond(
|
||||
Map<String, Object> session,
|
||||
Map<String, String> cookie,
|
||||
Map<String, String> request) throws Exception{
|
||||
|
||||
DBConnection db = HalContext.getDB();
|
||||
DataNode root = new DataNode(DataNode.DataType.List);
|
||||
|
||||
//// Get sensors
|
||||
String[] req_ids = null;
|
||||
if (request.get("id") != null)
|
||||
req_ids = request.get("id").split(",");
|
||||
String req_type = request.get("type");
|
||||
|
||||
List<Sensor> sensors = new ArrayList<>();
|
||||
for (Sensor sensor : Sensor.getSensors(db)) {
|
||||
if (sensor.getDeviceConfig() == null) // Show all sensors for now
|
||||
continue;
|
||||
|
||||
if (req_ids == null && req_type==null) // no options defined, then add all sensors
|
||||
sensors.add(sensor);
|
||||
else if (req_ids != null && ArrayUtil.contains(req_ids, ""+sensor.getId())) // id filtering
|
||||
sensors.add(sensor);
|
||||
else if (req_type != null && !req_type.isEmpty() &&
|
||||
sensor.getDeviceConfig().getSensorDataClass().getSimpleName().contains(req_type)) // device type filtering
|
||||
sensors.add(sensor);
|
||||
}
|
||||
|
||||
//// Figure out aggregation period
|
||||
SensorDataAggregatorDaemon.AggregationPeriodLength aggrType = null;
|
||||
long aggrLength = -1;
|
||||
if (request.get("aggr") != null) {
|
||||
switch (request.get("aggr")) {
|
||||
case "minute":
|
||||
aggrType = SensorDataAggregatorDaemon.AggregationPeriodLength.FIVE_MINUTES;
|
||||
aggrLength = UTCTimeUtility.DAY_IN_MS;
|
||||
break;
|
||||
case "hour":
|
||||
aggrType = SensorDataAggregatorDaemon.AggregationPeriodLength.HOUR;
|
||||
aggrLength = UTCTimeUtility.WEEK_IN_MS;
|
||||
break;
|
||||
case "day":
|
||||
aggrType = SensorDataAggregatorDaemon.AggregationPeriodLength.DAY;
|
||||
aggrLength = UTCTimeUtility.INFINITY;
|
||||
break;
|
||||
case "week":
|
||||
aggrType = SensorDataAggregatorDaemon.AggregationPeriodLength.WEEK;
|
||||
aggrLength = UTCTimeUtility.INFINITY;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/// Generate DataNode
|
||||
for (Sensor sensor : sensors) {
|
||||
DataNode deviceNode = new DataNode(DataNode.DataType.Map);
|
||||
deviceNode.set("id", sensor.getId());
|
||||
deviceNode.set("name", sensor.getName());
|
||||
deviceNode.set("user", sensor.getUser().getUsername());
|
||||
deviceNode.set("type", sensor.getDeviceConfig().getSensorDataClass().getSimpleName());
|
||||
deviceNode.set("x", sensor.getX());
|
||||
deviceNode.set("y", sensor.getY());
|
||||
|
||||
if (aggrLength > 0) {
|
||||
addAggregateDataToDataNode(deviceNode, aggrLength,
|
||||
AggregateDataListSqlResult.getAggregateDataForPeriod(db, sensor, aggrType, aggrLength));
|
||||
}
|
||||
root.add(deviceNode);
|
||||
}
|
||||
|
||||
return root;
|
||||
}
|
||||
|
||||
private void addAggregateDataToDataNode(DataNode deviceNode, long endTime, List<AggregateDataListSqlResult.AggregateData> dataList) {
|
||||
DataNode timestampNode = new DataNode(DataNode.DataType.List);
|
||||
DataNode dataNode = new DataNode(DataNode.DataType.List);
|
||||
// end timestamp
|
||||
if (endTime != UTCTimeUtility.INFINITY) {
|
||||
timestampNode.add(System.currentTimeMillis() - endTime);
|
||||
dataNode.add((String)null);
|
||||
}
|
||||
// actual data
|
||||
for (AggregateDataListSqlResult.AggregateData data : dataList) {
|
||||
timestampNode.add(data.timestamp);
|
||||
if (data.data == null || Float.isNaN(data.data))
|
||||
dataNode.add((String)null);
|
||||
else
|
||||
dataNode.add(data.data);
|
||||
}
|
||||
// start timestamp
|
||||
timestampNode.add(System.currentTimeMillis());
|
||||
dataNode.add((String)null);
|
||||
|
||||
deviceNode.set("timestamps", timestampNode);
|
||||
deviceNode.set("data", dataNode);
|
||||
}
|
||||
|
||||
}
|
||||
65
hal-core/src/se/hal/page/SensorOverviewWebPage.java
Normal file
65
hal-core/src/se/hal/page/SensorOverviewWebPage.java
Normal file
|
|
@ -0,0 +1,65 @@
|
|||
package se.hal.page;
|
||||
|
||||
import se.hal.HalContext;
|
||||
import se.hal.intf.HalWebPage;
|
||||
import se.hal.struct.Sensor;
|
||||
import se.hal.util.DeviceNameComparator;
|
||||
import se.hal.util.HistoryDataListSqlResult;
|
||||
import se.hal.util.HistoryDataListSqlResult.HistoryData;
|
||||
import zutil.ObjectUtil;
|
||||
import zutil.db.DBConnection;
|
||||
import zutil.io.file.FileUtil;
|
||||
import zutil.parser.Templator;
|
||||
|
||||
import java.sql.PreparedStatement;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
public class SensorOverviewWebPage extends HalWebPage {
|
||||
private static final int HISTORY_LIMIT = 200;
|
||||
private static final String OVERVIEW_TEMPLATE = "resource/web/sensor_overview.tmpl";
|
||||
private static final String DETAIL_TEMPLATE = "resource/web/sensor_detail.tmpl";
|
||||
|
||||
|
||||
public SensorOverviewWebPage(){
|
||||
super("sensor_overview");
|
||||
super.getRootNav().createSubNav("Sensors").createSubNav(this.getId(), "Overview");
|
||||
}
|
||||
|
||||
@Override
|
||||
public Templator httpRespond(
|
||||
Map<String, Object> session,
|
||||
Map<String, String> cookie,
|
||||
Map<String, String> request)
|
||||
throws Exception{
|
||||
|
||||
DBConnection db = HalContext.getDB();
|
||||
int id = (ObjectUtil.isEmpty(request.get("id")) ? -1 : Integer.parseInt(request.get("id")));
|
||||
|
||||
// Save new input
|
||||
if (id >= 0) {
|
||||
Sensor sensor = Sensor.getSensor(db, id);
|
||||
|
||||
// get history data
|
||||
PreparedStatement stmt = db.getPreparedStatement(
|
||||
"SELECT * FROM sensor_data_raw WHERE sensor_id == ? ORDER BY timestamp DESC LIMIT ?");
|
||||
stmt.setLong(1, sensor.getId());
|
||||
stmt.setLong(2, HISTORY_LIMIT);
|
||||
List<HistoryData> history = DBConnection.exec(stmt, new HistoryDataListSqlResult());
|
||||
|
||||
Templator tmpl = new Templator(FileUtil.find(DETAIL_TEMPLATE));
|
||||
tmpl.set("sensor", sensor);
|
||||
tmpl.set("history", history);
|
||||
return tmpl;
|
||||
} else {
|
||||
Sensor[] sensors = Sensor.getLocalSensors(db).toArray(new Sensor[0]);
|
||||
Arrays.sort(sensors, DeviceNameComparator.getInstance());
|
||||
|
||||
Templator tmpl = new Templator(FileUtil.find(OVERVIEW_TEMPLATE));
|
||||
tmpl.set("sensors", sensors);
|
||||
return tmpl;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
144
hal-core/src/se/hal/page/TriggerWebPage.java
Normal file
144
hal-core/src/se/hal/page/TriggerWebPage.java
Normal file
|
|
@ -0,0 +1,144 @@
|
|||
package se.hal.page;
|
||||
|
||||
import se.hal.HalContext;
|
||||
import se.hal.TriggerManager;
|
||||
import se.hal.intf.HalWebPage;
|
||||
import se.hal.struct.Action;
|
||||
import se.hal.struct.ClassConfigurationData;
|
||||
import se.hal.struct.Trigger;
|
||||
import se.hal.struct.TriggerFlow;
|
||||
import zutil.ObjectUtil;
|
||||
import zutil.db.DBConnection;
|
||||
import zutil.io.file.FileUtil;
|
||||
import zutil.log.LogUtil;
|
||||
import zutil.parser.Templator;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Map;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
public class TriggerWebPage extends HalWebPage {
|
||||
private static final Logger logger = LogUtil.getLogger();
|
||||
private static final String TEMPLATE = "resource/web/trigger.tmpl";
|
||||
|
||||
private ArrayList<ClassConfigurationData> triggerConfigurators;
|
||||
private ArrayList<ClassConfigurationData> actionConfigurators;
|
||||
|
||||
|
||||
public TriggerWebPage() {
|
||||
super("trigger");
|
||||
super.getRootNav().createSubNav("Events").createSubNav(this.getId(), "Triggers");
|
||||
|
||||
triggerConfigurators = new ArrayList<>();
|
||||
for(Class c : TriggerManager.getInstance().getAvailableTriggers())
|
||||
triggerConfigurators.add(new ClassConfigurationData(c));
|
||||
actionConfigurators = new ArrayList<>();
|
||||
for(Class c : TriggerManager.getInstance().getAvailableActions())
|
||||
actionConfigurators.add(new ClassConfigurationData(c));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Templator httpRespond (
|
||||
Map<String, Object> session,
|
||||
Map<String, String> cookie,
|
||||
Map<String, String> request)
|
||||
throws Exception {
|
||||
DBConnection db = HalContext.getDB();
|
||||
|
||||
if (request.containsKey("action")) {
|
||||
TriggerFlow flow = (ObjectUtil.isEmpty(request.get("flow-id")) ? null :
|
||||
TriggerFlow.getTriggerFlow(db, Integer.parseInt(request.get("flow-id"))));
|
||||
Trigger trigger = (ObjectUtil.isEmpty(request.get("trigger-id")) ? null :
|
||||
Trigger.getTrigger(db, Integer.parseInt(request.get("trigger-id"))));
|
||||
Action action = (ObjectUtil.isEmpty(request.get("action-id")) ? null :
|
||||
Action.getAction(db, Integer.parseInt(request.get("action-id"))));
|
||||
|
||||
switch(request.get("action")) {
|
||||
// Flows
|
||||
case "create_flow":
|
||||
logger.info("Creating new flow.");
|
||||
flow = new TriggerFlow();
|
||||
flow.save(db);
|
||||
break;
|
||||
|
||||
case "modify_flow":
|
||||
logger.info("Modifying flow: " + flow.getName());
|
||||
flow.setEnabled("on".equals(request.get("enabled")));
|
||||
flow.setName(request.get("name"));
|
||||
flow.save(db);
|
||||
break;
|
||||
|
||||
case "remove_flow":
|
||||
logger.info("Removing flow: " + flow.getName());
|
||||
flow.delete(db);
|
||||
break;
|
||||
|
||||
// Triggers
|
||||
case "create_trigger":
|
||||
if (flow == null){
|
||||
logger.warning("Invalid flow id: " + request.get("flow-id"));
|
||||
HalAlertManager.getInstance().addAlert(new HalAlertManager.HalAlert(
|
||||
HalAlertManager.AlertLevel.ERROR, "Invalid flow id: " + request.get("flow-id"), HalAlertManager.AlertTTL.ONE_VIEW));
|
||||
break;
|
||||
}
|
||||
|
||||
logger.info("Creating trigger associated to flow: " + flow.getName());
|
||||
trigger = new Trigger();
|
||||
flow.addTrigger(trigger);
|
||||
flow.save(db);
|
||||
/* FALLTHROUGH */
|
||||
case "modify_trigger":
|
||||
logger.info("Modifying trigger: " + trigger.getId());
|
||||
trigger.setObjectClass(request.get("type"));
|
||||
trigger.getObjectConfigurator().setValues(request).applyConfiguration();
|
||||
trigger.save(db); // will save all sub beans also
|
||||
break;
|
||||
|
||||
case "remove_trigger":
|
||||
if (flow == null)
|
||||
flow = TriggerFlow.getTriggerFlow(db, trigger);
|
||||
logger.info("Removing trigger: " + trigger.getId());
|
||||
flow.removeTrigger(trigger);
|
||||
trigger.delete(db);
|
||||
break;
|
||||
|
||||
// Triggers
|
||||
case "create_action":
|
||||
if (flow == null){
|
||||
HalAlertManager.getInstance().addAlert(new HalAlertManager.HalAlert(
|
||||
HalAlertManager.AlertLevel.ERROR, "Invalid flow id", HalAlertManager.AlertTTL.ONE_VIEW));
|
||||
break;
|
||||
}
|
||||
|
||||
logger.info("Creating action associated with flow: " + flow.getName());
|
||||
action = new Action();
|
||||
flow.addAction(action);
|
||||
flow.save(db);
|
||||
/* FALLTHROUGH */
|
||||
case "modify_action":
|
||||
logger.info("Modifying action: " + action.getId());
|
||||
action.setObjectClass(request.get("type"));
|
||||
action.getObjectConfigurator().setValues(request).applyConfiguration();
|
||||
action.save(db); // will save all sub beans also
|
||||
break;
|
||||
|
||||
case "remove_action":
|
||||
if (flow == null)
|
||||
flow = TriggerFlow.getTriggerFlow(db, action);
|
||||
logger.info("Removing action: " + action.getId());
|
||||
flow.removeAction(action);
|
||||
action.delete(db);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Templator tmpl = new Templator(FileUtil.find(TEMPLATE));
|
||||
tmpl.set("triggerConf", triggerConfigurators);
|
||||
tmpl.set("actionConf", actionConfigurators);
|
||||
tmpl.set("availableTriggers", TriggerManager.getInstance().getAvailableTriggers());
|
||||
tmpl.set("availableActions", TriggerManager.getInstance().getAvailableActions());
|
||||
tmpl.set("flows", TriggerFlow.getTriggerFlows(db));
|
||||
return tmpl;
|
||||
}
|
||||
}
|
||||
67
hal-core/src/se/hal/page/UserConfigWebPage.java
Normal file
67
hal-core/src/se/hal/page/UserConfigWebPage.java
Normal file
|
|
@ -0,0 +1,67 @@
|
|||
package se.hal.page;
|
||||
|
||||
import se.hal.HalContext;
|
||||
import se.hal.intf.HalWebPage;
|
||||
import se.hal.page.HalAlertManager.AlertLevel;
|
||||
import se.hal.page.HalAlertManager.AlertTTL;
|
||||
import se.hal.page.HalAlertManager.HalAlert;
|
||||
import se.hal.struct.User;
|
||||
import zutil.db.DBConnection;
|
||||
import zutil.io.file.FileUtil;
|
||||
import zutil.log.LogUtil;
|
||||
import zutil.parser.Templator;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
public class UserConfigWebPage extends HalWebPage {
|
||||
private static final Logger logger = LogUtil.getLogger();
|
||||
private static final String TEMPLATE = "resource/web/user_config.tmpl";
|
||||
|
||||
|
||||
public UserConfigWebPage() {
|
||||
super("user_profile");
|
||||
super.getUserNav().createSubNav(this.getId(), "Profile");
|
||||
}
|
||||
|
||||
@Override
|
||||
public Templator httpRespond(
|
||||
Map<String, Object> session,
|
||||
Map<String, String> cookie,
|
||||
Map<String, String> request)
|
||||
throws Exception {
|
||||
|
||||
DBConnection db = HalContext.getDB();
|
||||
User localUser = User.getLocalUser(db);
|
||||
|
||||
// Save new input
|
||||
if (request.containsKey("action")) {
|
||||
User user;
|
||||
switch(request.get("action")) {
|
||||
// Local User
|
||||
case "modify_local_user":
|
||||
if (localUser == null) {
|
||||
localUser = new User();
|
||||
localUser.setExternal(false);
|
||||
}
|
||||
logger.info("Modifying user: " + localUser.getUsername());
|
||||
localUser.setUsername(request.get("username"));
|
||||
localUser.setEmail(request.get("email"));
|
||||
localUser.setAddress(request.get("address"));
|
||||
localUser.save(db);
|
||||
|
||||
HalAlertManager.getInstance().addAlert(new HalAlert(
|
||||
AlertLevel.SUCCESS, "Successfully saved profile changes", AlertTTL.ONE_VIEW));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Output
|
||||
Templator tmpl = new Templator(FileUtil.find(TEMPLATE));
|
||||
tmpl.set("user", localUser);
|
||||
|
||||
return tmpl;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
29
hal-core/src/se/hal/plugin.json
Normal file
29
hal-core/src/se/hal/plugin.json
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
{
|
||||
"version": 1.0,
|
||||
"name": "Hal Core",
|
||||
"interfaces": [
|
||||
{"se.hal.intf.HalDaemon": "se.hal.daemon.SensorDataAggregatorDaemon"},
|
||||
{"se.hal.intf.HalDaemon": "se.hal.daemon.SensorDataCleanupDaemon"},
|
||||
|
||||
|
||||
{"se.hal.intf.HalJsonPage": "se.hal.page.MapJsonPage"},
|
||||
{"se.hal.intf.HalJsonPage": "se.hal.page.SensorJsonPage"},
|
||||
|
||||
{"se.hal.intf.HalWebPage": "se.hal.page.MapWebPage"},
|
||||
{"se.hal.intf.HalWebPage": "se.hal.page.SensorOverviewWebPage"},
|
||||
{"se.hal.intf.HalWebPage": "se.hal.page.SensorConfigWebPage"},
|
||||
{"se.hal.intf.HalWebPage": "se.hal.page.EventOverviewWebPage"},
|
||||
{"se.hal.intf.HalWebPage": "se.hal.page.EventConfigWebPage"},
|
||||
{"se.hal.intf.HalWebPage": "se.hal.page.TriggerWebPage"},
|
||||
{"se.hal.intf.HalWebPage": "se.hal.page.UserConfigWebPage"},
|
||||
{"se.hal.intf.HalWebPage": "se.hal.page.PropertyConfigWebPage"},
|
||||
{"se.hal.intf.HalWebPage": "se.hal.page.PluginConfigWebPage"},
|
||||
|
||||
|
||||
{"se.hal.intf.HalTrigger": "se.hal.trigger.DateTimeTrigger"},
|
||||
{"se.hal.intf.HalTrigger": "se.hal.trigger.EventTrigger"},
|
||||
{"se.hal.intf.HalTrigger": "se.hal.trigger.SensorTrigger"},
|
||||
{"se.hal.intf.HalTrigger": "se.hal.trigger.TimerTrigger"},
|
||||
{"se.hal.intf.HalAction": "se.hal.action.SendEventAction"}
|
||||
]
|
||||
}
|
||||
203
hal-core/src/se/hal/struct/AbstractDevice.java
Normal file
203
hal-core/src/se/hal/struct/AbstractDevice.java
Normal file
|
|
@ -0,0 +1,203 @@
|
|||
package se.hal.struct;
|
||||
|
||||
import se.hal.ControllerManager;
|
||||
import se.hal.HalContext;
|
||||
import se.hal.intf.HalDeviceData;
|
||||
import se.hal.intf.HalDeviceReportListener;
|
||||
import zutil.db.DBConnection;
|
||||
import zutil.db.bean.DBBean;
|
||||
import zutil.log.LogUtil;
|
||||
import zutil.parser.json.JSONParser;
|
||||
import zutil.parser.json.JSONWriter;
|
||||
import zutil.ui.Configurator;
|
||||
|
||||
import java.sql.SQLException;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
/**
|
||||
* Contains logic and data common to devices (Events and Sensors)
|
||||
*
|
||||
* @param <T> is the device type
|
||||
* @param <C> is the device configuration class
|
||||
* @param <D> is the device data class
|
||||
*/
|
||||
public abstract class AbstractDevice<T extends AbstractDevice, C,D extends HalDeviceData> extends DBBean {
|
||||
private static final Logger logger = LogUtil.getLogger();
|
||||
|
||||
// Sensor specific data
|
||||
private String name;
|
||||
private String type;
|
||||
private String config; // only used to store the deviceConfig configuration in DB
|
||||
|
||||
/** Sensor specific configuration **/
|
||||
private transient C deviceConfig;
|
||||
/** latest device data received **/
|
||||
private transient D deviceData;
|
||||
|
||||
// User configuration
|
||||
@DBColumn("user_id")
|
||||
private User user;
|
||||
|
||||
// UI variables
|
||||
@DBColumn("map_x")
|
||||
private double x;
|
||||
@DBColumn("map_y")
|
||||
private double y;
|
||||
|
||||
protected transient List<HalDeviceReportListener<T>> listeners = new LinkedList<>();
|
||||
|
||||
|
||||
/**************** DEVICE CONFIG ******************/
|
||||
|
||||
public Configurator<C> getDeviceConfigurator() {
|
||||
C obj = getDeviceConfig();
|
||||
if (obj != null) {
|
||||
Configurator<C> configurator = new Configurator<>(obj);
|
||||
configurator.setPreConfigurationListener(ControllerManager.getInstance());
|
||||
configurator.setPostConfigurationListener(ControllerManager.getInstance());
|
||||
return configurator;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
public C getDeviceConfig() {
|
||||
if (deviceConfig == null || !deviceConfig.getClass().getName().equals(type)) {
|
||||
try {
|
||||
Class c = Class.forName(type);
|
||||
deviceConfig = (C) c.newInstance();
|
||||
|
||||
applyConfig();
|
||||
deviceData = getLatestDeviceData(HalContext.getDB());
|
||||
} catch (Exception e) {
|
||||
logger.log(Level.SEVERE, "Unable instantiate DeviceConfig: "+type, e);
|
||||
}
|
||||
}
|
||||
return deviceConfig;
|
||||
}
|
||||
|
||||
/**
|
||||
* Will replace the current DeviceData.
|
||||
* And the current config will be applied on the new DeviceData.
|
||||
* DeviceData will be reset if the input is set as null.
|
||||
*/
|
||||
public void setDeviceConfig(C data) {
|
||||
if(data != null) {
|
||||
type = data.getClass().getName();
|
||||
deviceConfig = data;
|
||||
deviceData = getLatestDeviceData(HalContext.getDB());
|
||||
} else {
|
||||
deviceConfig = null;
|
||||
deviceData = null;
|
||||
type = null;
|
||||
config = null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void save(DBConnection db) throws SQLException {
|
||||
if (deviceConfig != null)
|
||||
updateConfigString();
|
||||
else
|
||||
this.config = null;
|
||||
super.save(db);
|
||||
}
|
||||
|
||||
/**
|
||||
* Will update the config String that will be stored in DB.
|
||||
*/
|
||||
private void updateConfigString() {
|
||||
Configurator<C> configurator = getDeviceConfigurator();
|
||||
this.config = JSONWriter.toString(configurator.getValuesAsNode());
|
||||
}
|
||||
/**
|
||||
* This method will configure the current DeviceData with the
|
||||
* configuration from the config String.
|
||||
*/
|
||||
private void applyConfig(){
|
||||
if (config != null && !config.isEmpty()) {
|
||||
Configurator<C> configurator = getDeviceConfigurator();
|
||||
configurator.setValues(JSONParser.read(config));
|
||||
configurator.applyConfiguration();
|
||||
}
|
||||
}
|
||||
|
||||
public abstract Class<?> getController();
|
||||
|
||||
/**************** DEVICE DATA ******************/
|
||||
|
||||
/**
|
||||
* @return the latest known data from the device
|
||||
*/
|
||||
public D getDeviceData(){
|
||||
return deviceData;
|
||||
}
|
||||
|
||||
public void setDeviceData(D latest){
|
||||
this.deviceData = latest;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads latest device data from DB
|
||||
*/
|
||||
protected abstract D getLatestDeviceData(DBConnection db);
|
||||
|
||||
/**************** OTHER ******************/
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return a String containing the class name of the DeviceData
|
||||
*/
|
||||
public String getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
/**
|
||||
* Will set the DeviceData class type. This method will
|
||||
* reset set the current DeviceData if the input type is
|
||||
* null or a different type from the current DeviceData class.
|
||||
*/
|
||||
public void setType(String type) {
|
||||
if (this.type == null || !this.type.equals(type)) {
|
||||
setDeviceConfig(null); // reset
|
||||
this.type = type;
|
||||
}
|
||||
}
|
||||
|
||||
public User getUser() {
|
||||
return user;
|
||||
}
|
||||
public void setUser(User user) {
|
||||
this.user = user;
|
||||
}
|
||||
|
||||
public double getX() {
|
||||
return x;
|
||||
}
|
||||
public void setX(double x) {
|
||||
this.x = x;
|
||||
}
|
||||
public double getY() {
|
||||
return y;
|
||||
}
|
||||
public void setY(double y) {
|
||||
this.y = y;
|
||||
}
|
||||
|
||||
public void addReportListener(HalDeviceReportListener<T> listener){
|
||||
listeners.add(listener);
|
||||
}
|
||||
public void removeReportListener(HalDeviceReportListener<T> listener){
|
||||
listeners.remove(listener);
|
||||
}
|
||||
public List<HalDeviceReportListener<T>> getReportListeners(){
|
||||
return listeners;
|
||||
}
|
||||
}
|
||||
42
hal-core/src/se/hal/struct/Action.java
Normal file
42
hal-core/src/se/hal/struct/Action.java
Normal file
|
|
@ -0,0 +1,42 @@
|
|||
package se.hal.struct;
|
||||
|
||||
import se.hal.intf.HalAction;
|
||||
import se.hal.intf.HalTrigger;
|
||||
import zutil.db.DBConnection;
|
||||
import zutil.db.bean.DBBean;
|
||||
import zutil.db.bean.DBBeanObjectDSO;
|
||||
import zutil.db.bean.DBBeanSQLResultHandler;
|
||||
|
||||
import java.sql.PreparedStatement;
|
||||
import java.sql.SQLException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Defines a action that will be executed
|
||||
*/
|
||||
@DBBean.DBTable(value = "action", superBean = true)
|
||||
public class Action extends DBBeanObjectDSO<HalAction>{
|
||||
|
||||
|
||||
public static Action getAction(DBConnection db, long id) throws SQLException {
|
||||
return DBBean.load(db, Action.class, id);
|
||||
}
|
||||
|
||||
|
||||
public Action() { }
|
||||
public Action(HalAction action) {
|
||||
this.setObject(action);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Executes this specific action
|
||||
*/
|
||||
public void execute(){
|
||||
if (getObject() != null)
|
||||
getObject().execute();
|
||||
}
|
||||
|
||||
}
|
||||
18
hal-core/src/se/hal/struct/ClassConfigurationData.java
Normal file
18
hal-core/src/se/hal/struct/ClassConfigurationData.java
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
package se.hal.struct;
|
||||
|
||||
import zutil.ui.Configurator;
|
||||
import zutil.ui.Configurator.ConfigurationParam;
|
||||
|
||||
/**
|
||||
* A Data class used by the dynamic class configuration pages
|
||||
*/
|
||||
public class ClassConfigurationData {
|
||||
public Class clazz;
|
||||
public ConfigurationParam[] params;
|
||||
|
||||
|
||||
public ClassConfigurationData(Class clazz) {
|
||||
this.clazz = clazz;
|
||||
this.params = Configurator.getConfiguration(clazz);
|
||||
}
|
||||
}
|
||||
64
hal-core/src/se/hal/struct/Event.java
Normal file
64
hal-core/src/se/hal/struct/Event.java
Normal file
|
|
@ -0,0 +1,64 @@
|
|||
package se.hal.struct;
|
||||
|
||||
import se.hal.intf.HalDeviceData;
|
||||
import se.hal.intf.HalEventController;
|
||||
import se.hal.intf.HalEventConfig;
|
||||
import se.hal.intf.HalEventData;
|
||||
import se.hal.util.DeviceDataSqlResult;
|
||||
import zutil.db.DBConnection;
|
||||
import zutil.db.SQLResultHandler;
|
||||
import zutil.db.bean.DBBean;
|
||||
import zutil.db.bean.DBBeanSQLResultHandler;
|
||||
import zutil.db.handler.SimpleSQLResult;
|
||||
import zutil.log.LogUtil;
|
||||
|
||||
import java.sql.PreparedStatement;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.sql.Statement;
|
||||
import java.util.List;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
@DBBean.DBTable(value="event", superBean=true)
|
||||
public class Event extends AbstractDevice<Event, HalEventConfig,HalEventData>{
|
||||
private static final Logger logger = LogUtil.getLogger();
|
||||
|
||||
|
||||
public static Event getEvent(DBConnection db, long id) throws SQLException{
|
||||
return DBBean.load(db, Event.class, id);
|
||||
}
|
||||
|
||||
public static List<Event> getLocalEvents(DBConnection db) throws SQLException {
|
||||
PreparedStatement stmt = db.getPreparedStatement(
|
||||
"SELECT event.* FROM event,user WHERE user.external == 0 AND user.id == event.user_id");
|
||||
return DBConnection.exec(stmt, DBBeanSQLResultHandler.createList(Event.class, db));
|
||||
}
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public Class<? extends HalEventController> getController(){
|
||||
return getDeviceConfig().getEventControllerClass();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected HalEventData getLatestDeviceData(DBConnection db) {
|
||||
try {
|
||||
Class deviceDataClass = getDeviceConfig().getEventDataClass();
|
||||
if (deviceDataClass == null)
|
||||
throw new ClassNotFoundException("Unknown event data class for: " + getDeviceConfig().getClass());
|
||||
|
||||
if (getId() != null) {
|
||||
PreparedStatement stmt = db.getPreparedStatement(
|
||||
"SELECT * FROM event_data_raw WHERE event_id == ? ORDER BY timestamp DESC LIMIT 1");
|
||||
stmt.setLong(1, getId());
|
||||
return (HalEventData)
|
||||
DBConnection.exec(stmt, new DeviceDataSqlResult(deviceDataClass));
|
||||
}
|
||||
} catch (Exception e){
|
||||
logger.log(Level.WARNING, null, e);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
68
hal-core/src/se/hal/struct/PluginConfig.java
Normal file
68
hal-core/src/se/hal/struct/PluginConfig.java
Normal file
|
|
@ -0,0 +1,68 @@
|
|||
/*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2020 Ziver Koc
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
package se.hal.struct;
|
||||
|
||||
import zutil.db.DBConnection;
|
||||
import zutil.db.bean.DBBean;
|
||||
import zutil.db.bean.DBBeanSQLResultHandler;
|
||||
|
||||
import java.sql.PreparedStatement;
|
||||
import java.sql.SQLException;
|
||||
|
||||
|
||||
@DBBean.DBTable(value="plugin")
|
||||
public class PluginConfig extends DBBean {
|
||||
private String name;
|
||||
private boolean enabled;
|
||||
|
||||
|
||||
/**
|
||||
* @return a PluginConfig bean for the specific plugin name.
|
||||
*/
|
||||
public static PluginConfig getPluginConfig(DBConnection db, String name) throws SQLException {
|
||||
PreparedStatement stmt = db.getPreparedStatement( "SELECT plugin.* FROM plugin WHERE name == ?" );
|
||||
stmt.setString(1, name);
|
||||
return DBConnection.exec(stmt, DBBeanSQLResultHandler.create(PluginConfig.class, db) );
|
||||
}
|
||||
|
||||
|
||||
public PluginConfig() {}
|
||||
public PluginConfig(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public boolean isEnabled() {
|
||||
return enabled;
|
||||
}
|
||||
|
||||
public void setEnabled(boolean enabled) {
|
||||
this.enabled = enabled;
|
||||
}
|
||||
}
|
||||
137
hal-core/src/se/hal/struct/Sensor.java
Normal file
137
hal-core/src/se/hal/struct/Sensor.java
Normal file
|
|
@ -0,0 +1,137 @@
|
|||
package se.hal.struct;
|
||||
|
||||
import se.hal.HalContext;
|
||||
import se.hal.intf.HalDeviceReportListener;
|
||||
import se.hal.intf.HalSensorController;
|
||||
import se.hal.intf.HalSensorConfig;
|
||||
import se.hal.intf.HalSensorData;
|
||||
import se.hal.util.DeviceDataSqlResult;
|
||||
import zutil.db.DBConnection;
|
||||
import zutil.db.bean.DBBean;
|
||||
import zutil.db.bean.DBBeanSQLResultHandler;
|
||||
import zutil.db.handler.SimpleSQLResult;
|
||||
import zutil.log.LogUtil;
|
||||
|
||||
import java.sql.PreparedStatement;
|
||||
import java.sql.SQLException;
|
||||
import java.util.List;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
|
||||
@DBBean.DBTable(value="sensor", superBean=true)
|
||||
public class Sensor extends AbstractDevice<Sensor, HalSensorConfig,HalSensorData>{
|
||||
private static final Logger logger = LogUtil.getLogger();
|
||||
|
||||
private long external_id = -1;
|
||||
/** local sensor= if sensor should be public. external sensor= if sensor should be requested from host **/
|
||||
private boolean sync = false;
|
||||
private long aggr_version;
|
||||
|
||||
|
||||
public static List<Sensor> getExternalSensors(DBConnection db) throws SQLException{
|
||||
PreparedStatement stmt = db.getPreparedStatement( "SELECT sensor.* FROM sensor,user WHERE user.external == 1 AND user.id == sensor.user_id" );
|
||||
return DBConnection.exec(stmt, DBBeanSQLResultHandler.createList(Sensor.class, db) );
|
||||
}
|
||||
public static Sensor getExternalSensor(DBConnection db, User user, long external_id) throws SQLException{
|
||||
PreparedStatement stmt = db.getPreparedStatement( "SELECT sensor.* FROM sensor WHERE ? == sensor.user_id AND sensor.external_id == ?" );
|
||||
stmt.setLong(1, user.getId());
|
||||
stmt.setLong(2, external_id);
|
||||
return DBConnection.exec(stmt, DBBeanSQLResultHandler.create(Sensor.class, db) );
|
||||
}
|
||||
|
||||
public static List<Sensor> getLocalSensors(DBConnection db) throws SQLException{
|
||||
PreparedStatement stmt = db.getPreparedStatement( "SELECT sensor.* FROM sensor,user WHERE user.external == 0 AND user.id == sensor.user_id" );
|
||||
return DBConnection.exec(stmt, DBBeanSQLResultHandler.createList(Sensor.class, db) );
|
||||
}
|
||||
|
||||
public static List<Sensor> getSensors(DBConnection db, User user) throws SQLException{
|
||||
PreparedStatement stmt = db.getPreparedStatement( "SELECT * FROM sensor WHERE user_id == ?" );
|
||||
stmt.setLong(1, user.getId());
|
||||
return DBConnection.exec(stmt, DBBeanSQLResultHandler.createList(Sensor.class, db) );
|
||||
}
|
||||
|
||||
public static List<Sensor> getSensors(DBConnection db) throws SQLException{
|
||||
PreparedStatement stmt = db.getPreparedStatement( "SELECT * FROM sensor" );
|
||||
return DBConnection.exec(stmt, DBBeanSQLResultHandler.createList(Sensor.class, db) );
|
||||
}
|
||||
|
||||
public static Sensor getSensor(DBConnection db, long id) throws SQLException{
|
||||
return DBBean.load(db, Sensor.class, id);
|
||||
}
|
||||
|
||||
public static long getHighestSequenceId(long sensorId) throws SQLException{
|
||||
PreparedStatement stmt = HalContext.getDB().getPreparedStatement("SELECT MAX(sequence_id) FROM sensor_data_aggr WHERE sensor_id == ?");
|
||||
stmt.setLong(1, sensorId);
|
||||
Integer id = DBConnection.exec(stmt, new SimpleSQLResult<Integer>());
|
||||
return (id != null ? id : 0);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Will delete this Sensor and its aggregate data
|
||||
* (raw data will never be deleted as a safety precaution!)
|
||||
*/
|
||||
@Override
|
||||
public void delete(DBConnection db) throws SQLException {
|
||||
clearAggregatedData(db);
|
||||
super.delete(db);
|
||||
}
|
||||
|
||||
/**
|
||||
* Will clear all aggregated data for this Sensor and increment the AggregationVersion
|
||||
*/
|
||||
public void clearAggregatedData(DBConnection db) throws SQLException{
|
||||
logger.fine("Clearing all aggregate data for sensor id: "+this.getId());
|
||||
PreparedStatement stmt = db.getPreparedStatement( "DELETE FROM sensor_data_aggr WHERE sensor_id == ?" );
|
||||
stmt.setLong(1, getId());
|
||||
DBConnection.exec(stmt);
|
||||
aggr_version++;
|
||||
}
|
||||
|
||||
|
||||
public long getExternalId() {
|
||||
return external_id;
|
||||
}
|
||||
public void setExternalId(long external_id) {
|
||||
this.external_id = external_id;
|
||||
}
|
||||
public boolean isSynced() {
|
||||
return sync;
|
||||
}
|
||||
public void setSynced(boolean synced) {
|
||||
this.sync = synced;
|
||||
}
|
||||
public long getAggregationVersion(){
|
||||
return this.aggr_version;
|
||||
}
|
||||
public void setAggregationVersion(long aggr_version){
|
||||
this.aggr_version = aggr_version;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public Class<? extends HalSensorController> getController(){
|
||||
return getDeviceConfig().getSensorControllerClass();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected HalSensorData getLatestDeviceData(DBConnection db) {
|
||||
try {
|
||||
Class deviceDataClass = getDeviceConfig().getSensorDataClass();
|
||||
if (deviceDataClass == null)
|
||||
throw new ClassNotFoundException("Unknown sensor data class for: " + getDeviceConfig().getClass());
|
||||
|
||||
if (getId() != null) {
|
||||
PreparedStatement stmt = db.getPreparedStatement(
|
||||
"SELECT * FROM sensor_data_raw WHERE sensor_id == ? ORDER BY timestamp DESC LIMIT 1");
|
||||
stmt.setLong(1, getId());
|
||||
return (HalSensorData)
|
||||
DBConnection.exec(stmt, new DeviceDataSqlResult(deviceDataClass));
|
||||
}
|
||||
} catch (Exception e){
|
||||
logger.log(Level.WARNING, null, e);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
54
hal-core/src/se/hal/struct/Trigger.java
Normal file
54
hal-core/src/se/hal/struct/Trigger.java
Normal file
|
|
@ -0,0 +1,54 @@
|
|||
package se.hal.struct;
|
||||
|
||||
import se.hal.intf.HalTrigger;
|
||||
import zutil.db.DBConnection;
|
||||
import zutil.db.bean.DBBean;
|
||||
import zutil.db.bean.DBBeanObjectDSO;
|
||||
import zutil.db.bean.DBBeanSQLResultHandler;
|
||||
|
||||
import java.sql.PreparedStatement;
|
||||
import java.sql.SQLException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* A class that declares a trigger/condition that
|
||||
* needs to be validated before an action can be run
|
||||
*/
|
||||
@DBBean.DBTable(value = "trigger", superBean = true)
|
||||
public class Trigger extends DBBeanObjectDSO<HalTrigger>{
|
||||
|
||||
|
||||
|
||||
public static Trigger getTrigger(DBConnection db, long id) throws SQLException {
|
||||
return DBBean.load(db, Trigger.class, id);
|
||||
}
|
||||
|
||||
|
||||
|
||||
public Trigger() { }
|
||||
public Trigger(HalTrigger trigger) {
|
||||
this.setObject(trigger);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Evaluates if this trigger has passed. If the trigger is
|
||||
* true then this method will return true until the {@link #reset()}
|
||||
* method is called.
|
||||
*/
|
||||
public boolean evaluate(){
|
||||
if (getObject() != null)
|
||||
return getObject().evaluate();
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset the evaluation to false.
|
||||
*/
|
||||
public void reset(){
|
||||
if (getObject() != null)
|
||||
getObject().reset();
|
||||
}
|
||||
|
||||
}
|
||||
130
hal-core/src/se/hal/struct/TriggerFlow.java
Normal file
130
hal-core/src/se/hal/struct/TriggerFlow.java
Normal file
|
|
@ -0,0 +1,130 @@
|
|||
package se.hal.struct;
|
||||
|
||||
import zutil.db.DBConnection;
|
||||
import zutil.db.bean.DBBean;
|
||||
import zutil.db.bean.DBBeanSQLResultHandler;
|
||||
import zutil.db.handler.SimpleSQLResult;
|
||||
import zutil.log.LogUtil;
|
||||
|
||||
import java.sql.PreparedStatement;
|
||||
import java.sql.SQLException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
/**
|
||||
* A class that encapsulates triggerList and their actionList.
|
||||
* TODO: Bad class name, should be renamed when we come up with a better one
|
||||
*/
|
||||
@DBBean.DBTable("trigger_flow")
|
||||
public class TriggerFlow extends DBBean {
|
||||
private static final Logger logger = LogUtil.getLogger();
|
||||
|
||||
private boolean enabled = true;
|
||||
private String name = "";
|
||||
|
||||
@DBLinkTable(beanClass=Trigger.class, table="trigger", idColumn = "flow_id")
|
||||
private List<Trigger> triggerList = new ArrayList<>();
|
||||
@DBLinkTable(beanClass=Action.class, table="action", idColumn = "flow_id")
|
||||
private List<Action> actionList = new ArrayList<>();
|
||||
|
||||
|
||||
public static List<TriggerFlow> getTriggerFlows(DBConnection db) throws SQLException {
|
||||
PreparedStatement stmt = db.getPreparedStatement("SELECT * FROM trigger_flow");
|
||||
return DBConnection.exec(stmt, DBBeanSQLResultHandler.createList(TriggerFlow.class, db));
|
||||
}
|
||||
public static TriggerFlow getTriggerFlow(DBConnection db, int id) throws SQLException {
|
||||
return DBBean.load(db, TriggerFlow.class, id);
|
||||
}
|
||||
/**
|
||||
* Looks up the parent TriggerFlow for the specified Trigger
|
||||
*/
|
||||
public static TriggerFlow getTriggerFlow(DBConnection db, Trigger trigger) throws SQLException {
|
||||
return getParentFlow(db, "trigger", trigger);
|
||||
}
|
||||
/**
|
||||
* Looks up the parent TriggerFlow for the specified Action
|
||||
*/
|
||||
public static TriggerFlow getTriggerFlow(DBConnection db, Action action) throws SQLException {
|
||||
return getParentFlow(db, "action", action);
|
||||
}
|
||||
private static TriggerFlow getParentFlow(DBConnection db, String table, DBBean subObj) throws SQLException {
|
||||
if (subObj.getId() == null)
|
||||
return null;
|
||||
Integer flowId = db.exec("SELECT flow_id FROM "+table+" WHERE id=="+subObj.getId(),
|
||||
new SimpleSQLResult<Integer>());
|
||||
if (flowId == null)
|
||||
return null;
|
||||
return TriggerFlow.getTriggerFlow(db, flowId);
|
||||
}
|
||||
|
||||
|
||||
public boolean isEnabled() {
|
||||
return enabled;
|
||||
}
|
||||
public void setEnabled(boolean enabled) {
|
||||
this.enabled = enabled;
|
||||
}
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
|
||||
public void addTrigger(Trigger trigger) {
|
||||
triggerList.add(trigger);
|
||||
}
|
||||
public List<Trigger> getTriggers() {
|
||||
return triggerList;
|
||||
}
|
||||
public void removeTrigger(Trigger trigger) {
|
||||
triggerList.remove(trigger);
|
||||
}
|
||||
|
||||
public void addAction(Action action) {
|
||||
actionList.add(action);
|
||||
}
|
||||
public List<Action> getActions() {
|
||||
return actionList;
|
||||
}
|
||||
public void removeAction(Action action) {
|
||||
actionList.remove(action);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return true if any one of the triggerList evaluate to true,
|
||||
* false if there are no triggerList added.
|
||||
* Note: this method will not execute any actionList
|
||||
*/
|
||||
public boolean evaluate(){
|
||||
if (triggerList.isEmpty() || !enabled)
|
||||
return false;
|
||||
for(Trigger trigger : triggerList){
|
||||
if (!trigger.evaluate())
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes the associated actionList in this flow
|
||||
*/
|
||||
public void execute(){
|
||||
if (!enabled)
|
||||
return;
|
||||
for(Action action : actionList){
|
||||
action.execute();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Resets all trigger evaluations
|
||||
*/
|
||||
public void reset() {
|
||||
for(Trigger trigger : triggerList){
|
||||
trigger.reset();
|
||||
}
|
||||
}
|
||||
}
|
||||
101
hal-core/src/se/hal/struct/User.java
Normal file
101
hal-core/src/se/hal/struct/User.java
Normal file
|
|
@ -0,0 +1,101 @@
|
|||
package se.hal.struct;
|
||||
|
||||
import zutil.api.Gravatar;
|
||||
import zutil.db.DBConnection;
|
||||
import zutil.db.bean.DBBean;
|
||||
import zutil.db.bean.DBBeanSQLResultHandler;
|
||||
|
||||
import java.sql.PreparedStatement;
|
||||
import java.sql.SQLException;
|
||||
import java.util.List;
|
||||
|
||||
@DBBean.DBTable("user")
|
||||
public class User extends DBBean{
|
||||
|
||||
private String username;
|
||||
private String email;
|
||||
private String address;
|
||||
private int external;
|
||||
|
||||
private String hostname;
|
||||
private int port;
|
||||
|
||||
|
||||
|
||||
public static List<User> getExternalUsers(DBConnection db) throws SQLException{
|
||||
PreparedStatement stmt = db.getPreparedStatement( "SELECT * FROM user WHERE user.external == 1" );
|
||||
return DBConnection.exec(stmt, DBBeanSQLResultHandler.createList(User.class, db) );
|
||||
}
|
||||
|
||||
public static User getLocalUser(DBConnection db) throws SQLException{
|
||||
PreparedStatement stmt = db.getPreparedStatement( "SELECT * FROM user WHERE user.external == 0" );
|
||||
return DBConnection.exec(stmt, DBBeanSQLResultHandler.create(User.class, db) );
|
||||
}
|
||||
|
||||
public static List<User> getUsers(DBConnection db) throws SQLException{
|
||||
PreparedStatement stmt = db.getPreparedStatement( "SELECT * FROM user" );
|
||||
return DBConnection.exec(stmt, DBBeanSQLResultHandler.createList(User.class, db) );
|
||||
}
|
||||
|
||||
public static User getUser(DBConnection db, int id) throws SQLException {
|
||||
return DBBean.load(db, User.class, id);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Will delete this user and all its Sensors
|
||||
*/
|
||||
@Override
|
||||
public void delete(DBConnection db) throws SQLException {
|
||||
List<Sensor> sensorList = Sensor.getSensors(db, this);
|
||||
for(Sensor sensor : sensorList){
|
||||
sensor.delete(db);
|
||||
}
|
||||
super.delete(db);
|
||||
}
|
||||
|
||||
|
||||
public String getUsername() {
|
||||
return username;
|
||||
}
|
||||
public void setUsername(String name) {
|
||||
this.username = name;
|
||||
}
|
||||
public String getEmail() {
|
||||
return email;
|
||||
}
|
||||
public void setEmail(String email) {
|
||||
this.email = email;
|
||||
}
|
||||
public String getAvatarUrl(){
|
||||
return Gravatar.getImageUrl(email, 130);
|
||||
}
|
||||
public String getLargeAvatarUrl(){
|
||||
return Gravatar.getImageUrl(email, 250);
|
||||
}
|
||||
public String getAddress() {
|
||||
return address;
|
||||
}
|
||||
public void setAddress(String address) {
|
||||
this.address = address;
|
||||
}
|
||||
public boolean isExternal() {
|
||||
return external > 0;
|
||||
}
|
||||
public void setExternal(boolean external) {
|
||||
this.external = (external? 1:0 );
|
||||
}
|
||||
public String getHostname() {
|
||||
return hostname;
|
||||
}
|
||||
public void setHostname(String hostname) {
|
||||
this.hostname = hostname;
|
||||
}
|
||||
public int getPort() {
|
||||
return port;
|
||||
}
|
||||
public void setPort(int port) {
|
||||
this.port = port;
|
||||
}
|
||||
|
||||
}
|
||||
55
hal-core/src/se/hal/struct/devicedata/DimmerEventData.java
Normal file
55
hal-core/src/se/hal/struct/devicedata/DimmerEventData.java
Normal file
|
|
@ -0,0 +1,55 @@
|
|||
/*
|
||||
* Copyright (c) 2015 Ziver
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
package se.hal.struct.devicedata;
|
||||
|
||||
import se.hal.intf.HalEventData;
|
||||
|
||||
public class DimmerEventData extends HalEventData {
|
||||
|
||||
private double dimmValue;
|
||||
|
||||
|
||||
public DimmerEventData() { }
|
||||
public DimmerEventData(double dimmValue, long timestamp) {
|
||||
this.dimmValue = dimmValue;
|
||||
this.setTimestamp(timestamp);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return the dim level from 0.0 to 1.0
|
||||
*/
|
||||
@Override
|
||||
public double getData() {
|
||||
return dimmValue;
|
||||
}
|
||||
@Override
|
||||
public void setData(double dimmValue) {
|
||||
this.dimmValue = dimmValue;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString(){
|
||||
return dimmValue+"%";
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,31 @@
|
|||
package se.hal.struct.devicedata;
|
||||
|
||||
|
||||
import se.hal.intf.HalSensorData;
|
||||
|
||||
public class HumiditySensorData extends HalSensorData {
|
||||
|
||||
private double humidity;
|
||||
|
||||
|
||||
public HumiditySensorData() { }
|
||||
public HumiditySensorData(double humidity, long timestamp) {
|
||||
this.humidity = humidity;
|
||||
this.setTimestamp(timestamp);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public double getData() {
|
||||
return humidity;
|
||||
}
|
||||
@Override
|
||||
public void setData(double humidity) {
|
||||
this.humidity = humidity;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString(){
|
||||
return humidity+"%";
|
||||
}
|
||||
}
|
||||
36
hal-core/src/se/hal/struct/devicedata/LightSensorData.java
Normal file
36
hal-core/src/se/hal/struct/devicedata/LightSensorData.java
Normal file
|
|
@ -0,0 +1,36 @@
|
|||
package se.hal.struct.devicedata;
|
||||
|
||||
import se.hal.intf.HalSensorData;
|
||||
|
||||
public class LightSensorData extends HalSensorData {
|
||||
|
||||
private double lux;
|
||||
|
||||
|
||||
public LightSensorData(){}
|
||||
public LightSensorData(double lux, long timestamp){
|
||||
this.lux = lux;
|
||||
this.setTimestamp(timestamp);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return the light intensity in lux
|
||||
*/
|
||||
@Override
|
||||
public double getData() {
|
||||
return lux;
|
||||
}
|
||||
/**
|
||||
* @param lux set the light intensity in lux
|
||||
*/
|
||||
@Override
|
||||
public void setData(double lux) {
|
||||
this.lux = lux;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString(){
|
||||
return lux+" lux";
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,34 @@
|
|||
package se.hal.struct.devicedata;
|
||||
|
||||
import se.hal.intf.HalSensorData;
|
||||
|
||||
public class PowerConsumptionSensorData extends HalSensorData {
|
||||
|
||||
private double wattHours;
|
||||
|
||||
|
||||
|
||||
public PowerConsumptionSensorData() { }
|
||||
public PowerConsumptionSensorData(double wattHours, long timestamp) {
|
||||
this.wattHours = wattHours;
|
||||
super.setTimestamp(timestamp);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return int representing Watt/Hour
|
||||
*/
|
||||
@Override
|
||||
public double getData() {
|
||||
return wattHours;
|
||||
}
|
||||
@Override
|
||||
public void setData(double wattHours){
|
||||
this.wattHours = wattHours;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString(){
|
||||
return wattHours+" Wh";
|
||||
}
|
||||
}
|
||||
62
hal-core/src/se/hal/struct/devicedata/SwitchEventData.java
Normal file
62
hal-core/src/se/hal/struct/devicedata/SwitchEventData.java
Normal file
|
|
@ -0,0 +1,62 @@
|
|||
/*
|
||||
* Copyright (c) 2015 Ziver
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
package se.hal.struct.devicedata;
|
||||
|
||||
import se.hal.intf.HalEventData;
|
||||
|
||||
public class SwitchEventData extends HalEventData {
|
||||
|
||||
private boolean enabled;
|
||||
|
||||
|
||||
public SwitchEventData() { }
|
||||
public SwitchEventData(boolean enabled, long timestamp) {
|
||||
this.enabled = enabled;
|
||||
this.setTimestamp(timestamp);
|
||||
}
|
||||
|
||||
public void turnOn(){
|
||||
enabled = true;
|
||||
}
|
||||
public void turnOff(){
|
||||
enabled = false;
|
||||
}
|
||||
|
||||
public boolean isOn(){
|
||||
return enabled;
|
||||
}
|
||||
|
||||
@Override
|
||||
public double getData() {
|
||||
return (enabled ? 1.0 : 0.0);
|
||||
}
|
||||
@Override
|
||||
public void setData(double enabled) {
|
||||
this.enabled = enabled > 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString(){
|
||||
return enabled ? "ON" : "OFF";
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,37 @@
|
|||
package se.hal.struct.devicedata;
|
||||
|
||||
import se.hal.intf.HalSensorData;
|
||||
|
||||
public class TemperatureSensorData extends HalSensorData {
|
||||
|
||||
private double temperature;
|
||||
|
||||
|
||||
public TemperatureSensorData(){}
|
||||
public TemperatureSensorData(double temperature, long timestamp){
|
||||
this.temperature = temperature;
|
||||
super.setTimestamp(timestamp);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return temperature in degrees C
|
||||
*/
|
||||
@Override
|
||||
public double getData() {
|
||||
return temperature;
|
||||
}
|
||||
/**
|
||||
* @param temperature the temperature to set in degrees C
|
||||
*/
|
||||
@Override
|
||||
public void setData(double temperature) {
|
||||
this.temperature = temperature;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public String toString(){
|
||||
return temperature+" \u00b0C";
|
||||
}
|
||||
}
|
||||
57
hal-core/src/se/hal/trigger/DateTimeTrigger.java
Normal file
57
hal-core/src/se/hal/trigger/DateTimeTrigger.java
Normal file
|
|
@ -0,0 +1,57 @@
|
|||
package se.hal.trigger;
|
||||
|
||||
import se.hal.intf.HalTrigger;
|
||||
import zutil.CronTimer;
|
||||
import zutil.ui.Configurator;
|
||||
import zutil.ui.Configurator.PreConfigurationActionListener;
|
||||
|
||||
import java.text.SimpleDateFormat;
|
||||
|
||||
public class DateTimeTrigger implements HalTrigger,Configurator.PostConfigurationActionListener {
|
||||
|
||||
@Configurator.Configurable("Minute (Cron format)")
|
||||
private String minute = "00";
|
||||
@Configurator.Configurable("Hour (Cron format)")
|
||||
private String hour = "12";
|
||||
@Configurator.Configurable("Day of the Month (Cron format)")
|
||||
private String dayOfMonth = "*";
|
||||
@Configurator.Configurable("Month (1-12 or Cron format)")
|
||||
private String month = "*";
|
||||
@Configurator.Configurable("Day of the Week (1-7 or Cron format)")
|
||||
private String dayOfWeek = "*";
|
||||
@Configurator.Configurable("Year (Cron format)")
|
||||
private String year = "*";
|
||||
|
||||
private transient CronTimer cronTimer;
|
||||
private transient long timeOut = -1;
|
||||
|
||||
|
||||
@Override
|
||||
public void postConfigurationAction(Configurator configurator, Object obj) {
|
||||
cronTimer = new CronTimer(minute, hour, dayOfMonth, month, dayOfWeek, year);
|
||||
reset();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean evaluate() {
|
||||
if (cronTimer == null)
|
||||
return false;
|
||||
// have we passed the majority of the minute? then get next timeout
|
||||
if (System.currentTimeMillis()-timeOut > 50*1000)
|
||||
reset();
|
||||
return timeOut <= System.currentTimeMillis();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reset() {
|
||||
if (cronTimer != null)
|
||||
timeOut = cronTimer.next();
|
||||
}
|
||||
|
||||
public String toString(){
|
||||
return //"Cron: \""+minute+" "+hour+" "+dayOfMonth+" "+month+" "+dayOfWeek+" "+year+"\" "+
|
||||
"Next timeout: "+
|
||||
(timeOut>0 ? new SimpleDateFormat("yyyy-MM-dd HH:mm").format(timeOut) : timeOut);
|
||||
}
|
||||
|
||||
}
|
||||
69
hal-core/src/se/hal/trigger/DeviceTrigger.java
Normal file
69
hal-core/src/se/hal/trigger/DeviceTrigger.java
Normal file
|
|
@ -0,0 +1,69 @@
|
|||
package se.hal.trigger;
|
||||
|
||||
import se.hal.TriggerManager;
|
||||
import se.hal.intf.HalDeviceData;
|
||||
import se.hal.intf.HalDeviceReportListener;
|
||||
import se.hal.intf.HalTrigger;
|
||||
import se.hal.struct.AbstractDevice;
|
||||
import zutil.ui.Configurator;
|
||||
import zutil.ui.Configurator.PostConfigurationActionListener;
|
||||
import zutil.ui.Configurator.PreConfigurationActionListener;
|
||||
|
||||
/**
|
||||
* An abstract class that implements generic device data logic
|
||||
*/
|
||||
public abstract class DeviceTrigger implements HalTrigger,
|
||||
PreConfigurationActionListener,
|
||||
PostConfigurationActionListener, HalDeviceReportListener<AbstractDevice> {
|
||||
|
||||
@Configurator.Configurable("Device ID")
|
||||
protected int deviceId = -1;
|
||||
@Configurator.Configurable("Trigger only on change")
|
||||
protected boolean triggerOnChange = true;
|
||||
@Configurator.Configurable("Data to compare to")
|
||||
protected double expectedData;
|
||||
|
||||
private transient HalDeviceData receivedData;
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public void preConfigurationAction(Configurator configurator, Object obj) {
|
||||
AbstractDevice device = getDevice(deviceId);
|
||||
if (device != null)
|
||||
device.removeReportListener(this);
|
||||
reset();
|
||||
}
|
||||
@Override
|
||||
public void postConfigurationAction(Configurator configurator, Object obj) {
|
||||
AbstractDevice device = getDevice(deviceId);
|
||||
if (device != null)
|
||||
device.addReportListener(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void receivedReport(AbstractDevice device) {
|
||||
receivedData = device.getDeviceData();
|
||||
// Instant trigger evaluation
|
||||
if (triggerOnChange)
|
||||
TriggerManager.getInstance().evaluateAndExecute();
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean evaluate() {
|
||||
if (receivedData != null)
|
||||
return expectedData == receivedData.getData();
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reset() {
|
||||
if (triggerOnChange) // only reset if we want to trigger on change
|
||||
receivedData = null;
|
||||
}
|
||||
|
||||
|
||||
|
||||
protected abstract AbstractDevice getDevice(long id);
|
||||
}
|
||||
31
hal-core/src/se/hal/trigger/EventTrigger.java
Normal file
31
hal-core/src/se/hal/trigger/EventTrigger.java
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
package se.hal.trigger;
|
||||
|
||||
import se.hal.HalContext;
|
||||
import se.hal.struct.Event;
|
||||
import zutil.log.LogUtil;
|
||||
|
||||
import java.sql.SQLException;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
public class EventTrigger extends DeviceTrigger{
|
||||
private static final Logger logger = LogUtil.getLogger();
|
||||
|
||||
@Override
|
||||
protected Event getDevice(long id) {
|
||||
try {
|
||||
if (id >= 0)
|
||||
return Event.getEvent(HalContext.getDB(), id);
|
||||
} catch (SQLException e){ logger.log(Level.SEVERE, null, e);}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString(){
|
||||
Event event = getDevice(deviceId);
|
||||
return "Trigger " + (triggerOnChange ? "on" : "when") +
|
||||
" event: "+ deviceId +" ("+(event != null ? event.getName() : null) + ")" +
|
||||
" == "+ expectedData;
|
||||
}
|
||||
|
||||
}
|
||||
32
hal-core/src/se/hal/trigger/SensorTrigger.java
Normal file
32
hal-core/src/se/hal/trigger/SensorTrigger.java
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
package se.hal.trigger;
|
||||
|
||||
import se.hal.HalContext;
|
||||
import se.hal.struct.Event;
|
||||
import se.hal.struct.Sensor;
|
||||
import zutil.log.LogUtil;
|
||||
|
||||
import java.sql.SQLException;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
public class SensorTrigger extends DeviceTrigger{
|
||||
private static final Logger logger = LogUtil.getLogger();
|
||||
|
||||
@Override
|
||||
protected Sensor getDevice(long id) {
|
||||
try {
|
||||
if (id >= 0)
|
||||
return Sensor.getSensor(HalContext.getDB(), id);
|
||||
} catch (SQLException e){ logger.log(Level.SEVERE, null, e);}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString(){
|
||||
Sensor sensor = getDevice(deviceId);
|
||||
return "Trigger " + (triggerOnChange ? "on" : "when") +
|
||||
" sensor: "+ deviceId +" ("+(sensor != null ? sensor.getName() : null) + ")" +
|
||||
" == "+ expectedData;
|
||||
}
|
||||
|
||||
}
|
||||
27
hal-core/src/se/hal/trigger/TimerTrigger.java
Normal file
27
hal-core/src/se/hal/trigger/TimerTrigger.java
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
package se.hal.trigger;
|
||||
|
||||
import se.hal.intf.HalTrigger;
|
||||
import zutil.Timer;
|
||||
import zutil.ui.Configurator;
|
||||
|
||||
public class TimerTrigger implements HalTrigger {
|
||||
|
||||
@Configurator.Configurable("Countdown time (in seconds)")
|
||||
private int timerTime = 10; // default 10s
|
||||
private Timer timer;
|
||||
|
||||
|
||||
@Override
|
||||
public boolean evaluate() {
|
||||
return timer == null || timer.hasTimedOut();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reset() {
|
||||
timer = new Timer(timerTime * 1000).start();
|
||||
}
|
||||
|
||||
public String toString(){
|
||||
return "Timer: "+ timerTime +"s";
|
||||
}
|
||||
}
|
||||
106
hal-core/src/se/hal/util/AggregateDataListSqlResult.java
Normal file
106
hal-core/src/se/hal/util/AggregateDataListSqlResult.java
Normal file
|
|
@ -0,0 +1,106 @@
|
|||
package se.hal.util;
|
||||
|
||||
import se.hal.daemon.SensorDataAggregatorDaemon.AggregationPeriodLength;
|
||||
import se.hal.struct.Sensor;
|
||||
import se.hal.struct.devicedata.PowerConsumptionSensorData;
|
||||
import zutil.db.DBConnection;
|
||||
import zutil.db.SQLResultHandler;
|
||||
|
||||
import java.sql.PreparedStatement;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.sql.Statement;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class AggregateDataListSqlResult implements SQLResultHandler<ArrayList<AggregateDataListSqlResult.AggregateData>> {
|
||||
|
||||
public static class AggregateData {
|
||||
public int id;
|
||||
public long timestamp;
|
||||
public Float data;
|
||||
public String username;
|
||||
|
||||
public AggregateData(int id, long time, Float data, String uname) {
|
||||
this.id = id;
|
||||
this.timestamp = time;
|
||||
this.data = data;
|
||||
this.username = uname;
|
||||
}
|
||||
}
|
||||
|
||||
public static List<AggregateData> getAggregateDataForPeriod(DBConnection db, Sensor sensor, AggregationPeriodLength aggrPeriodLength, long ageLimitInMs) throws SQLException {
|
||||
PreparedStatement stmt = db.getPreparedStatement(
|
||||
"SELECT user.username as username,"
|
||||
+ " sensor.*,"
|
||||
+ " sensor_data_aggr.*"
|
||||
+ " FROM sensor_data_aggr, user, sensor"
|
||||
+ " WHERE sensor.id = sensor_data_aggr.sensor_id"
|
||||
+ " AND sensor.id = ?"
|
||||
+ " AND user.id = sensor.user_id"
|
||||
+ " AND user.id = ?"
|
||||
+ " AND timestamp_end-timestamp_start == ?"
|
||||
+ " AND timestamp_start > ?"
|
||||
+ " ORDER BY timestamp_start ASC");
|
||||
stmt.setLong(1, sensor.getId());
|
||||
stmt.setLong(2, sensor.getUser().getId());
|
||||
switch(aggrPeriodLength){
|
||||
case SECOND: stmt.setLong(3, UTCTimeUtility.SECOND_IN_MS-1); break;
|
||||
case MINUTE: stmt.setLong(3, UTCTimeUtility.MINUTE_IN_MS-1); break;
|
||||
case FIVE_MINUTES: stmt.setLong(3, UTCTimeUtility.FIVE_MINUTES_IN_MS-1); break;
|
||||
case FIFTEEN_MINUTES: stmt.setLong(3, UTCTimeUtility.FIFTEEN_MINUTES_IN_MS-1); break;
|
||||
case HOUR: stmt.setLong(3, UTCTimeUtility.HOUR_IN_MS-1); break;
|
||||
case DAY: stmt.setLong(3, UTCTimeUtility.DAY_IN_MS-1); break;
|
||||
case WEEK: stmt.setLong(3, UTCTimeUtility.WEEK_IN_MS-1); break;
|
||||
default: throw new IllegalArgumentException("selected aggrPeriodLength is not supported");
|
||||
}
|
||||
stmt.setLong(4, (System.currentTimeMillis() - ageLimitInMs) );
|
||||
return DBConnection.exec(stmt , new AggregateDataListSqlResult(sensor));
|
||||
}
|
||||
|
||||
|
||||
private Sensor sensor;
|
||||
|
||||
private AggregateDataListSqlResult(Sensor sensor){
|
||||
this.sensor = sensor;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public ArrayList<AggregateData> handleQueryResult(Statement stmt, ResultSet result) throws SQLException {
|
||||
ArrayList<AggregateData> list = new ArrayList<>();
|
||||
long previousTimestampEnd = -1;
|
||||
while (result.next()){
|
||||
|
||||
int id = result.getInt("id");
|
||||
long timestampStart = result.getLong("timestamp_start");
|
||||
long timestampEnd = result.getLong("timestamp_end");
|
||||
String username = result.getString("username");
|
||||
float confidence = result.getFloat("confidence");
|
||||
|
||||
// Calculate the data point
|
||||
float data = result.getFloat("data"); //the "raw" recorded data
|
||||
float estimatedData = data/confidence; //estimate the "real" value of the data by looking at the confidence value
|
||||
|
||||
// Add null data point to list if one or more periods of data is missing before this
|
||||
if (previousTimestampEnd != -1 && sensor.getDeviceConfig() != null){
|
||||
boolean shortInterval = timestampEnd-timestampStart < sensor.getDeviceConfig().getDataInterval();
|
||||
long distance = timestampStart - (previousTimestampEnd + 1);
|
||||
if (// Only add nulls if the report interval is smaller than the aggregated interval
|
||||
!shortInterval && distance > 0 ||
|
||||
// Only add nulls if space between aggr is larger than sensor report interval
|
||||
shortInterval && distance > sensor.getDeviceConfig().getDataInterval())
|
||||
list.add(new AggregateData(id, previousTimestampEnd + 1, null /*Float.NaN*/, username));
|
||||
}
|
||||
|
||||
if (sensor.getDeviceConfig().getSensorDataClass() == PowerConsumptionSensorData.class)
|
||||
estimatedData = (estimatedData/1000f);
|
||||
list.add(new AggregateData(id, timestampEnd, estimatedData, username)); //add this data point to list
|
||||
|
||||
// Update previous end timestamp
|
||||
previousTimestampEnd = timestampEnd;
|
||||
}
|
||||
return list;
|
||||
}
|
||||
}
|
||||
37
hal-core/src/se/hal/util/DeviceDataSqlResult.java
Normal file
37
hal-core/src/se/hal/util/DeviceDataSqlResult.java
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
package se.hal.util;
|
||||
|
||||
import se.hal.intf.HalDeviceData;
|
||||
import zutil.ClassUtil;
|
||||
import zutil.db.SQLResultHandler;
|
||||
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.sql.Statement;
|
||||
|
||||
public class DeviceDataSqlResult implements SQLResultHandler<HalDeviceData> {
|
||||
|
||||
private Class<? extends HalDeviceData> clazz;
|
||||
|
||||
|
||||
public DeviceDataSqlResult(Class<? extends HalDeviceData> clazz){
|
||||
this.clazz = clazz;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public HalDeviceData handleQueryResult(Statement stmt, ResultSet result) throws SQLException {
|
||||
try {
|
||||
if (result.next()) {
|
||||
HalDeviceData dataObj = clazz.newInstance();
|
||||
dataObj.setData(result.getDouble("data"));
|
||||
dataObj.setTimestamp(result.getLong("timestamp"));
|
||||
return dataObj;
|
||||
}
|
||||
} catch (SQLException e){
|
||||
throw e;
|
||||
} catch (Exception e){
|
||||
throw new SQLException(e);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
23
hal-core/src/se/hal/util/DeviceNameComparator.java
Normal file
23
hal-core/src/se/hal/util/DeviceNameComparator.java
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
package se.hal.util;
|
||||
|
||||
import se.hal.struct.AbstractDevice;
|
||||
|
||||
import java.util.Comparator;
|
||||
|
||||
/**
|
||||
* A comparator that compares on the device name.
|
||||
*/
|
||||
public class DeviceNameComparator implements Comparator<AbstractDevice> {
|
||||
private static DeviceNameComparator instance;
|
||||
|
||||
@Override
|
||||
public int compare(AbstractDevice device1, AbstractDevice device2) {
|
||||
return device1.getName().compareTo(device2.getName());
|
||||
}
|
||||
|
||||
public static DeviceNameComparator getInstance() {
|
||||
if (instance == null)
|
||||
instance = new DeviceNameComparator();
|
||||
return instance;
|
||||
}
|
||||
}
|
||||
28
hal-core/src/se/hal/util/HistoryDataListSqlResult.java
Normal file
28
hal-core/src/se/hal/util/HistoryDataListSqlResult.java
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
package se.hal.util;
|
||||
|
||||
import zutil.db.SQLResultHandler;
|
||||
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.sql.Statement;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class HistoryDataListSqlResult implements SQLResultHandler<List<HistoryDataListSqlResult.HistoryData>> {
|
||||
public static class HistoryData{
|
||||
public long timestamp;
|
||||
public float data;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<HistoryData> handleQueryResult(Statement stmt, ResultSet result) throws SQLException {
|
||||
ArrayList<HistoryData> list = new ArrayList<HistoryData>();
|
||||
while(result.next()){
|
||||
HistoryData data = new HistoryData();
|
||||
data.timestamp = result.getLong("timestamp");
|
||||
data.data = result.getFloat("data");
|
||||
list.add(data);
|
||||
}
|
||||
return list;
|
||||
}
|
||||
}
|
||||
52
hal-core/src/se/hal/util/UTCTimePeriod.java
Normal file
52
hal-core/src/se/hal/util/UTCTimePeriod.java
Normal file
|
|
@ -0,0 +1,52 @@
|
|||
package se.hal.util;
|
||||
|
||||
import se.hal.daemon.SensorDataAggregatorDaemon.AggregationPeriodLength;
|
||||
|
||||
public class UTCTimePeriod{
|
||||
private final long start;
|
||||
private final long end;
|
||||
private final AggregationPeriodLength periodLength;
|
||||
|
||||
public UTCTimePeriod(long timestamp, AggregationPeriodLength periodLength){
|
||||
start = UTCTimeUtility.getTimestampPeriodStart(periodLength, timestamp);
|
||||
end = UTCTimeUtility.getTimestampPeriodEnd(periodLength, timestamp);
|
||||
this.periodLength = periodLength;
|
||||
}
|
||||
|
||||
public long getStartTimestamp(){
|
||||
return start;
|
||||
}
|
||||
|
||||
public long getEndTimestamp(){
|
||||
return end;
|
||||
}
|
||||
|
||||
public UTCTimePeriod getNextPeriod(){
|
||||
return new UTCTimePeriod(end+1, periodLength);
|
||||
}
|
||||
|
||||
public UTCTimePeriod getPreviosPeriod(){
|
||||
return new UTCTimePeriod(start-1, periodLength);
|
||||
}
|
||||
|
||||
public boolean containsTimestamp(long timestamp){
|
||||
return start <= timestamp && timestamp <= end;
|
||||
}
|
||||
|
||||
public boolean equals(Object other){
|
||||
if(other == null)
|
||||
return false;
|
||||
if(other instanceof UTCTimePeriod){
|
||||
UTCTimePeriod o = (UTCTimePeriod)other;
|
||||
return start == o.start
|
||||
&& end == o.end
|
||||
&& periodLength == o.periodLength;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public String toString(){
|
||||
return start + "=>" + end + " (" + UTCTimeUtility.getDateString(start) + "=>" + UTCTimeUtility.getDateString(end) + ")";
|
||||
}
|
||||
|
||||
}
|
||||
250
hal-core/src/se/hal/util/UTCTimeUtility.java
Normal file
250
hal-core/src/se/hal/util/UTCTimeUtility.java
Normal file
|
|
@ -0,0 +1,250 @@
|
|||
package se.hal.util;
|
||||
|
||||
import se.hal.daemon.SensorDataAggregatorDaemon.AggregationPeriodLength;
|
||||
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Calendar;
|
||||
import java.util.Locale;
|
||||
import java.util.TimeZone;
|
||||
|
||||
public class UTCTimeUtility {
|
||||
public static final TimeZone TIMEZONE = TimeZone.getTimeZone("UTC");
|
||||
public static final Locale LOCALE = new Locale("sv","SE");
|
||||
|
||||
public static final long SECOND_IN_MS = 1000;
|
||||
public static final long MINUTE_IN_MS = SECOND_IN_MS * 60;
|
||||
public static final long FIVE_MINUTES_IN_MS = MINUTE_IN_MS * 5;
|
||||
public static final long FIFTEEN_MINUTES_IN_MS = MINUTE_IN_MS * 15;
|
||||
public static final long HOUR_IN_MS = MINUTE_IN_MS * 60;
|
||||
public static final long DAY_IN_MS = HOUR_IN_MS * 24;
|
||||
public static final long WEEK_IN_MS = DAY_IN_MS * 7;
|
||||
public static final long INFINITY = Long.MAX_VALUE; //sort of true
|
||||
|
||||
public static long getTimestampPeriodStart(AggregationPeriodLength aggrPeriodLength, long timestamp) throws NumberFormatException{
|
||||
Calendar cal = Calendar.getInstance(TIMEZONE, LOCALE);
|
||||
cal.setTimeInMillis(timestamp);
|
||||
cal.setFirstDayOfWeek(Calendar.MONDAY);
|
||||
switch(aggrPeriodLength){
|
||||
case YEAR:
|
||||
cal.set(Calendar.DAY_OF_YEAR, 1);
|
||||
cal.set(Calendar.HOUR_OF_DAY, 0);
|
||||
cal.set(Calendar.MINUTE, 0);
|
||||
cal.set(Calendar.SECOND, 0);
|
||||
cal.set(Calendar.MILLISECOND, 0);
|
||||
break;
|
||||
case MONTH:
|
||||
cal.set(Calendar.DAY_OF_MONTH, 1);
|
||||
cal.set(Calendar.HOUR_OF_DAY, 0);
|
||||
cal.set(Calendar.MINUTE, 0);
|
||||
cal.set(Calendar.SECOND, 0);
|
||||
cal.set(Calendar.MILLISECOND, 0);
|
||||
break;
|
||||
case WEEK:
|
||||
cal.set(Calendar.DAY_OF_WEEK, Calendar.MONDAY);
|
||||
cal.set(Calendar.HOUR_OF_DAY, 0);
|
||||
cal.set(Calendar.MINUTE, 0);
|
||||
cal.set(Calendar.SECOND, 0);
|
||||
cal.set(Calendar.MILLISECOND, 0);
|
||||
break;
|
||||
case DAY:
|
||||
cal.set(Calendar.HOUR_OF_DAY, 0);
|
||||
cal.set(Calendar.MINUTE, 0);
|
||||
cal.set(Calendar.SECOND, 0);
|
||||
cal.set(Calendar.MILLISECOND, 0);
|
||||
break;
|
||||
case HOUR:
|
||||
cal.set(Calendar.MINUTE, 0);
|
||||
cal.set(Calendar.SECOND, 0);
|
||||
cal.set(Calendar.MILLISECOND, 0);
|
||||
break;
|
||||
case FIVE_MINUTES:
|
||||
cal.set(Calendar.MINUTE, (cal.get(Calendar.MINUTE)/5)*5);
|
||||
cal.set(Calendar.SECOND, 0);
|
||||
cal.set(Calendar.MILLISECOND, 0);
|
||||
break;
|
||||
case FIFTEEN_MINUTES:
|
||||
cal.set(Calendar.MINUTE, (cal.get(Calendar.MINUTE)/15)*15);
|
||||
cal.set(Calendar.SECOND, 0);
|
||||
cal.set(Calendar.MILLISECOND, 0);
|
||||
break;
|
||||
case MINUTE:
|
||||
cal.set(Calendar.SECOND, 0);
|
||||
cal.set(Calendar.MILLISECOND, 0);
|
||||
break;
|
||||
case SECOND:
|
||||
cal.set(Calendar.MILLISECOND, 0);
|
||||
break;
|
||||
}
|
||||
return cal.getTimeInMillis();
|
||||
}
|
||||
|
||||
public static long getTimestampPeriodEnd(AggregationPeriodLength aggrPeriodLength, long timestamp) throws NumberFormatException{
|
||||
Calendar cal = Calendar.getInstance(TIMEZONE, LOCALE);
|
||||
cal.setTimeInMillis(timestamp);
|
||||
cal.setFirstDayOfWeek(Calendar.MONDAY);
|
||||
switch(aggrPeriodLength){
|
||||
case YEAR:
|
||||
cal.set(Calendar.DAY_OF_YEAR, cal.getActualMaximum(Calendar.DAY_OF_YEAR));
|
||||
cal.set(Calendar.HOUR_OF_DAY, 23);
|
||||
cal.set(Calendar.MINUTE, 59);
|
||||
cal.set(Calendar.SECOND, 59);
|
||||
cal.set(Calendar.MILLISECOND, 1000);
|
||||
break;
|
||||
case MONTH:
|
||||
cal.set(Calendar.DAY_OF_MONTH, cal.getActualMaximum(Calendar.DAY_OF_MONTH));
|
||||
cal.set(Calendar.HOUR_OF_DAY, 23);
|
||||
cal.set(Calendar.MINUTE, 59);
|
||||
cal.set(Calendar.SECOND, 59);
|
||||
cal.set(Calendar.MILLISECOND, 1000);
|
||||
break;
|
||||
case WEEK:
|
||||
cal.set(Calendar.DAY_OF_WEEK, Calendar.SUNDAY);
|
||||
cal.set(Calendar.HOUR_OF_DAY, 23);
|
||||
cal.set(Calendar.MINUTE, 59);
|
||||
cal.set(Calendar.SECOND, 59);
|
||||
cal.set(Calendar.MILLISECOND, 1000);
|
||||
break;
|
||||
case DAY:
|
||||
cal.set(Calendar.HOUR_OF_DAY, 23);
|
||||
cal.set(Calendar.MINUTE, 59);
|
||||
cal.set(Calendar.SECOND, 59);
|
||||
cal.set(Calendar.MILLISECOND, 1000);
|
||||
break;
|
||||
case HOUR:
|
||||
cal.set(Calendar.MINUTE, 59);
|
||||
cal.set(Calendar.SECOND, 59);
|
||||
cal.set(Calendar.MILLISECOND, 1000);
|
||||
break;
|
||||
case FIVE_MINUTES:
|
||||
cal.set(Calendar.MINUTE, 4+(cal.get(Calendar.MINUTE)/5)*5);
|
||||
cal.set(Calendar.SECOND, 59);
|
||||
cal.set(Calendar.MILLISECOND, 1000);
|
||||
break;
|
||||
case FIFTEEN_MINUTES:
|
||||
cal.set(Calendar.MINUTE, 14+(cal.get(Calendar.MINUTE)/15)*15);
|
||||
cal.set(Calendar.SECOND, 59);
|
||||
cal.set(Calendar.MILLISECOND, 1000);
|
||||
break;
|
||||
case MINUTE:
|
||||
cal.set(Calendar.SECOND, 59);
|
||||
cal.set(Calendar.MILLISECOND, 1000);
|
||||
break;
|
||||
case SECOND:
|
||||
cal.set(Calendar.MILLISECOND, 1000);
|
||||
break;
|
||||
}
|
||||
return cal.getTimeInMillis()-1; //subtract one
|
||||
}
|
||||
|
||||
public static int getMillisecondInSecondFromTimestamp(long ms) throws NumberFormatException{
|
||||
if(ms < 0)
|
||||
throw new NumberFormatException("argument must be positive");
|
||||
Calendar cal = Calendar.getInstance(TIMEZONE, LOCALE);
|
||||
cal.setTimeInMillis(ms);
|
||||
return cal.get(Calendar.MILLISECOND);
|
||||
}
|
||||
|
||||
public static int getSecondOfMinuteFromTimestamp(long ms) throws NumberFormatException{
|
||||
if(ms < 0)
|
||||
throw new NumberFormatException("argument must be positive");
|
||||
Calendar cal = Calendar.getInstance(TIMEZONE, LOCALE);
|
||||
cal.setTimeInMillis(ms);
|
||||
return cal.get(Calendar.SECOND);
|
||||
}
|
||||
|
||||
public static int getMinuteOfHourFromTimestamp(long ms) throws NumberFormatException{
|
||||
if(ms < 0)
|
||||
throw new NumberFormatException("argument must be positive");
|
||||
Calendar cal = Calendar.getInstance(TIMEZONE, LOCALE);
|
||||
cal.setTimeInMillis(ms);
|
||||
return cal.get(Calendar.MINUTE);
|
||||
}
|
||||
|
||||
public static int getHourOfDayFromTimestamp(long ms) throws NumberFormatException{
|
||||
if(ms < 0)
|
||||
throw new NumberFormatException("argument must be positive");
|
||||
Calendar cal = Calendar.getInstance(TIMEZONE, LOCALE);
|
||||
cal.setTimeInMillis(ms);
|
||||
return cal.get(Calendar.HOUR_OF_DAY);
|
||||
}
|
||||
|
||||
public static int getDayOfWeekFromTimestamp(long ms) throws NumberFormatException{
|
||||
if(ms < 0)
|
||||
throw new NumberFormatException("argument must be positive");
|
||||
Calendar cal = Calendar.getInstance(TIMEZONE, LOCALE);
|
||||
cal.setTimeInMillis(ms);
|
||||
return cal.get(Calendar.DAY_OF_WEEK);
|
||||
}
|
||||
|
||||
public static int getDayOfMonthFromTimestamp(long ms) throws NumberFormatException{
|
||||
if(ms < 0)
|
||||
throw new NumberFormatException("argument must be positive");
|
||||
Calendar cal = Calendar.getInstance(TIMEZONE, LOCALE);
|
||||
cal.setTimeInMillis(ms);
|
||||
return cal.get(Calendar.DAY_OF_MONTH);
|
||||
}
|
||||
|
||||
public static int getDayOfYearFromTimestamp(long ms) throws NumberFormatException{
|
||||
if(ms < 0)
|
||||
throw new NumberFormatException("argument must be positive");
|
||||
Calendar cal = Calendar.getInstance(TIMEZONE, LOCALE);
|
||||
cal.setTimeInMillis(ms);
|
||||
return cal.get(Calendar.DAY_OF_YEAR);
|
||||
}
|
||||
|
||||
public static int getWeekOfYearFromTimestamp(long ms) throws NumberFormatException{
|
||||
if(ms < 0)
|
||||
throw new NumberFormatException("argument must be positive");
|
||||
Calendar cal = Calendar.getInstance(TIMEZONE, LOCALE);
|
||||
cal.setTimeInMillis(ms);
|
||||
return cal.get(Calendar.WEEK_OF_YEAR);
|
||||
}
|
||||
|
||||
public static int getMonthOfYearFromTimestamp(long ms) throws NumberFormatException{
|
||||
if(ms < 0)
|
||||
throw new NumberFormatException("argument must be positive");
|
||||
Calendar cal = Calendar.getInstance(TIMEZONE, LOCALE);
|
||||
cal.setTimeInMillis(ms);
|
||||
return cal.get(Calendar.MONTH);
|
||||
}
|
||||
|
||||
public static int getYearFromTimestamp(long ms) throws NumberFormatException{
|
||||
if(ms < 0)
|
||||
throw new NumberFormatException("argument must be positive");
|
||||
Calendar cal = Calendar.getInstance(TIMEZONE, LOCALE);
|
||||
cal.setTimeInMillis(ms);
|
||||
return cal.get(Calendar.YEAR);
|
||||
}
|
||||
|
||||
public static String timeInMsToString(long ms) throws NumberFormatException{
|
||||
if(ms < 0)
|
||||
throw new NumberFormatException("argument must be positive");
|
||||
String retval = "";
|
||||
int weeks = (int) (ms / WEEK_IN_MS);
|
||||
if(weeks > 0){
|
||||
retval += weeks + "w+";
|
||||
}
|
||||
int days = ((int) (ms / DAY_IN_MS)) % 7;
|
||||
if(days > 0){
|
||||
retval += days + "d+";
|
||||
}
|
||||
int hours = (int) ((ms % DAY_IN_MS) / HOUR_IN_MS);
|
||||
retval += (hours<10?"0"+hours:hours);
|
||||
int minutes = (int) ((ms % HOUR_IN_MS) / MINUTE_IN_MS);
|
||||
retval += ":" + (minutes<10?"0"+minutes:minutes);
|
||||
int seconds = (int) ((ms % MINUTE_IN_MS) / SECOND_IN_MS);
|
||||
retval += ":" + (seconds<10?"0"+seconds:seconds);
|
||||
int milliseconds = (int) (ms % SECOND_IN_MS);
|
||||
retval += "." + (milliseconds<100?"0"+(milliseconds<10?"0"+milliseconds:milliseconds):milliseconds);
|
||||
return retval;
|
||||
}
|
||||
|
||||
public static String getDateString(long timestamp){
|
||||
SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");
|
||||
format.setTimeZone(TIMEZONE);
|
||||
Calendar cal = Calendar.getInstance(TIMEZONE, LOCALE);
|
||||
cal.setTimeInMillis(timestamp);
|
||||
return format.format(cal.getTime());
|
||||
}
|
||||
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue