繼續(xù)上一篇《HTML5 WebSocket 技術(shù)介紹》的內(nèi)容,本篇將以示例說明WebSocket的使用,這個示例同時結(jié)合了TWaver HTML5的使用,場景如下:后臺提供拓?fù)鋽?shù)據(jù),并以JSON格式通過WebSocket推送到各個客戶端,客戶端獲取到拓?fù)湫畔⒑螅ㄟ^TWaver HTML5的Network組件呈現(xiàn)于界面,客戶端可以操作網(wǎng)元,操作結(jié)果通過WebSocket提交到后臺,后臺服務(wù)器更新并通知所有的客戶端刷新界面,此外后臺服務(wù)器端還會不斷產(chǎn)生告警,并推送到各個客戶端更新界面。
jetty :http://www.eclipse.org/jetty/
twaver html5

WebSocketServlet - WebSocket服務(wù)類
WebSocket - 對應(yīng)一個WebSocket客戶端
WebSocket.Conllection - 代表一個WebSocket連接
本例中將定義一個AlarmServlet類,繼承于WebSocketServlet,并實現(xiàn)doWebSocketConnect方法,返回一個AlarmWebSocket實例,代表一個客戶端。
org.eclipse.jetty.websocket.WebSocket.OnTextMessage的三個方法:onOpen/onMessage/onClose,這三個方法分別在連接建立,收到客戶端消息,關(guān)閉連接時回調(diào),如果需要向客戶端發(fā)送消息,可以通過
Connection#sendMessage(...)方法,消息統(tǒng)一使用JSON格式,下面是具體實現(xiàn):
本例需要用到三種業(yè)務(wù)類型,節(jié)點,連線和告警,后臺分別提供了實現(xiàn)類,并定義了名稱,位置,線寬等屬性,此外還提供了導(dǎo)出json數(shù)據(jù)的功能。
后臺代碼如下:
前臺代碼:
客戶端接收到消息后,需要對應(yīng)的處理,增加對"alarm.clear"和"alarm.add"的處理,這樣告警就能實時更新了
前臺代碼:
后臺代碼
結(jié)構(gòu):

大體結(jié)構(gòu)
準(zhǔn)備
需要用到j(luò)etty和twaver html5,可自行下載:jetty :http://www.eclipse.org/jetty/
twaver html5
jetty目錄結(jié)構(gòu)
jetty下載解壓后是下面的結(jié)構(gòu),運行start.jar(java -jar start.jar)啟動jetty服務(wù)器,web項目可以發(fā)布在/webapps目錄中,比如本例目錄/webapps/alarm/
后臺部分
后臺使用jetty,其使用風(fēng)格延續(xù)servlet的api,可以按Serlvet的使用和部署方式來使用,本例中主要用到三個類WebSocketServlet
全名為org.eclipse.jetty.websocket.WebSocketServlet,用于提供websocket服務(wù),繼承于HttpServlet,增加了方法public WebSocket doWebSocketConnect(HttpServletRequest request, String protocol),在客戶端第一次請求websocket連接時會調(diào)用該方法,如果允許建立連接,則返回一個WebSocket實例對象,否則返回null。本例中將定義一個AlarmServlet類,繼承于WebSocketServlet,并實現(xiàn)doWebSocketConnect方法,返回一個AlarmWebSocket實例,代表一個客戶端。
AlarmServlet
AlarmWebSocket中有個clients屬性,用于維持一個客戶端(AlarmWebSocket)列表,當(dāng)與客戶端建立連接時,會將客戶端對應(yīng)的AlarmWebSocket實例添加到這個列表,當(dāng)客戶端關(guān)閉時,則從這個列表中刪除。 1 public class AlarmServlet extends org.eclipse.jetty.websocket.WebSocketServlet {
2 private final Set<AlarmWebSocket> clients;//保存客戶端列表
3
4 public AlarmServlet() {
5 initDatas();//初始化數(shù)據(jù)
6 }
7
8 @Override
9 public WebSocket doWebSocketConnect(HttpServletRequest request, String protocol) {
10 return new AlarmWebSocket();
11 }
12 //
13 }
2 private final Set<AlarmWebSocket> clients;//保存客戶端列表
3
4 public AlarmServlet() {
5 initDatas();//初始化數(shù)據(jù)
6 }
7
8 @Override
9 public WebSocket doWebSocketConnect(HttpServletRequest request, String protocol) {
10 return new AlarmWebSocket();
11 }
12 //

13 }
AlarmWebSocket
來看看AlarmWebSocket的實現(xiàn),這里定義的是一個內(nèi)部類,實現(xiàn)了接口org.eclipse.jetty.websocket.WebSocket.OnTextMessage的三個方法:onOpen/onMessage/onClose,這三個方法分別在連接建立,收到客戶端消息,關(guān)閉連接時回調(diào),如果需要向客戶端發(fā)送消息,可以通過
Connection#sendMessage(...)方法,消息統(tǒng)一使用JSON格式,下面是具體實現(xiàn):
1 class AlarmWebSocket implements org.eclipse.jetty.websocket.WebSocket.OnTextMessage
2 {
3 WebSocket.Connection connection;
4 @Override
5 public void onOpen(Connection connect) {
6 this.connection = connect;
7 clients.add(this);
8 sendMessage(this, "reload", loadDatas());
9 }
10 @Override
11 public void onClose(int code, String message) {
12 clients.remove(this);
13 }
14 @Override
15 public void onMessage(String message) {
16 Object json = JSON.parse(message);
17 if(!(json instanceof Map)){
18 return;
19 }
20 //解析消息,jetty中json數(shù)據(jù)將被解析成map對象
21 Map map = (Map)json;
22 //通過消息中的信息,更新后臺數(shù)據(jù)模型
23
24 //處理消息,通知到其他各個客戶端
25 for(AlarmWebSocket client : clients){
26 if(this.equals(client)){
27 continue;
28 }
29 sendMessage(client, null, message);
30 }
31 }
32 }
33 private void sendMessage(AlarmWebSocket client, String action, String message){
34 try {
35 if(message == null || message.isEmpty()){
36 message = "\"\"";
37 }
38 if(action != null){
39 message = "{\"action\":\"" + action + "\", \"data\":" + message + "}";
40 }
41 client.connection.sendMessage(message);
42 } catch (IOException e) {
43 e.printStackTrace();
44 }
45 }
2 {
3 WebSocket.Connection connection;
4 @Override
5 public void onOpen(Connection connect) {
6 this.connection = connect;
7 clients.add(this);
8 sendMessage(this, "reload", loadDatas());
9 }
10 @Override
11 public void onClose(int code, String message) {
12 clients.remove(this);
13 }
14 @Override
15 public void onMessage(String message) {
16 Object json = JSON.parse(message);
17 if(!(json instanceof Map)){
18 return;
19 }
20 //解析消息,jetty中json數(shù)據(jù)將被解析成map對象
21 Map map = (Map)json;
22 //通過消息中的信息,更新后臺數(shù)據(jù)模型
23

24 //處理消息,通知到其他各個客戶端
25 for(AlarmWebSocket client : clients){
26 if(this.equals(client)){
27 continue;
28 }
29 sendMessage(client, null, message);
30 }
31 }
32 }
33 private void sendMessage(AlarmWebSocket client, String action, String message){
34 try {
35 if(message == null || message.isEmpty()){
36 message = "\"\"";
37 }
38 if(action != null){
39 message = "{\"action\":\"" + action + "\", \"data\":" + message + "}";
40 }
41 client.connection.sendMessage(message);
42 } catch (IOException e) {
43 e.printStackTrace();
44 }
45 }
后臺配置
后臺配置如serlvet相同,這里設(shè)置的url名稱為/alarmServer 1 <?xml version="1.0" encoding="UTF-8"?>
2 <web-app
3 xmlns="http://java.sun.com/xml/ns/javaee"
4 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
5 xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
6 metadata-complete="false"
7 version="3.0">
8 <servlet>
9 <servlet-name>alarmServlet</servlet-name>
10 <servlet-class>web.AlarmServlet</servlet-class>
11 <load-on-startup>1</load-on-startup>
12 </servlet>
13
14 <servlet-mapping>
15 <servlet-name>alarmServlet</servlet-name>
16 <url-pattern>/alarmServer</url-pattern>
17 </servlet-mapping>
18 </web-app>
2 <web-app
3 xmlns="http://java.sun.com/xml/ns/javaee"
4 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
5 xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
6 metadata-complete="false"
7 version="3.0">
8 <servlet>
9 <servlet-name>alarmServlet</servlet-name>
10 <servlet-class>web.AlarmServlet</servlet-class>
11 <load-on-startup>1</load-on-startup>
12 </servlet>
13
14 <servlet-mapping>
15 <servlet-name>alarmServlet</servlet-name>
16 <url-pattern>/alarmServer</url-pattern>
17 </servlet-mapping>
18 </web-app>
前臺部分
看看前臺的大體結(jié)構(gòu),創(chuàng)建websocket連接,監(jiān)聽相關(guān)事件,比如onmessage事件,可以收到后臺發(fā)送的信息(JSON格式),解析后更新到界面,詳細(xì)的處理函數(shù)將稍后介紹 1 function init(){
2 window.WebSocket = window.WebSocket || window.MozWebSocket;
3 if (!window.WebSocket){
4 alert("WebSocket not supported by this browser");
5 return;
6 }
7 var websocket = new WebSocket("ws://127.0.0.1:8080/alarm/alarmServer");
8 websocket.onopen = onopen;
9 websocket.onclose = onclose;
10 websocket.onmessage = onmessage;
11
12 }
13 function onmessage(evt){
14 var data = evt.data;
15 if(!data){
16 return;
17 }
18 data = stringToJson(data);
19 if(!data){
20 return;
21 }
22
23 }
24 function jsonToString(json){
25 return JSON.stringify(json);
26 }
27 function stringToJson(str){
28 try{
29 str = str.replace(/\'/g, "\"");
30 return JSON.parse(str);
31 }catch(error){
32 console.log(error);
33 }
34 }
2 window.WebSocket = window.WebSocket || window.MozWebSocket;
3 if (!window.WebSocket){
4 alert("WebSocket not supported by this browser");
5 return;
6 }
7 var websocket = new WebSocket("ws://127.0.0.1:8080/alarm/alarmServer");
8 websocket.onopen = onopen;
9 websocket.onclose = onclose;
10 websocket.onmessage = onmessage;
11

12 }
13 function onmessage(evt){
14 var data = evt.data;
15 if(!data){
16 return;
17 }
18 data = stringToJson(data);
19 if(!data){
20 return;
21 }
22

23 }
24 function jsonToString(json){
25 return JSON.stringify(json);
26 }
27 function stringToJson(str){
28 try{
29 str = str.replace(/\'/g, "\"");
30 return JSON.parse(str);
31 }catch(error){
32 console.log(error);
33 }
34 }
WebSocket前后臺流程
? 

業(yè)務(wù)實現(xiàn)
數(shù)據(jù)模型
本例需要用到三種業(yè)務(wù)類型,節(jié)點,連線和告警,后臺分別提供了實現(xiàn)類,并定義了名稱,位置,線寬等屬性,此外還提供了導(dǎo)出json數(shù)據(jù)的功能。 1 interface IJSON{
2 String toJSON();
3 }
4 class Data{
5 String name;
6 public Data(String name){
7 this.name = name;
8 }
9 }
10 class Node extends Data implements IJSON{
11 public Node(String name, double x, double y){
12 super(name);
13 this.x = x;
14 this.y = y;
15 }
16 double x, y;
17 public String toJSON(){
18 return "{\"name\":\"" + name + "\", \"x\":\"" + x + "\",\"y\":\"" + y + "\"}";
19 }
20 }
21 class Link extends Data implements IJSON{
22 public Link(String name, String from, String to, int width){
23 super(name);
24 this.from =from;
25 this.to = to;
26 this.width = width;
27 }
28 String from;
29 String to;
30 int width = 2;
31 public String toJSON(){
32 return "{\"name\":\"" + name + "\", \"from\":\"" + from + "\", \"to\":\"" + to + "\", \"width\":\"" + width + "\"}";
33 }
34 }
35 class Alarm implements IJSON{
36 public Alarm(String elementName, String alarmSeverity){
37 this.alarmSeverity = alarmSeverity;
38 this.elementName = elementName;
39 }
40 String alarmSeverity;
41 String elementName;
42 @Override
43 public String toJSON() {
44 return "{\"elementName\": \"" + elementName + "\", \"alarmSeverity\": \"" + alarmSeverity + "\"}";
45 }
46 }
后臺維持三個數(shù)據(jù)集合,分別存放節(jié)點,連線和告警信息,此外elementMap以節(jié)點名稱為鍵,便于節(jié)點的快速查找 2 String toJSON();
3 }
4 class Data{
5 String name;
6 public Data(String name){
7 this.name = name;
8 }
9 }
10 class Node extends Data implements IJSON{
11 public Node(String name, double x, double y){
12 super(name);
13 this.x = x;
14 this.y = y;
15 }
16 double x, y;
17 public String toJSON(){
18 return "{\"name\":\"" + name + "\", \"x\":\"" + x + "\",\"y\":\"" + y + "\"}";
19 }
20 }
21 class Link extends Data implements IJSON{
22 public Link(String name, String from, String to, int width){
23 super(name);
24 this.from =from;
25 this.to = to;
26 this.width = width;
27 }
28 String from;
29 String to;
30 int width = 2;
31 public String toJSON(){
32 return "{\"name\":\"" + name + "\", \"from\":\"" + from + "\", \"to\":\"" + to + "\", \"width\":\"" + width + "\"}";
33 }
34 }
35 class Alarm implements IJSON{
36 public Alarm(String elementName, String alarmSeverity){
37 this.alarmSeverity = alarmSeverity;
38 this.elementName = elementName;
39 }
40 String alarmSeverity;
41 String elementName;
42 @Override
43 public String toJSON() {
44 return "{\"elementName\": \"" + elementName + "\", \"alarmSeverity\": \"" + alarmSeverity + "\"}";
45 }
46 }
1 Map<String, Data> elementMap = new HashMap<String, AlarmServlet.Data>();
2 List<Node> nodes = new ArrayList<AlarmServlet.Node>();
3 List<Link> links = new ArrayList<AlarmServlet.Link>();
4 List<Alarm> alarms = new ArrayList<AlarmServlet.Alarm>();
2 List<Node> nodes = new ArrayList<AlarmServlet.Node>();
3 List<Link> links = new ArrayList<AlarmServlet.Link>();
4 List<Alarm> alarms = new ArrayList<AlarmServlet.Alarm>();
初始化數(shù)據(jù)
在servlet構(gòu)造中,我們添加了些模擬數(shù)據(jù),在客戶端建立連接時(AlarmWebSocket#onOpen(Connection connection)),后臺將節(jié)點連線和告警信息以JSON格式發(fā)送到前臺(sendMessage(this, "reload", loadDatas());) 1 public AlarmServlet() {
2 initDatas();
3
4 }
5
6 public void initDatas() {
7 int i = 0;
8 double cx = 350, cy = 230, a = 250, b = 180;
9 nodes.add(new Node("center", cx, cy));
10 double angle = 0, perAngle = 2 * Math.PI/10;
11 while(i++ < 10){
12 Node node = new Node("node_" + i, cx + a * Math.cos(angle), cy + b * Math.sin(angle));
13 elementMap.put(node.name, node);
14 nodes.add(node);
15 angle += perAngle;
16 }
17 i = 0;
18 while(i++ < 10){
19 Link link = new Link("link_" + i, "center", "node_" + i, 1 + random.nextInt(10));
20 elementMap.put(link.name, link);
21 links.add(link);
22 }
23 }
24
25 private String loadDatas(){
26 StringBuffer result = new StringBuffer();
27 result.append("{\"nodes\":");
28 listToJSON(nodes, result);
29 result.append(", \"links\":");
30 listToJSON(links, result);
31 result.append(", \"alarms\":");
32 listToJSON(alarms, result);
33 result.append("}");
34 return result.toString();
35 }
36
37 class AlarmWebSocket implements org.eclipse.jetty.websocket.WebSocket.OnTextMessage
38 {
39
40 @Override
41 public void onOpen(Connection connect) {
42 this.connection = connect;
43 clients.add(this);
44 sendMessage(this, "reload", loadDatas());
45 }
46
47 }
2 initDatas();
3

4 }
5
6 public void initDatas() {
7 int i = 0;
8 double cx = 350, cy = 230, a = 250, b = 180;
9 nodes.add(new Node("center", cx, cy));
10 double angle = 0, perAngle = 2 * Math.PI/10;
11 while(i++ < 10){
12 Node node = new Node("node_" + i, cx + a * Math.cos(angle), cy + b * Math.sin(angle));
13 elementMap.put(node.name, node);
14 nodes.add(node);
15 angle += perAngle;
16 }
17 i = 0;
18 while(i++ < 10){
19 Link link = new Link("link_" + i, "center", "node_" + i, 1 + random.nextInt(10));
20 elementMap.put(link.name, link);
21 links.add(link);
22 }
23 }
24
25 private String loadDatas(){
26 StringBuffer result = new StringBuffer();
27 result.append("{\"nodes\":");
28 listToJSON(nodes, result);
29 result.append(", \"links\":");
30 listToJSON(links, result);
31 result.append(", \"alarms\":");
32 listToJSON(alarms, result);
33 result.append("}");
34 return result.toString();
35 }
36
37 class AlarmWebSocket implements org.eclipse.jetty.websocket.WebSocket.OnTextMessage
38 {
39

40 @Override
41 public void onOpen(Connection connect) {
42 this.connection = connect;
43 clients.add(this);
44 sendMessage(this, "reload", loadDatas());
45 }
46

47 }
初始數(shù)據(jù)前臺展示
初始數(shù)據(jù)通過后臺的sendMessage(...)方法推送到客戶端,客戶端可以在onmessage回調(diào)函數(shù)中收到,本例我們使用twaver html5組件來展示這些信息。TWaver組件的使用流程一如既往,先作數(shù)據(jù)轉(zhuǎn)換,將JSON數(shù)據(jù)轉(zhuǎn)換成TWaver的網(wǎng)元類型,然后填充到ElementBox數(shù)據(jù)容器,最后關(guān)聯(lián)上Network拓?fù)鋱D組件,代碼如下: 1 <!DOCTYPE html>
2 <html>
3 <head>
4 <title>TWaver HTML5 Demo - Alarm</title>
5 <script type="text/javascript" src="./twaver.js"></script>
6 <script type="text/javascript">
7 var box, network, nameFinder;
8 function init(){
9 network = new twaver.network.Network();
10 box = network.getElementBox();
11 nameFinder = new twaver.QuickFinder(box, "name");
12
13 var networkDom = network.getView();
14 networkDom.style.width = "100%";
15 networkDom.style.height = "100%";
16 document.body.appendChild(networkDom);
17
18 window.WebSocket = window.WebSocket || window.MozWebSocket;
19 if (!window.WebSocket){
20 alert("WebSocket not supported by this browser");
21 return;
22 }
23 var websocket = new WebSocket("ws://127.0.0.1:8080/alarm/alarmServer");
24
25 websocket.onmessage = onmessage;
26
27 }
28
29 function onmessage(evt){
30 var data = evt.data;
31 if(!data){
32 return;
33 }
34 data = stringToJson(data);
35 if(!data){
36 return;
37 }
38 var action = data.action;
39 if(!action){
40 return;
41 }
42 if(action == "alarm.clear"){
43 box.getAlarmBox().clear();
44 return;
45 }
46 data = data.data;
47 if(!data){
48 return;
49 }
50 if(action == "reload"){
51 reloadDatas(data);
52 return;
53 }
54 if(action == "alarm.add"){
55 newAlarm(data)
56 return;
57 }
58 if(action == "node.move"){
59 modeMove(data);
60 return;
61 }
62 }
63
64 function reloadDatas(datas){
65 box.clear();
66 var nodes = datas.nodes;
67 var links = datas.links;
68 var alarms = datas.alarms;
69
70 for(var i=0,l=nodes.length; i < l; i++){
71 var data = nodes[i];
72 var node = new twaver.Node();
73 node.setName(data.name);
74 node.setCenterLocation(parseFloat(data.x), parseFloat(data.y));
75 box.add(node);
76 }
77
78 for(var i=0,l=links.length; i < l; i++){
79 var data = links[i];
80 var from = findFirst(data.from);
81 var to = findFirst(data.to);
82 var link = new twaver.Link(from, to);
83 link.setName(data.name);
84 link.setStyle("link.width", parseInt(data.width));
85 box.add(link);
86 }
87
88 var alarmBox = box.getAlarmBox();
89 for(var i=0,l=alarms.length; i < l; i++){
90 newAlarm(alarms[i]);
91 }
92 }
93 function findFirst(name){
94 return nameFinder.findFirst(name);
95 }
96 function newAlarm(data){
97 var element = findFirst(data.elementName);
98 var alarmSeverity = twaver.AlarmSeverity.getByName(data.alarmSeverity);
99 if(!element || !alarmSeverity){
100 return;
101 }
102 addAlarm(element.getId(), alarmSeverity, box.getAlarmBox());
103 }
104 function addAlarm(elementID,alarmSeverity,alarmBox){
105 var alarm = new twaver.Alarm(null, elementID,alarmSeverity);
106 alarmBox.add(alarm);
107 }
108 function modeMove(datas){
109 for(var i=0,l=datas.length; i<l; i++){
110 var data = datas[i];
111 var node = findFirst(data.name);
112 if(node){
113 var x = parseFloat(data.x);
114 var y = parseFloat(data.y);
115 node.setCenterLocation(x, y);
116 }
117 }
118 }
119
120 </script>
121 </head>
122 <body onload="init()" style="margin:0;"></body>
123 </html>
2 <html>
3 <head>
4 <title>TWaver HTML5 Demo - Alarm</title>
5 <script type="text/javascript" src="./twaver.js"></script>
6 <script type="text/javascript">
7 var box, network, nameFinder;
8 function init(){
9 network = new twaver.network.Network();
10 box = network.getElementBox();
11 nameFinder = new twaver.QuickFinder(box, "name");
12
13 var networkDom = network.getView();
14 networkDom.style.width = "100%";
15 networkDom.style.height = "100%";
16 document.body.appendChild(networkDom);
17
18 window.WebSocket = window.WebSocket || window.MozWebSocket;
19 if (!window.WebSocket){
20 alert("WebSocket not supported by this browser");
21 return;
22 }
23 var websocket = new WebSocket("ws://127.0.0.1:8080/alarm/alarmServer");
24

25 websocket.onmessage = onmessage;
26
27 }
28

29 function onmessage(evt){
30 var data = evt.data;
31 if(!data){
32 return;
33 }
34 data = stringToJson(data);
35 if(!data){
36 return;
37 }
38 var action = data.action;
39 if(!action){
40 return;
41 }
42 if(action == "alarm.clear"){
43 box.getAlarmBox().clear();
44 return;
45 }
46 data = data.data;
47 if(!data){
48 return;
49 }
50 if(action == "reload"){
51 reloadDatas(data);
52 return;
53 }
54 if(action == "alarm.add"){
55 newAlarm(data)
56 return;
57 }
58 if(action == "node.move"){
59 modeMove(data);
60 return;
61 }
62 }
63
64 function reloadDatas(datas){
65 box.clear();
66 var nodes = datas.nodes;
67 var links = datas.links;
68 var alarms = datas.alarms;
69
70 for(var i=0,l=nodes.length; i < l; i++){
71 var data = nodes[i];
72 var node = new twaver.Node();
73 node.setName(data.name);
74 node.setCenterLocation(parseFloat(data.x), parseFloat(data.y));
75 box.add(node);
76 }
77
78 for(var i=0,l=links.length; i < l; i++){
79 var data = links[i];
80 var from = findFirst(data.from);
81 var to = findFirst(data.to);
82 var link = new twaver.Link(from, to);
83 link.setName(data.name);
84 link.setStyle("link.width", parseInt(data.width));
85 box.add(link);
86 }
87
88 var alarmBox = box.getAlarmBox();
89 for(var i=0,l=alarms.length; i < l; i++){
90 newAlarm(alarms[i]);
91 }
92 }
93 function findFirst(name){
94 return nameFinder.findFirst(name);
95 }
96 function newAlarm(data){
97 var element = findFirst(data.elementName);
98 var alarmSeverity = twaver.AlarmSeverity.getByName(data.alarmSeverity);
99 if(!element || !alarmSeverity){
100 return;
101 }
102 addAlarm(element.getId(), alarmSeverity, box.getAlarmBox());
103 }
104 function addAlarm(elementID,alarmSeverity,alarmBox){
105 var alarm = new twaver.Alarm(null, elementID,alarmSeverity);
106 alarmBox.add(alarm);
107 }
108 function modeMove(datas){
109 for(var i=0,l=datas.length; i<l; i++){
110 var data = datas[i];
111 var node = findFirst(data.name);
112 if(node){
113 var x = parseFloat(data.x);
114 var y = parseFloat(data.y);
115 node.setCenterLocation(x, y);
116 }
117 }
118 }
119

120 </script>
121 </head>
122 <body onload="init()" style="margin:0;"></body>
123 </html>
界面效果
?

后臺推送告警,前臺實時更新
增加后臺推送告警的代碼,這里我們在后臺起了一個定時器,每隔兩秒產(chǎn)生一條隨機告警,或者清除所有告警,并將信息推送給所有的客戶端后臺代碼如下:
1 public AlarmServlet() {
2
3 Timer timer = new Timer();
4 timer.schedule(new TimerTask() {
5 @Override
6 public void run() {
7 if(random.nextInt(10) == 9){
8 alarms.clear();
9 sendMessage ("alarm.clear", "");
10 return;
11 }
12 sendMessage("alarm.add", randomAlarm());
13 }
14 }, 0, 2000);
15 }
16 public void sendMessage(String action, String message) {
17 for(AlarmWebSocket client : clients){
18 sendMessage(client, action, message);
19 }
20 }
21 private Random random = new Random();
22 private Data getRandomElement(){
23 if(random.nextBoolean()){
24 return nodes.get(random.nextInt(nodes.size()));
25 }
26 return links.get(random.nextInt(links.size()));
27 }
28 String[] alarmSeverities = new String[]{"Critical", "Major", "Minor", "Warning", "Indeterminate"};
29 private String randomAlarm(){
30 Alarm alarm = new Alarm(getRandomElement().name, alarmSeverities[random.nextInt(alarmSeverities.length)]);
31 alarms.add(alarm);
32 return alarm.toJSON();
33 }
34
2

