Created Json endpoints and separated json stuff from http pages, moved chart JS to common file.

This commit is contained in:
Ziver Koc 2016-11-28 17:11:17 +01:00
parent 969f089a9e
commit 92f402a07b
12 changed files with 348 additions and 279 deletions

View file

@ -8,7 +8,7 @@
<sourceFolder url="file://$MODULE_DIR$/resource" type="java-resource" relativeOutputPath="resource" />
<excludeFolder url="file://$MODULE_DIR$/build" />
</content>
<orderEntry type="jdk" jdkName="1.8" jdkType="JavaSDK" />
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
<orderEntry type="module-library" exported="">
<library name="lib">

View file

@ -44,3 +44,80 @@ $.fn.relTimestamp = function() {
return this;
});
};
////////////////////////////////////// Hal functions
////////////// Chart functions
function createChart(elementId, url, updateTime=-1){
var tickConf = {count: 20};
if (updateTime < 60*60*1000)
tickConf['format'] = '%H:%M';
else if (updateTime < 24*60*60*1000)
tickConf['format'] = '%Y-%m-%d %H:%M';
else
tickConf['format'] = '%Y-%m-%d';
var chart = c3.generate({
bindto: elementId,
data: {json: []}, // set empty data, data will be loaded later
axis : {
x : {
type : 'timeseries',
label: 'Timestamp',
tick: tickConf,
},
y: {
label: 'Power (kWh)',
min: 0,
},
y2: {
show: true,
label: 'Temperature (C)',
min: 0,
}
},
grid: {
y: {show: true}
},
point: {
show: false
}
});
updateChart(chart, url, updateTime);;
}
function updateChart(chart, url, updateTime){
$.getJSON(url, function(json){
chart.load(getChartData(json));
});
if (updateTime > 0)
setTimeout(function(){ updateChart(chart, url, updateTime); }, updateTime);
}
function getChartData(json){
var dataXaxis = {};
var dataYaxis = {};
var data = [];
var labels = [];
json.forEach(function(sensor, i) {
var index = 'data'+i;
labels[index] = sensor.user +": "+ sensor.name;
dataXaxis[index] = 'data'+i+'x';
data.push([index+'x'].concat(sensor.timestamps));
data.push([index].concat(sensor.data));
if (sensor.type == "PowerConsumptionSensorData")
dataYaxis[index] = 'y';
else //if (sensor.type == "TemperatureSensorData")
dataYaxis[index] = 'y2';
});
return {
xs: dataXaxis,
columns: data,
names: labels,
type: 'spline',
axes: dataYaxis,
};
}

View file

@ -136,7 +136,7 @@
function drawMap(){
// Get map data
$.getJSON("?json&action=getdata", function(json){
$.getJSON("/data/map?action=getdata", function(json){
// reset map
svg.clear();
@ -183,7 +183,7 @@
$.ajax({
async: false,
dataType: "json",
url: "?json",
url: "/data/map?",
data: {
action: "save",
id: element.attr("device-id"),

View file

@ -20,83 +20,10 @@
<script>
$(function(){
initChart("#minute-power-chart", "?json&data=minute", 5*60*1000);
initChart("#hour-power-chart", "?json&data=hour", 60*60*1000);
initChart("#day-power-chart", "?json&data=day", 24*60*60*1000);
initChart("#week-power-chart", "?json&data=week", 7*24*60*60*1000);
createChart("#minute-power-chart", "/data/sensor?aggr=minute", 5*60*1000);
createChart("#hour-power-chart", "/data/sensor?aggr=hour", 60*60*1000);
createChart("#day-power-chart", "/data/sensor?aggr=day", 24*60*60*1000);
createChart("#week-power-chart", "/data/sensor?aggr=week", 7*24*60*60*1000);
});
function initChart(elementId, url, updateTime){
var tickConf = {count: 20};
if (updateTime < 60*60*1000)
tickConf['format'] = '%H:%M';
else if (updateTime < 24*60*60*1000)
tickConf['format'] = '%Y-%m-%d %H:%M';
else
tickConf['format'] = '%Y-%m-%d';
var chart = c3.generate({
bindto: elementId,
data: {json: []}, // set empty data, data will be loaded later
axis : {
x : {
type : 'timeseries',
label: 'Timestamp',
tick: tickConf,
},
y: {
label: 'Power (kWh)',
min: 0,
},
y2: {
show: true,
label: 'Temperature (C)',
min: 0,
}
},
grid: {
y: {show: true}
},
point: {
show: false
}
});
updateChart(chart, url, updateTime);;
}
function updateChart(chart, url, updateTime){
$.getJSON(url, function(json){
chart.load(getChartData(json));
});
setTimeout(function(){ updateChart(chart, url, updateTime); }, updateTime);
}
function getChartData(json){
var dataXaxis = {};
var dataYaxis = {};
var data = [];
var labels = [];
json.forEach(function(sensor, i) {
var index = 'data'+i;
labels[index] = sensor.user +": "+ sensor.name;
dataXaxis[index] = 'data'+i+'x';
data.push([index+'x'].concat(sensor.timestamps));
data.push([index].concat(sensor.data));
if (sensor.type == "PowerConsumptionSensorData")
dataYaxis[index] = 'y';
else //if (sensor.type == "TemperatureSensorData")
dataYaxis[index] = 'y2';
});
return {
xs: dataXaxis,
columns: data,
names: labels,
type: 'spline',
axes: dataYaxis,
};
}
</script>

View file

@ -1,26 +1,5 @@
<h1 class="page-header">Details for <a href="#">{{sensor.getName()}}</a></h1>
<script>
$(function(){
Morris.Line({
element: "chart",
data: [
{ x: (Date.now()-24*60*60*1000) },
{{#aggregation}}
{ x: {{.timestamp}}, data: {{.data}} },
{{/aggregation}}
{ x: Date.now() }
],
xkey: 'x',
ykeys: ['data'],
labels: ['Data'],
continuousLine: false,
resize: true,
hideHover: 'auto',
});
});
</script>
<div class="col-md-12">
<div class="panel panel-default drop-shadow">
<div class="panel-heading">Last 24 hours</div>
@ -29,7 +8,11 @@
</div>
</div>
</div>
<script>
$(function(){
createChart("#chart", "/data/sensor?aggr=minute&sensor_id={{sensor.getID()}}", 5*60*1000);
});
</script>
<div class="col-md-5">
<div class="panel panel-default drop-shadow">

View file

@ -8,7 +8,6 @@ import zutil.io.file.FileUtil;
import zutil.net.http.HttpHeader;
import zutil.net.http.HttpPage;
import zutil.net.http.HttpPrintStream;
import zutil.parser.DataNode;
import zutil.parser.Templator;
import zutil.parser.json.JSONWriter;
import zutil.ui.Navigation;
@ -44,31 +43,21 @@ public abstract class HalHttpPage implements HttpPage{
Map<String, String> request) throws IOException {
try {
if(this instanceof HalJsonPage &&
(("application/json").equals(header.getHeader("ContentType")) ||
request.containsKey("json"))){
out.setHeader("Content-Type", "application/json");
JSONWriter writer = new JSONWriter(out);
writer.write(((HalJsonPage)this).jsonResponse(session,cookie, request));
writer.close();
}
else{
DBConnection db = HalContext.getDB();
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("alerts", HalAlertManager.getInstance().generateAlerts());
tmpl.set("content", httpRespond(session, cookie, request));
out.print(tmpl.compile());
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("alerts", HalAlertManager.getInstance().generateAlerts());
tmpl.set("content", httpRespond(session, cookie, request));
out.print(tmpl.compile());
} catch (Exception e) {
throw new IOException(e);
}
@ -95,13 +84,4 @@ public abstract class HalHttpPage implements HttpPage{
Map<String, String> request)
throws Exception;
public interface HalJsonPage{
DataNode jsonResponse(
Map<String, Object> session,
Map<String, String> cookie,
Map<String, String> request)
throws Exception;
}
}

View file

@ -0,0 +1,58 @@
package se.hal.intf;
import zutil.net.http.HttpHeader;
import zutil.net.http.HttpPage;
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;
/**
* A interface defining a Hal json endpoint
*/
public abstract class HalJsonPage extends HalHttpPage{
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");
JSONWriter writer = new JSONWriter(out);
try{
writer.write(jsonRespond(session, cookie, request));
} catch (Exception 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;
}

View file

@ -2,10 +2,6 @@ package se.hal.page;
import se.hal.HalContext;
import se.hal.intf.HalHttpPage;
import se.hal.struct.AbstractDevice;
import se.hal.struct.Event;
import se.hal.struct.Sensor;
import zutil.db.DBConnection;
import zutil.io.file.FileUtil;
import zutil.net.http.HttpHeader;
import zutil.net.http.HttpPrintStream;
@ -14,7 +10,6 @@ import zutil.net.http.multipart.MultipartFileField;
import zutil.net.http.multipart.MultipartParser;
import zutil.parser.Base64Decoder;
import zutil.parser.Base64Encoder;
import zutil.parser.DataNode;
import zutil.parser.Templator;
import java.io.IOException;
@ -24,7 +19,7 @@ import java.util.Map;
/**
* Created by Ziver on 2016-06-23.
*/
public class MapHttpPage extends HalHttpPage implements HalHttpPage.HalJsonPage {
public class MapHttpPage extends HalHttpPage {
private static final String TEMPLATE = "resource/web/map.tmpl";
private String bgType;
@ -84,58 +79,6 @@ public class MapHttpPage extends HalHttpPage implements HalHttpPage.HalJsonPage
}
@Override
public DataNode jsonResponse(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;
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;
}
private void loadBgImage() {
String property = HalContext.getStringProperty("map_bgimage");
if (property != null) {

View file

@ -0,0 +1,78 @@
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.parser.DataNode;
import java.sql.SQLException;
import java.util.Map;
/**
* TODO: This json endpoint might not be needed as we have SensorJsonPage?
*
* Created by Ziver on 2016-06-23.
*/
public class MapJsonPage extends HalJsonPage {
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;
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;
}
}

View file

@ -19,7 +19,7 @@ import java.util.ArrayList;
import java.util.List;
import java.util.Map;
public class PCOverviewHttpPage extends HalHttpPage implements HalHttpPage.HalJsonPage {
public class PCOverviewHttpPage extends HalHttpPage {
private static final String TEMPLATE = "resource/web/pc_overview.tmpl";
public PCOverviewHttpPage() {
@ -37,7 +37,7 @@ public class PCOverviewHttpPage extends HalHttpPage implements HalHttpPage.HalJs
DBConnection db = HalContext.getDB();
List<User> users = User.getUsers(db);
List<Sensor> sensors = getSensorList(db);
List<Sensor> sensors = Sensor.getSensors(db);
Templator tmpl = new Templator(FileUtil.find(TEMPLATE));
tmpl.set("users", User.getUsers(db));
@ -46,87 +46,5 @@ public class PCOverviewHttpPage extends HalHttpPage implements HalHttpPage.HalJs
return tmpl;
}
@Override
public DataNode jsonResponse(
Map<String, Object> session,
Map<String, String> cookie,
Map<String, String> request) throws Exception {
DataNode root = new DataNode(DataNode.DataType.List);
DBConnection db = HalContext.getDB();
List<Sensor> sensors = getSensorList(db);
if (request.containsKey("data")) {
AggregationPeriodLength aggrType;
long aggrLength = UTCTimeUtility.INFINITY;
switch(request.get("data")){
case "minute":
default:
aggrType = AggregationPeriodLength.FIVE_MINUTES;
aggrLength = UTCTimeUtility.DAY_IN_MS;
break;
case "hour":
aggrType = AggregationPeriodLength.HOUR;
aggrLength = UTCTimeUtility.WEEK_IN_MS;
break;
case "day":
aggrType = AggregationPeriodLength.DAY;
break;
case "week":
aggrType = AggregationPeriodLength.WEEK;
break;
}
for (Sensor sensor : sensors) {
addAggregateDataToDataNode(root, sensor, aggrLength,
AggregateDataListSqlResult.getAggregateDataForPeriod(db, sensor, aggrType, aggrLength));
}
}
return root;
}
private void addAggregateDataToDataNode(DataNode root, Sensor sensor, long endTime, List<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);
DataNode deviceNode = new DataNode(DataNode.DataType.Map);
deviceNode.set("name", sensor.getName());
deviceNode.set("user", sensor.getUser().getUsername());
deviceNode.set("type", sensor.getDeviceConfig().getSensorDataClass().getSimpleName());
deviceNode.set("timestamps", timestampNode);
deviceNode.set("data", dataNode);
root.add(deviceNode);
}
/**
* @return a List of all PowerConsumption sensors
*/
private List<Sensor> getSensorList(DBConnection db) throws SQLException {
List<Sensor> sensors = new ArrayList<>();
for (Sensor sensor : Sensor.getSensors(db)) {
if (sensor.getDeviceConfig() != null) // Show all sensors for now
// if (sensor.getDeviceConfig() != null &&
// sensor.getDeviceConfig().getSensorDataClass() == PowerConsumptionSensorData.class)
sensors.add(sensor);
}
return sensors;
}
}

View file

@ -0,0 +1,107 @@
package se.hal.page;
import se.hal.HalContext;
import se.hal.deamon.SensorDataAggregatorDaemon;
import se.hal.intf.HalJsonPage;
import se.hal.struct.Sensor;
import se.hal.util.AggregateDataListSqlResult;
import se.hal.util.UTCTimeUtility;
import zutil.db.DBConnection;
import zutil.log.LogUtil;
import zutil.parser.DataNode;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.logging.Logger;
/**
* Request parameters:
* aggr: Aggrigation periods. Possible values: minute,hour,day,week
*/
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
List<Sensor> sensors = new ArrayList<>();
for (Sensor sensor : Sensor.getSensors(db)) {
if (sensor.getDeviceConfig() != null) // Show all sensors for now
// if (sensor.getDeviceConfig() != null &&
// sensor.getDeviceConfig().getSensorDataClass() == PowerConsumptionSensorData.class)
sensors.add(sensor);
}
//// Figure out aggregation period
SensorDataAggregatorDaemon.AggregationPeriodLength aggrType;
long aggrLength = UTCTimeUtility.INFINITY;
switch (request.get("aggr")) {
case "minute":
default:
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;
break;
case "week":
aggrType = SensorDataAggregatorDaemon.AggregationPeriodLength.WEEK;
break;
}
/// Generate DataNode
for (Sensor sensor : sensors) {
addAggregateDataToDataNode(root, sensor, aggrLength,
AggregateDataListSqlResult.getAggregateDataForPeriod(db, sensor, aggrType, aggrLength));
}
return root;
}
private void addAggregateDataToDataNode(DataNode root, Sensor sensor, 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);
DataNode deviceNode = new DataNode(DataNode.DataType.Map);
deviceNode.set("name", sensor.getName());
deviceNode.set("user", sensor.getUser().getUsername());
deviceNode.set("type", sensor.getDeviceConfig().getSensorDataClass().getSimpleName());
deviceNode.set("timestamps", timestampNode);
deviceNode.set("data", dataNode);
root.add(deviceNode);
}
}

View file

@ -52,8 +52,6 @@ public class SensorOverviewHttpPage extends HalHttpPage {
Templator tmpl = new Templator(FileUtil.find(DETAIL_TEMPLATE));
tmpl.set("sensor", sensor);
tmpl.set("history", history);
tmpl.set("aggregation",
AggregateDataListSqlResult.getAggregateDataForPeriod(db, sensor, AggregationPeriodLength.FIVE_MINUTES, UTCTimeUtility.DAY_IN_MS));
return tmpl;
}
else {