Ajax輪詢是通過特定的的時間間隔(如每1秒),由瀏覽器對服務器發出HTTP請求,然后由服務器返回最新的數據給客戶端的瀏覽器。這種簡單粗暴模式有一個明顯的缺點,就是瀏覽器需要不斷的向服務器發出請求,HTTP請求可能包含較長的頭部,其中真正有效的數據可能只是很小的一部分,顯然這樣會浪費很多的帶寬等資源(對於很多局域網內的企業應用,這個簡單粗暴模式確實解決問題)。
1.1. websocket簡介
本文接下來采用一種更加高效率的服務端主動推送技術(WebSocket )來提高數據的傳輸效率。WebSocket通信協議 是 HTML5 開始支持的一種在單個 TCP 連接上進行全雙工通訊的協議。在 WebSocket 技術架構中,瀏覽器和服務器只需要完成一次握手,兩者之間就直接可以創建持久性的連接,並進行雙向數據傳輸。

瀏覽器通過 JavaScript 向服務器發出建立 WebSocket 連接的請求,連接建立以后,客戶端和服務器端就可以通過 TCP 連接直接交換數據。當你獲取 Web Socket 連接后,可以通過 send() 方法來向服務器發送數據,並通過 onmessage 事件來接收服務器返回的數據。
1.2. 創建一個Flask-Sockets服務端項目
本例我們通過VS2019創建一個空的Python項目,來實現Flask-Sockets服務端代碼,我們在現有的解決方案里添加一個Project,如下圖:



1.2.1. 安裝Flask-Sockets組件
Flask我們可采用Flask-Sockets組件來實現websocket 通信驗證原型,
組件網址:https://github.com/heroku-python/flask-sockets
安裝命令:pip install Flask-Sockets
vs community 我們可以直接在Python Environment 安裝Flask-Sockets組件(注意字母大小寫,否則不能安裝成功),如下圖:

1.2.2. 實現websocket服務端源碼
服務端先實現由服務端定時推送一個自增的變量值給客戶端,代碼如下:
from flask import Flask
from flask_sockets import Sockets
import time
app = Flask(__name__)
sockets = Sockets(app)
@sockets.route('/tagCurValue')
def tagCurValue(ws):
'''
代碼模擬每5秒鍾,定時給客戶端推送一個自增的變量數據
'''
n=0
while not ws.closed:
n=n+1
ws.send(str(n))
print("tag curent value:"+ str(n))
time.sleep(5)
@app.route('/')
def hello():
return 'Hello World!'
if __name__ == "__main__":
from gevent import pywsgi
from geventwebsocket.handler import WebSocketHandler
server = pywsgi.WSGIServer(('0.0.0.0', 5000), application=app, handler_class=WebSocketHandler)
print('Server Start')
server.serve_forever()
server = pywsgi.WSGIServer(('0.0.0.0', 5000), application=app, handler_class=WebSocketHandler)
上述代碼的'0.0.0.0'要注意賦值,組件的Example例子是一個空字符串,筆者也踩了坑,花費不少時間才找到案例運行失敗的原因。
1.2.3. 運行FlaskSocketSvr服務端
首先,我們需要把FlaskSocketSvr設置成解決方案的默認項目,后然F5運行測試環境如下圖:


瀏覽器地址欄輸入以下網址:http://127.0.0.1:5000/ 結果如下圖:

1.2.4. 重構客戶端代碼
現在我們重構客戶端代碼以便可以訪問服務端提高的websocket URL獲得數據更新
http://127.0.0.1:5000/tagCurValue
UI JS代碼如下:
//JQuery 代碼入口
$(document).ready(function(){
//setInterval("getData()",3000);
if ("WebSocket" in window) {
//連接server--TagCurValue
var ws = new WebSocket("ws://127.0.0.1:5000/tagCurValue");
//var ws = new WebSocket("ws://127.0.0.1:8008/tagCurValue");
ws.onmessage = function (evt) {
// 接收數據
receivedMsg = evt.data;
$("#divTag").html(receivedMsg);
};
}
});
現在我們在Project通過Python菜單執行Start server,然后再F5運行服務端,我們就可以通過瀏覽器運行測試頁面了。

測試頁面url:http://127.0.0.1:8008/

我們也能通過瀏覽器的開發工具查看網絡訪問只發生了一次。

1.3. 讀取opc服務的tag位號值
最后,我們再把tagCurValue'重構成真正讀取opc服務某一個tag位號的值。以完成了從UI端到服務端主動獲取opc服務tag位號值,並更新UI界面的技術原型。views文件函數tagCurValue'代碼修改如下:
@sockets.route('/tagCurValue')
def tagCurValue(ws):
import OpenOPC
while not ws.closed:
opc = OpenOPC.client()
opc.connect('Matrikon.OPC.Simulation')
result= opc['Random.Int1']
opc.close()
ws.send(unicode( result))
print("tag curent value:"+ unicode( result))
time.sleep(5)
現在,我們重新運行測試環境,結果如下圖:

1.4. 小結
本小節介紹了如何通過采用websocket技術,實現由服務端主動Send數據到UI端的通信模式,客戶端實時刷新UI端tag位號的當前值,提高了數據傳輸的效率和性能,樣例的FlaskSocketSvr服務也演示了一個獨立的服務端到UI端的數據傳輸方式(獨立的服務端地址)。最后,新的服務也通過讀取OPC server的tag位號值演示了通過OPC Sever獲取監控設備數據的方式。
