Added network sync
Former-commit-id: 59cc72d4de037d9ae2ecdd6d1bc5aa7816c883d8
This commit is contained in:
parent
c27620d63d
commit
2aa2c76025
9 changed files with 363 additions and 10 deletions
BIN
hal.db
BIN
hal.db
Binary file not shown.
16
src/se/koc/hal/HalContext.java
Normal file
16
src/se/koc/hal/HalContext.java
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
package se.koc.hal;
|
||||
|
||||
import zutil.db.DBConnection;
|
||||
|
||||
public class HalContext {
|
||||
|
||||
public static DBConnection db;
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
public static void initialize() throws Exception{
|
||||
db = new DBConnection(DBConnection.DBMS.SQLite, "hal.db");
|
||||
}
|
||||
}
|
||||
|
|
@ -12,6 +12,7 @@ import java.util.TimerTask;
|
|||
import java.util.logging.Logger;
|
||||
|
||||
import se.koc.hal.HalContext;
|
||||
import se.koc.hal.struct.Sensor;
|
||||
import zutil.db.DBConnection;
|
||||
import zutil.db.SQLResultHandler;
|
||||
import zutil.db.handler.SimpleSQLHandler;
|
||||
|
|
@ -120,10 +121,6 @@ public class DataAggregatorDaemon extends TimerTask implements HalDaemon {
|
|||
return cal.getTimeInMillis();
|
||||
}
|
||||
|
||||
public static Integer getNextSequenceId(long sensorId) throws SQLException{
|
||||
Integer id = HalContext.db.exec("SELECT MAX(sequence_id) FROM sensor_data_aggr WHERE sensor_id == "+ sensorId, new SimpleSQLHandler<Integer>());
|
||||
return (id != null ? id+1 : 1);
|
||||
}
|
||||
|
||||
private class FiveMinuteAggregator implements SQLResultHandler<Object>{
|
||||
@Override
|
||||
|
|
@ -139,7 +136,7 @@ public class DataAggregatorDaemon extends TimerTask implements HalDaemon {
|
|||
logger.finer("Calculated minute period: "+ currentPeriodTimestamp +" sum: "+ sum +" confidence: "+ confidence);
|
||||
HalContext.db.exec(String.format(Locale.US, "INSERT INTO sensor_data_aggr(sensor_id, sequence_id, timestamp_start, timestamp_end, data, confidence) VALUES(%d, %d, %d, %d, %d, %f)",
|
||||
result.getInt("sensor_id"),
|
||||
getNextSequenceId(result.getInt("sensor_id")),
|
||||
Sensor.getHighestSequenceId(result.getInt("sensor_id")) + 1,
|
||||
currentPeriodTimestamp,
|
||||
currentPeriodTimestamp + FIVE_MINUTES_IN_MS -1,
|
||||
sum,
|
||||
|
|
@ -171,7 +168,7 @@ public class DataAggregatorDaemon extends TimerTask implements HalDaemon {
|
|||
logger.finer("Calculated hour period: "+ currentPeriodTimestamp +" sum: "+ sum +" confidence: "+ aggrConfidence);
|
||||
HalContext.db.exec(String.format(Locale.US, "INSERT INTO sensor_data_aggr(sensor_id, sequence_id, timestamp_start, timestamp_end, data, confidence) VALUES(%d, %d, %d, %d, %d, %f)",
|
||||
result.getInt("sensor_id"),
|
||||
getNextSequenceId(result.getInt("sensor_id")),
|
||||
Sensor.getHighestSequenceId(result.getInt("sensor_id")) + 1,
|
||||
currentPeriodTimestamp,
|
||||
currentPeriodTimestamp + HOUR_IN_MS -1,
|
||||
sum,
|
||||
|
|
@ -208,7 +205,7 @@ public class DataAggregatorDaemon extends TimerTask implements HalDaemon {
|
|||
logger.finer("Calculated day period: "+ currentPeriodTimestamp +" sum: "+ sum +" confidence: "+ aggrConfidence+ " samples: " + samples);
|
||||
HalContext.db.exec(String.format(Locale.US, "INSERT INTO sensor_data_aggr(sensor_id, sequence_id, timestamp_start, timestamp_end, data, confidence) VALUES(%d, %d, %d, %d, %d, %f)",
|
||||
result.getInt("sensor_id"),
|
||||
getNextSequenceId(result.getInt("sensor_id")),
|
||||
Sensor.getHighestSequenceId(result.getInt("sensor_id")) + 1,
|
||||
currentPeriodTimestamp,
|
||||
currentPeriodTimestamp + DAY_IN_MS -1,
|
||||
sum,
|
||||
|
|
|
|||
89
src/se/koc/hal/deamon/DataSynchronizationClient.java
Normal file
89
src/se/koc/hal/deamon/DataSynchronizationClient.java
Normal file
|
|
@ -0,0 +1,89 @@
|
|||
package se.koc.hal.deamon;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.ObjectInputStream;
|
||||
import java.io.ObjectOutputStream;
|
||||
import java.io.Serializable;
|
||||
import java.net.Socket;
|
||||
import java.net.UnknownHostException;
|
||||
import java.sql.SQLException;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Timer;
|
||||
import java.util.TimerTask;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import se.koc.hal.HalContext;
|
||||
import se.koc.hal.deamon.DataSynchronizationDaemon.SensorDataDTO;
|
||||
import se.koc.hal.deamon.DataSynchronizationDaemon.SensorDataListDTO;
|
||||
import se.koc.hal.struct.Sensor;
|
||||
import se.koc.hal.struct.User;
|
||||
import zutil.db.DBConnection;
|
||||
import zutil.log.LogUtil;
|
||||
|
||||
public class DataSynchronizationClient extends TimerTask implements HalDaemon{
|
||||
private static final Logger logger = LogUtil.getLogger();
|
||||
private static final long SYNC_INTERVALL = 5 * 60 * 1000; // 5 min
|
||||
|
||||
|
||||
@Override
|
||||
public void initiate(Timer timer) {
|
||||
timer.schedule(this, 10000, SYNC_INTERVALL);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
DBConnection db = HalContext.db;
|
||||
List<User> users = User.getExternalUsers(db);
|
||||
for(User user : users){
|
||||
logger.fine("Synchronizing user: "+ user.getName() +" ("+user.getHostname()+":"+user.getPort()+")");
|
||||
try (Socket s = new Socket(user.getHostname(), user.getPort());){
|
||||
ObjectOutputStream out = new ObjectOutputStream(s.getOutputStream());
|
||||
ObjectInputStream in = new ObjectInputStream(s.getInputStream());
|
||||
|
||||
List<Sensor> sensors = Sensor.getSensors(db, user);
|
||||
for(Sensor sensor : sensors){
|
||||
PeerDataReqDTO req = new PeerDataReqDTO();
|
||||
req.sensorId = sensor.getExternalId();
|
||||
req.offsetSequenceId = Sensor.getHighestSequenceId(sensor.getId());
|
||||
out.writeObject(req);
|
||||
|
||||
SensorDataListDTO dataList = (SensorDataListDTO) in.readObject();
|
||||
for(SensorDataDTO data : dataList){
|
||||
int deletions = db.exec("DELETE FROM sensor_data_aggr WHERE sensor_id == "+ sensor.getId() +" AND "+ data.timestampStart +" <= timestamp_start AND timestamp_end <= "+ data.timestampEnd);
|
||||
logger.finer("Aggregate data replaced "+ deletions +" entries");
|
||||
db.exec(String.format(Locale.US, "INSERT INTO sensor_data_aggr(sensor_id, sequence_id, timestamp_start, timestamp_end, data, confidence) VALUES(%d, %d, %d, %d, %d, %f)",
|
||||
sensor.getId(),
|
||||
data.sequenceId,
|
||||
data.timestampStart,
|
||||
data.timestampEnd,
|
||||
data.data,
|
||||
data.confidence));
|
||||
}
|
||||
}
|
||||
|
||||
} catch (UnknownHostException e) {
|
||||
e.printStackTrace();
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
} catch (ClassNotFoundException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
}
|
||||
} catch (SQLException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/////////////// DTO ///////////////////////
|
||||
protected class PeerDataReqDTO implements Serializable{
|
||||
private static final long serialVersionUID = -9066734025245139989L;
|
||||
|
||||
public long sensorId;
|
||||
public long offsetSequenceId; // highest known sequence id
|
||||
}
|
||||
}
|
||||
116
src/se/koc/hal/deamon/DataSynchronizationDaemon.java
Normal file
116
src/se/koc/hal/deamon/DataSynchronizationDaemon.java
Normal file
|
|
@ -0,0 +1,116 @@
|
|||
package se.koc.hal.deamon;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.ObjectInputStream;
|
||||
import java.io.ObjectOutputStream;
|
||||
import java.io.Serializable;
|
||||
import java.net.Socket;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.sql.Statement;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Timer;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import se.koc.hal.HalContext;
|
||||
import se.koc.hal.deamon.DataSynchronizationClient.PeerDataReqDTO;
|
||||
import zutil.db.SQLResultHandler;
|
||||
import zutil.log.LogUtil;
|
||||
import zutil.net.threaded.ThreadedTCPNetworkServer;
|
||||
import zutil.net.threaded.ThreadedTCPNetworkServerThread;
|
||||
|
||||
public class DataSynchronizationDaemon extends ThreadedTCPNetworkServer implements HalDaemon{
|
||||
private static final Logger logger = LogUtil.getLogger();
|
||||
public static final int SERVER_PORT = 6666;
|
||||
|
||||
|
||||
public DataSynchronizationDaemon() {
|
||||
super(SERVER_PORT);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initiate(Timer timer) {
|
||||
this.start();
|
||||
}
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
protected ThreadedTCPNetworkServerThread getThreadInstance(Socket s) {
|
||||
try {
|
||||
return new DataSynchronizationDaemonThread(s);
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
private class DataSynchronizationDaemonThread implements ThreadedTCPNetworkServerThread{
|
||||
private Socket s;
|
||||
private ObjectOutputStream out;
|
||||
private ObjectInputStream in;
|
||||
|
||||
|
||||
public DataSynchronizationDaemonThread(Socket s) throws IOException{
|
||||
this.s = s;
|
||||
this.out = new ObjectOutputStream(s.getOutputStream());
|
||||
this.in = new ObjectInputStream(s.getInputStream());
|
||||
}
|
||||
|
||||
|
||||
public void run(){
|
||||
logger.fine("User connected: "+ s.getInetAddress().getHostName());
|
||||
|
||||
try {
|
||||
Object obj = null;
|
||||
while((obj = in.readObject()) != null){
|
||||
if(obj instanceof PeerDataReqDTO){
|
||||
PeerDataReqDTO req = (PeerDataReqDTO) obj;
|
||||
|
||||
SensorDataListDTO list = HalContext.db.exec("SELECT * FROM sensor_data_aggr WHERE sensor_id == "+ req.sensorId +" AND sequence_id > "+ req.offsetSequenceId,
|
||||
new SQLResultHandler<SensorDataListDTO>() {
|
||||
@Override
|
||||
public SensorDataListDTO handleQueryResult(Statement stmt, ResultSet result) throws SQLException {
|
||||
SensorDataListDTO list = new SensorDataListDTO();
|
||||
while(result.next()){
|
||||
SensorDataDTO data = new SensorDataDTO();
|
||||
data.sequenceId = result.getLong("sensor_id");
|
||||
data.timestampStart = result.getLong("timestamp_start");
|
||||
data.timestampEnd = result.getLong("timestamp_end");
|
||||
data.data = result.getInt("data");
|
||||
data.confidence = result.getFloat("confidence");
|
||||
list.add(data);
|
||||
}
|
||||
return list;
|
||||
}
|
||||
});
|
||||
out.writeObject(list);
|
||||
}
|
||||
}
|
||||
} catch (ClassNotFoundException e) {
|
||||
e.printStackTrace();
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
} catch (SQLException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/////////////// DTO ///////////////////////
|
||||
protected class SensorDataListDTO extends ArrayList<SensorDataDTO> implements Serializable{
|
||||
private static final long serialVersionUID = -5701618637734020691L;
|
||||
}
|
||||
|
||||
protected class SensorDataDTO implements Serializable{
|
||||
private static final long serialVersionUID = 8494331502087736809L;
|
||||
|
||||
public long sequenceId;
|
||||
public long timestampStart;
|
||||
public long timestampEnd;
|
||||
public int data;
|
||||
public float confidence;
|
||||
}
|
||||
}
|
||||
23
src/se/koc/hal/page/PCHeatMapHttpPage.java
Normal file
23
src/se/koc/hal/page/PCHeatMapHttpPage.java
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
package se.koc.hal.page;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Map;
|
||||
|
||||
import zutil.io.file.FileUtil;
|
||||
import zutil.net.http.HttpHeaderParser;
|
||||
import zutil.net.http.HttpPage;
|
||||
import zutil.net.http.HttpPrintStream;
|
||||
import zutil.parser.Templator;
|
||||
|
||||
public class PCHeatMapHttpPage implements HttpPage {
|
||||
|
||||
@Override
|
||||
public void respond(HttpPrintStream out, HttpHeaderParser client_info,
|
||||
Map<String, Object> session, Map<String, String> cookie,
|
||||
Map<String, String> request) throws IOException {
|
||||
|
||||
Templator tmpl = new Templator(FileUtil.find("web-resource/heatmap.html"));
|
||||
out.print(tmpl.compile());
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -4,9 +4,11 @@ import java.sql.PreparedStatement;
|
|||
import java.sql.SQLException;
|
||||
import java.util.List;
|
||||
|
||||
import se.koc.hal.HalContext;
|
||||
import zutil.db.DBConnection;
|
||||
import zutil.db.bean.DBBean;
|
||||
import zutil.db.bean.DBBeanSQLResultHandler;
|
||||
import zutil.db.handler.SimpleSQLHandler;
|
||||
|
||||
/**
|
||||
* Created by Ziver on 2015-12-03.
|
||||
|
|
@ -14,9 +16,10 @@ import zutil.db.bean.DBBeanSQLResultHandler;
|
|||
@DBBean.DBTable("sensor")
|
||||
public class Sensor extends DBBean{
|
||||
private String name;
|
||||
private int user_id;
|
||||
private long user_id;
|
||||
private String type;
|
||||
private String config;
|
||||
private long external_id;
|
||||
|
||||
|
||||
public static List<Sensor> getExternalSensors(DBConnection db) throws SQLException{
|
||||
|
|
@ -29,6 +32,16 @@ public class Sensor extends DBBean{
|
|||
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 sensor.* FROM sensor,user WHERE user.id == "+ user.getId() );
|
||||
return DBConnection.exec(stmt, DBBeanSQLResultHandler.createList(Sensor.class, db) );
|
||||
}
|
||||
|
||||
|
||||
public static long getHighestSequenceId(long sensorId) throws SQLException{
|
||||
Integer id = HalContext.db.exec("SELECT MAX(sequence_id) FROM sensor_data_aggr WHERE sensor_id == "+ sensorId, new SimpleSQLHandler<Integer>());
|
||||
return (id != null ? id+1 : 1);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
|
@ -38,10 +51,10 @@ public class Sensor extends DBBean{
|
|||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
public int getUser_id() {
|
||||
public long getUserId() {
|
||||
return user_id;
|
||||
}
|
||||
public void setUser_id(int user_id) {
|
||||
public void setUserId(long user_id) {
|
||||
this.user_id = user_id;
|
||||
}
|
||||
public String getType() {
|
||||
|
|
@ -50,4 +63,10 @@ public class Sensor extends DBBean{
|
|||
public void setType(String type) {
|
||||
this.type = type;
|
||||
}
|
||||
public long getExternalId() {
|
||||
return external_id;
|
||||
}
|
||||
public void setExternalId(long external_id) {
|
||||
this.external_id = external_id;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
88
web-resource/heatmap.html
Normal file
88
web-resource/heatmap.html
Normal file
|
|
@ -0,0 +1,88 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<meta name="description" content="">
|
||||
<meta name="author" content="">
|
||||
|
||||
<title>Power;Challenge</title>
|
||||
|
||||
<!-- Bootstrap core CSS -->
|
||||
<link href="css/bootstrap.min.css" rel="stylesheet">
|
||||
<link href="css/main.css" rel="stylesheet">
|
||||
|
||||
<script src="js/jquery-1.11.3.min.js"></script>
|
||||
<script src="js/bootstrap.min.js"></script>
|
||||
<script src="http://maps.googleapis.com/maps/api/js"></script>
|
||||
|
||||
<script>
|
||||
function initialize() {
|
||||
var mapProp = {
|
||||
center:new google.maps.LatLng(59.329323,18.068581),
|
||||
zoom:12,
|
||||
mapTypeId:google.maps.MapTypeId.ROADMAP
|
||||
};
|
||||
var map=new google.maps.Map(document.getElementById("googleMap"),mapProp);
|
||||
|
||||
var home = new google.maps.Circle({
|
||||
center: {lat: 59.365954, lng: 17.975351},
|
||||
radius:2000,
|
||||
strokeColor:"#00FF00",
|
||||
strokeOpacity:0.8,
|
||||
strokeWeight:2,
|
||||
fillColor:"#00FF00",
|
||||
fillOpacity:0.4,
|
||||
map: map
|
||||
});
|
||||
|
||||
var external = new google.maps.Circle({
|
||||
center: {lat: 59.275638, lng: 18.024362},
|
||||
radius:2000,
|
||||
strokeColor:"#FF0000",
|
||||
strokeOpacity:0.8,
|
||||
strokeWeight:2,
|
||||
fillColor:"#FF0000",
|
||||
fillOpacity:0.4,
|
||||
map: map
|
||||
});
|
||||
}
|
||||
google.maps.event.addDomListener(window, 'load', initialize);
|
||||
</script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
<nav class="navbar navbar-inverse navbar-fixed-top">
|
||||
<div class="container-fluid">
|
||||
<div class="navbar-header">
|
||||
<a class="navbar-brand" href="#">Power;Challenge</a>
|
||||
</div>
|
||||
<div id="navbar" class="navbar-collapse collapse">
|
||||
<ul class="nav navbar-nav navbar-right">
|
||||
<li><a href="#">Ziver</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<div class="container-fluid">
|
||||
<div class="row">
|
||||
<div class="col-sm-3 col-md-2 sidebar">
|
||||
<ul class="nav nav-sidebar">
|
||||
<li><a href="/">Overview</a></li>
|
||||
<li class="active"><a href="heatmap">Heat Map <span class="sr-only">(current)</span></a></li>
|
||||
<li><a href="#">Statistics</a></li>
|
||||
<li><a href="configure">Configuration</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="col-sm-9 col-sm-offset-3 col-md-10 col-md-offset-2 main">
|
||||
<h1 class="page-header">Heat Map</h1>
|
||||
|
||||
<div id="googleMap" style="width: 95%;height: 800px;"></div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
5
web-resource/js/jquery-1.11.3.min.js
vendored
Normal file
5
web-resource/js/jquery-1.11.3.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
Loading…
Add table
Add a link
Reference in a new issue