This article aims to develop a real-time environment monitoring demo with vert.x and echarts.
vert.x see http://vertx.io
ehcars see http://echarts.baidu.com/index.html
The whole business process is relatively simple, that is, the page communicates with the back-end web server through sockJs, and then the back-end server pushes data to the page in real time.
The page code is as follows:
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title></title> <script type="text/javascript" src="/js/jquery-easyui-1.5.2/jquery.min.js"></script> <script type="text/javascript" src="/js/sockjs.min.js"></script> <script type="text/javascript" src="/js/vertx-eventbus.min.js"></script> <script type="text/javascript" src="/js/baidu/echarts-all.js"></script> </head> <body> <div><b>Environmental real-time monitoring:</b></div> <br> <table width="800"> <tr> <td><font color="blue">温度</font></td> <td><font color="blue">湿度</font></td> <td><font color="blue">CO2</font></td> <td><font color="blue">NH3</font></td> </tr> <tr> <td><div id="T"></div></td> <td><div id="H"></div></td> <td><div id="CO2"></div></td> <td><div id="NH3"></div></td> </tr> </table> <br> <table> <tr> <td><div id="T_chart" style="width: 250px;height:250px;"></div></td> <td><div id="T_chart_line" style="width: 500px;height:250px;"></div></td> <td><div id="H_chart" style="width: 250px;height:250px;"></div></td> <td><div id="H_chart_line" style="width: 500px;height:250px;"></div></td> </tr> <tr> <td><div id="CO2_chart" style="width: 250px;height:250px;"></div></td> <td><div id="CO2_chart_line" style="width: 500px;height:250px;"></div></td> <td><div id="NH3_chart" style="width: 250px;height:250px;"></div></td> <td><div id="NH3_chart_line" style="width: 500px;height:250px;"></div></td> </tr> </table> <script> // dash board---------------------------------------------- --------------------- var myChart_T = echarts.init(document.getElementById("T_chart")); var option_T = { series : [ { name:'temperature', type:'gauge', detail : {formatter:'{value}℃'}, data:[{value: 50, name: '温度'}], min: 0, max:50, axisLine:{ show: true, //Specify a color for each segment (in this example, the temperature is divided into three segments: low, suitable, and high), format: percentage, color. lineStyle: { color: [ [0.3, '#228b22'], [0.7, '#48b'], [1, '#ff4500'] ], width: 20 } }, splitLine:{ length :20 } } ] }; myChart_T.setOption(option_T); var myChart_H = echarts.init(document.getElementById("H_chart")); var option_H = { series : [ { name:'humidity', type:'gauge', detail : {formatter:'{value}%'}, data:[{value: 50, name: '湿度'}], axisLine:{ show: true, lineStyle: { color: [ [0.4, '#228b22'], [0.75, '#48b'], [1, '#ff4500'] ], width: 20 } }, splitLine:{ length :20 } } ] }; myChart_H.setOption(option_H); var myChart_CO2 = echarts.init(document.getElementById("CO2_chart")); var option_CO2 = { series : [ { name:'CO2', type:'gauge', detail : {formatter:'{value}%'}, data:[{value: 0.05, name: 'CO2'}], min: 0, max:0.15, splitNumber:5, axisLine:{ show: true, lineStyle: { color: [ [0.33, '#228b22'], [0.66, '#48b'], [1, '#ff4500'] ], width: 20 } }, splitLine:{ length :20 } } ] }; myChart_CO2.setOption(option_CO2); var myChart_NH3 = echarts.init(document.getElementById("NH3_chart")); var option_NH3 = { series : [ { name:'NH3', type:'gauge', detail : {formatter:'{value}%'}, data:[{value: 0.05, name: 'NH3'}], min: 0, max:0.005, splitNumber:10, axisLine:{ show: true, lineStyle: { color: [ [0.9, '#48b'], [1, '#ff4500'] ], width: 20 } }, splitLine:{ length :20 } } ] }; myChart_NH3.setOption(option_NH3); var count = 0; var date = []; // line chart---------------------------------------------- ------------------------------------ var myChart_T_line = echarts.init(document.getElementById("T_chart_line")); var data_T = []; var option_T_line = { xAxis: A type: 'category', boundaryGap: false, date: date }, yAxis: { min: 0, max:50, type: 'value' }, series: [ { name:'meanRate', type:'line', smooth:true, areaStyle: { normal: {} }, data: data_T } ] }; myChart_T_line.setOption(option_T_line); myChart_T_line.hideLoading(); var myChart_H_line = echarts.init(document.getElementById("H_chart_line")); var data_H = []; var option_H_line = { xAxis: A type: 'category', boundaryGap: false, date: date }, yAxis: { min: 0, max:100, type: 'value' }, series: [ { name:'meanRate', type:'line', smooth:true, areaStyle: { normal: {} }, data: data_H } ] }; myChart_H_line.setOption(option_H_line); myChart_H_line.hideLoading(); var myChart_CO2_line = echarts.init(document.getElementById("CO2_chart_line")); var data_CO2 = []; var option_CO2_line = { xAxis: A type: 'category', boundaryGap: false, date: date }, yAxis: { min: 0, max:0.20, type: 'value' }, series: [ { name:'meanRate', type:'line', smooth:true, areaStyle: { normal: {} }, data: data_CO2 } ] }; myChart_CO2_line.setOption(option_CO2_line); myChart_CO2_line.hideLoading(); var myChart_NH3_line = echarts.init(document.getElementById("NH3_chart_line")); var data_NH3 = []; var option_NH3_line = { xAxis: A type: 'category', boundaryGap: false, date: date }, yAxis: { min: 0, max:0.007, type: 'value' }, series: [ { name:'meanRate', type:'line', smooth:true, areaStyle: { normal: {} }, data: data_NH3 } ] }; myChart_NH3_line.setOption(option_NH3_line); myChart_NH3_line.hideLoading(); // webSocket------------------------------------------------------------------------ var eb = new EventBus("/eventbus/"); eb.onopen = function() { eb.registerHandler("chartServer.to.client", function(err, msg) { $('#T').text(msg.body.T + "\n"); $('#H').text(msg.body.H + "\n"); $('#CO2').text(msg.body.CO2 + "\n"); $('#NH3').text(msg.body.NH3 + "\n"); option_T.series[0].data[0].value = msg.body.T; myChart_T.setOption(option_T, true); option_H.series[0].data[0].value = msg.body.H; myChart_H.setOption(option_H, true); option_CO2.series[0].data[0].value = msg.body.CO2; myChart_CO2.setOption(option_CO2, true); option_NH3.series[0].data[0].value = msg.body.NH3; myChart_NH3.setOption(option_NH3, true); date.push(msg.body.time); count = count + 1; if(count > 100){ date.shift(); data_T.shift(); data_H.shift(); data_CO2.shift(); data_NH3.shift(); } data_T.push(msg.body.T); data_H.push(msg.body.H); data_CO2.push(msg.body.CO2); data_NH3.push(msg.body.NH3); myChart_T_line.setOption(option_T_line, true); myChart_H_line.setOption(option_H_line, true); myChart_CO2_line.setOption(option_CO2_line, true); myChart_NH3_line.setOption(option_NH3_line, true); }); }; </script> </body> </html>
The background code is as follows:
package com.wof.realtimesca.chart; import java.text.DecimalFormat; import java.text.SimpleDateFormat; import java.util.GregorianCalendar; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import io.vertx.core.json.JsonObject; import io.vertx.ext.web.handler.sockjs.BridgeEventType; import io.vertx.ext.web.handler.sockjs.BridgeOptions; import io.vertx.ext.web.handler.sockjs.PermittedOptions; import io.vertx.rxjava.core.AbstractVerticle; import io.vertx.rxjava.core.eventbus.EventBus; import io.vertx.rxjava.core.eventbus.Message; import io.vertx.rxjava.core.eventbus.MessageConsumer; import io.vertx.rxjava.ext.web.Router; import io.vertx.rxjava.ext.web.handler.StaticHandler; import io.vertx.rxjava.ext.web.handler.sockjs.SockJSHandler; import rx.Observable; import rx.Subscription; public class ChartVerticle extends AbstractVerticle { private static final Logger logger = LoggerFactory.getLogger(ChartVerticle.class); public void start() throws Exception { // router Router router = Router.router(vertx); // event bus BridgeOptions bridgeOptions = new BridgeOptions() .addInboundPermitted(new PermittedOptions().setAddress("tcpServer.to.chartServer")) .addOutboundPermitted(new PermittedOptions().setAddress("chartServer.to.client")); SockJSHandler sockJSHandler = SockJSHandler.create(vertx).bridge(bridgeOptions, event -> { if (event.type() == BridgeEventType.SOCKET_CREATED) { logger.info("created from socket."); } else if (event.type() == BridgeEventType.SOCKET_CLOSED){ logger.info("a socket closed."); } event.complete(true); }); sockJSHandler.socketHandler(sockJSSocket -> { sockJSSocket.exceptionHandler(handler -> { logger.error(handler.getCause().getMessage()); }); }); router.route("/eventbus/*").handler(sockJSHandler); router.route().handler(StaticHandler.create());//This static resource handler without any prefix must be placed behind sockJSHandler; otherwise, jsSocket cannot communicate with the background. EventBus eventBus = vertx.eventBus(); MessageConsumer<String> consumer = eventBus.consumer("tcpServer.to.chartServer"); Observable<Message<String>> observable = consumer.toObservable(); //message结构(json): {"type":"rec","address":"char.to.client.push","body":"welcome!"} Subscription sub = observable.subscribe(msg -> { eventBus.publish("chartServer.to.client", msg.body()); }); // http server vertx.createHttpServer().requestHandler(router::accept).listen(8090); // Simulate the sensor to send data to the server SimpleDateFormat sdf = new SimpleDateFormat("HH:mm:ss"); vertx.setPeriodic (500, t -> { JsonObject jsonData = new JsonObject(); String date = sdf.format(GregorianCalendar.getInstance().getTime()); jsonData.put("time", date); String data = getDataValue(28, 30, 0, 1);//温度 jsonData.put("T", data); data = getDataValue(70, 75, 0, 1);//湿度 jsonData.put("H", data); data = getDataValue(5, 10, 2, 2);//CO2 0.05—0.1% jsonData.put("CO2", data); data = getDataValue(1, 4, 3, 4);//NH3 < 0.004﹪ jsonData.put("NH3", data); vertx.eventBus().publish("tcpServer.to.chartServer", jsonData); }); } /** * Generate random decimals * * @param min * @param max * @param count number of decimal places * @param zeroCount the number of decimal places to keep * @return */ private String getDataValue(int min, int max, int count, int zeroCount){ double d = (Math.random() * (max - min + 1) + min); for(int i = 1; i <= count ; i++){ d /= 10; } String zero = "0."; if(zeroCount < 1) zero = "0.0"; else { for(int i = 1; i <= zeroCount; i++){ zero += "0"; } } DecimalFormat Dformat = new DecimalFormat(zero); String val = Dformat.format(d); return val; } }
Effect picture: