本文旨在用vert.x与echarts开发一个实时环境监控demo。
vert.x参看http://vertx.io
ehcars参看http://echarts.baidu.com/index.html
整个业务流程比较简单,就是页面通过sockJs与后端的web服务器通讯,然后后端服务器实时推送数据到页面。
页面代码如下:
<!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>环境实时监控:</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>
// 仪表盘-------------------------------------------------------------------
var myChart_T = echarts.init(document.getElementById("T_chart"));
var option_T = {
series : [
{
name:'温度',
type:'gauge',
detail : {formatter:'{value}℃'},
data:[{value: 50, name: '温度'}],
min:0,
max:50,
axisLine:{
show: true,
//给每一段指定颜色(本例中温度分低,适宜,高三段),格式:百分比,颜色。
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:'湿度',
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 = [];
// 折线图----------------------------------------------------------------------------------
var myChart_T_line = echarts.init(document.getElementById("T_chart_line"));
var data_T = [];
var option_T_line = {
xAxis: {
type: 'category',
boundaryGap: false,
data: 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: {
type: 'category',
boundaryGap: false,
data: 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: {
type: 'category',
boundaryGap: false,
data: 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: {
type: 'category',
boundaryGap: false,
data: 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>
后台代码如下:
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(vertx);
// 事件总线
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("a socket creatae.");
} 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());//这种没有任何前缀的静态资源handler一定要放到sockJSHandler后面;否则,jsSocket无法与后台通讯。
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);
// 模拟传感器发送数据到服务器
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);
});
}
/**
* 产生随机小数
*
* @param min
* @param max
* @param count 小数位数
* @param zeroCount 需要保留的小数位数
* @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;
}
}
效果图: