From 92f402a07bebd47e64fccb7bd94c68af6fcf9c1c Mon Sep 17 00:00:00 2001 From: Ziver Koc Date: Mon, 28 Nov 2016 17:11:17 +0100 Subject: [PATCH] Created Json endpoints and separated json stuff from http pages, moved chart JS to common file. --- Hal.iml | 2 +- resource/web/js/hal.js | 77 ++++++++++++++ resource/web/map.tmpl | 4 +- resource/web/pc_overview.tmpl | 81 +-------------- resource/web/sensor_detail.tmpl | 27 +---- src/se/hal/intf/HalHttpPage.java | 46 +++------ src/se/hal/intf/HalJsonPage.java | 58 +++++++++++ src/se/hal/page/MapHttpPage.java | 59 +---------- src/se/hal/page/MapJsonPage.java | 78 ++++++++++++++ src/se/hal/page/PCOverviewHttpPage.java | 86 +--------------- src/se/hal/page/SensorJsonPage.java | 107 ++++++++++++++++++++ src/se/hal/page/SensorOverviewHttpPage.java | 2 - 12 files changed, 348 insertions(+), 279 deletions(-) create mode 100755 src/se/hal/intf/HalJsonPage.java create mode 100755 src/se/hal/page/MapJsonPage.java create mode 100755 src/se/hal/page/SensorJsonPage.java diff --git a/Hal.iml b/Hal.iml index 5f300ff3..934527c6 100755 --- a/Hal.iml +++ b/Hal.iml @@ -8,7 +8,7 @@ - + diff --git a/resource/web/js/hal.js b/resource/web/js/hal.js index b27379fe..f49ea49f 100755 --- a/resource/web/js/hal.js +++ b/resource/web/js/hal.js @@ -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, + }; +} \ No newline at end of file diff --git a/resource/web/map.tmpl b/resource/web/map.tmpl index a0094a59..2d7b6875 100755 --- a/resource/web/map.tmpl +++ b/resource/web/map.tmpl @@ -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"), diff --git a/resource/web/pc_overview.tmpl b/resource/web/pc_overview.tmpl index 482f9048..be557c5a 100755 --- a/resource/web/pc_overview.tmpl +++ b/resource/web/pc_overview.tmpl @@ -20,83 +20,10 @@ diff --git a/resource/web/sensor_detail.tmpl b/resource/web/sensor_detail.tmpl index 807c51ba..2bcdb897 100755 --- a/resource/web/sensor_detail.tmpl +++ b/resource/web/sensor_detail.tmpl @@ -1,26 +1,5 @@

Details for {{sensor.getName()}}

- -
Last 24 hours
@@ -29,7 +8,11 @@
- +
diff --git a/src/se/hal/intf/HalHttpPage.java b/src/se/hal/intf/HalHttpPage.java index cf1e6336..9124ca20 100755 --- a/src/se/hal/intf/HalHttpPage.java +++ b/src/se/hal/intf/HalHttpPage.java @@ -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 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 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 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 request) throws Exception; - - - public interface HalJsonPage{ - DataNode jsonResponse( - Map session, - Map cookie, - Map request) - throws Exception; - } } \ No newline at end of file diff --git a/src/se/hal/intf/HalJsonPage.java b/src/se/hal/intf/HalJsonPage.java new file mode 100755 index 00000000..d5f53139 --- /dev/null +++ b/src/se/hal/intf/HalJsonPage.java @@ -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 session, + Map cookie, + Map 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 session, + Map cookie, + Map request) throws Exception { + return null; + } + + + protected abstract DataNode jsonRespond( + Map session, + Map cookie, + Map request) throws Exception; +} \ No newline at end of file diff --git a/src/se/hal/page/MapHttpPage.java b/src/se/hal/page/MapHttpPage.java index 2cf24eb0..7796e617 100755 --- a/src/se/hal/page/MapHttpPage.java +++ b/src/se/hal/page/MapHttpPage.java @@ -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 session, - Map cookie, - Map 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) { diff --git a/src/se/hal/page/MapJsonPage.java b/src/se/hal/page/MapJsonPage.java new file mode 100755 index 00000000..8695bae2 --- /dev/null +++ b/src/se/hal/page/MapJsonPage.java @@ -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 session, + Map cookie, + Map 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; + } + +} diff --git a/src/se/hal/page/PCOverviewHttpPage.java b/src/se/hal/page/PCOverviewHttpPage.java index f3d1c768..d9a2f0c3 100755 --- a/src/se/hal/page/PCOverviewHttpPage.java +++ b/src/se/hal/page/PCOverviewHttpPage.java @@ -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 users = User.getUsers(db); - List sensors = getSensorList(db); + List 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 session, - Map cookie, - Map request) throws Exception { - DataNode root = new DataNode(DataNode.DataType.List); - DBConnection db = HalContext.getDB(); - List 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 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 getSensorList(DBConnection db) throws SQLException { - List 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; - } } diff --git a/src/se/hal/page/SensorJsonPage.java b/src/se/hal/page/SensorJsonPage.java new file mode 100755 index 00000000..21dcd7d6 --- /dev/null +++ b/src/se/hal/page/SensorJsonPage.java @@ -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 session, + Map cookie, + Map request) throws Exception{ + + DBConnection db = HalContext.getDB(); + DataNode root = new DataNode(DataNode.DataType.List); + + //// Get sensors + List 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 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); + } + +} diff --git a/src/se/hal/page/SensorOverviewHttpPage.java b/src/se/hal/page/SensorOverviewHttpPage.java index 77e1ccc1..2178842c 100755 --- a/src/se/hal/page/SensorOverviewHttpPage.java +++ b/src/se/hal/page/SensorOverviewHttpPage.java @@ -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 {