3 Timer timer = new Timer();
4 timer.schedule(new TimerTask() {
5 @Override
6 public void run() {
7 if(random.nextInt(10) == 9){
8 alarms.clear();
9 sendMessage ("alarm.clear", "");
10 return;
11 }
12 sendMessage("alarm.add", randomAlarm());
13 }
14 }, 0, 2000);
15 }
16 public void sendMessage(String action, String message) {
17 for(AlarmWebSocket client : clients){
18 sendMessage(client, action, message);
19 }
20 }
21 private Random random = new Random();
22 private Data getRandomElement(){
23 if(random.nextBoolean()){
24 return nodes.get(random.nextInt(nodes.size()));
25 }
26 return links.get(random.nextInt(links.size()));
27 }
28 String[] alarmSeverities = new String[]{"Critical", "Major", "Minor", "Warning", "Indeterminate"};
29 private String randomAlarm(){
30 Alarm alarm = new Alarm(getRandomElement().name, alarmSeverities[random.nextInt(alarmSeverities.length)]);
31 alarms.add(alarm);
32 return alarm.toJSON();
33 }
34
前臺代碼:
客戶端接收到消息后,需要對應(yīng)的處理,增加對"alarm.clear"和"alarm.add"的處理,這樣告警就能實時更新了
1 function onmessage(evt){
2
3 if(action == "alarm.clear"){
4 box.getAlarmBox().clear();
5 return;
6 }
7 data = data.data;
8 if(!data){
9 return;
10 }
11
12 if(action == "alarm.add"){
13 newAlarm(data)
14 return;
15 }
16
17 }
2

3 if(action == "alarm.clear"){
4 box.getAlarmBox().clear();
5 return;
6 }
7 data = data.data;
8 if(!data){
9 return;
10 }
11

12 if(action == "alarm.add"){
13 newAlarm(data)
14 return;
15 }
16

17 }
客戶端拖拽節(jié)點,同步到其他客戶端
最后增加拖拽同步,監(jiān)聽network網(wǎng)元拖拽監(jiān)聽,在網(wǎng)元拖拽放手后,將節(jié)點位置信息發(fā)送給后臺前臺代碼:
1 network.addInteractionListener(function(evt){
2 var moveEnd = "MoveEnd";
3 if(evt.kind.substr(-moveEnd.length) == moveEnd){
4 var nodes = [];
5 var selection = box.getSelectionModel().getSelection();
6 selection.forEach(function(element){
7 if(element instanceof twaver.Node){
8 var xy = element.getCenterLocation();
9 nodes.push({name: element.getName(), x: xy.x, y: xy.y});
10 }
11 });
12 websocket.send(jsonToString({action: "node.move", data: nodes}));
13 }
14 });
后臺接收到節(jié)點位置信息后,首先更新后臺數(shù)據(jù)(節(jié)點位置),然后將消息轉(zhuǎn)發(fā)給其他客戶端,這樣各個客戶端就實現(xiàn)了同步操作2 var moveEnd = "MoveEnd";
3 if(evt.kind.substr(-moveEnd.length) == moveEnd){
4 var nodes = [];
5 var selection = box.getSelectionModel().getSelection();
6 selection.forEach(function(element){
7 if(element instanceof twaver.Node){
8 var xy = element.getCenterLocation();
9 nodes.push({name: element.getName(), x: xy.x, y: xy.y});
10 }
11 });
12 websocket.send(jsonToString({action: "node.move", data: nodes}));
13 }
14 });
后臺代碼
1 class AlarmWebSocket implements org.eclipse.jetty.websocket.WebSocket.OnTextMessage
2 {
3
4 @Override
5 public void onMessage(String message) {
6 Object json = JSON.parse(message);
7 if(!(json instanceof Map)){
8 return;
9 }
10 Map map = (Map)json;
11 Object action = map.get("action");
12 Object data = map.get("data");
13 if("node.move".equals(action)){
14 if(!(data instanceof Object[])){
15 return;
16 }
17 Object[] nodes = (Object[])data;
18 for(Object nodeData : nodes){
19 if(!(nodeData instanceof Map) || !((Map)nodeData).containsKey("name") || !((Map)nodeData).containsKey("x") || !((Map)nodeData).containsKey("y")){
20 continue;
21 }
22 String name = ((Map)nodeData).get("name").toString();
23 Data element = elementMap.get(name);
24 if(!(element instanceof Node)){
25 continue;
26 }
27 double x = Double.parseDouble(((Map)nodeData).get("x").toString());
28 double y = Double.parseDouble(((Map)nodeData).get("y").toString());
29 ((Node)element).x = x;
30 ((Node)element).y = y;
31 }
32
33 }else{
34 return;
35 }
36 for(AlarmWebSocket client : clients){
37 if(this.equals(client)){
38 continue;
39 }
40 sendMessage(client, null, message);
41 }
42 }
43 }
2 {
3

4 @Override
5 public void onMessage(String message) {
6 Object json = JSON.parse(message);
7 if(!(json instanceof Map)){
8 return;
9 }
10 Map map = (Map)json;
11 Object action = map.get("action");
12 Object data = map.get("data");
13 if("node.move".equals(action)){
14 if(!(data instanceof Object[])){
15 return;
16 }
17 Object[] nodes = (Object[])data;
18 for(Object nodeData : nodes){
19 if(!(nodeData instanceof Map) || !((Map)nodeData).containsKey("name") || !((Map)nodeData).containsKey("x") || !((Map)nodeData).containsKey("y")){
20 continue;
21 }
22 String name = ((Map)nodeData).get("name").toString();
23 Data element = elementMap.get(name);
24 if(!(element instanceof Node)){
25 continue;
26 }
27 double x = Double.parseDouble(((Map)nodeData).get("x").toString());
28 double y = Double.parseDouble(((Map)nodeData).get("y").toString());
29 ((Node)element).x = x;
30 ((Node)element).y = y;
31 }
32
33 }else{
34 return;
35 }
36 for(AlarmWebSocket client : clients){
37 if(this.equals(client)){
38 continue;
39 }
40 sendMessage(client, null, message);
41 }
42 }
43 }
完整代碼
代碼:webSocketDemo結(jié)構(gòu):
