1. 程式人生 > >Python工業網際網路監控專案實戰3—websocket to UI

Python工業網際網路監控專案實戰3—websocket to UI

  本小節繼續演示如何在Django專案中採用早期websocket技術原型來實現把OPC服務端資料實時推送到UI端,讓監控頁面在另一種技術方式下,實時顯示現場裝置的工藝資料變化情況。本例我們仍然採用比較輕量級的dwebsocket元件。

1. 安裝dwebsocket元件 

  安裝命令:pip install dwebsocket  

1.1. dwebsocket使用方法 

  如果你想為一個單獨的檢視處理一個websocket連線可以使用accept_websocket裝飾器,它會將標準的HTTP請求路由到檢視中。使用require_websocke裝飾器只允許使用WebSocket連線,會拒絕正常的HTTP請求。  

  在設定中新增設定MIDDLEWARE_CLASSES=dwebsocket.middleware.WebSocketMiddleware這樣會拒絕單獨的檢視使用websocket,必須加上accept_websocket 裝飾器。 

   設定WEBSOCKET_ACCEPT_ALL=True可以允許每一個單獨的檢視實用websockets

 1.2. 常用方法和屬性

  1.request.is_websocket()  如果是個websocket請求返回True,如果是個普通的http請求返回False,可以用這個方法區分它們。 

  2.request.websocket  在一個websocket請求建立之後,這個請求將會有一個websocket屬性,用來給客戶端提供一個簡單的api通訊,如果request.is_websocket()是False,這個屬性將是None。 

  3.WebSocket.wait()  返回一個客戶端傳送的資訊,在客戶端關閉連線之前他不會返回任何值,這種情況下,方法將返回None 

  4.WebSocket.read()  如果沒有從客戶端接收到新的訊息,read方法會返回一個新的訊息,如果沒有,就不返回。這是一個替代wait的非阻塞方法 

   5.WebSocket.count_messages()  返回訊息佇列數量 

  6.WebSocket.has_messages()  如果有新訊息返回True,否則返回False 

  7.WebSocket.send(message)  向客戶端傳送訊息 

  8.WebSocket.__iter__()  websocket迭代器 

  dwebsocket使用起來比較簡單,增加一個簡單的服務端url和重構UI程式碼;UI程式碼建立一個websocket連線並在onmessage 事件裡處理返回的資料即可,不用花費多大的代價就能快速讓監控頁面升級到一個新的方式下,下面看程式碼演進吧。

2.重構服務端程式碼——增加一個推送的websocket url

  使用accept_websocket裝飾器在Collector APP的views檔案中增加一個pushCollector的方法,實現UI端連線上服務端後,服務端使用websocket主動向UI介面推送實時裝置工藝資料,函式程式碼如下:

from dwebsocket.decorators import accept_websocket
import OpenOPC
@accept_websocket   
def pushCollectorData(request):

    tank4C9={            
        'DeviceId': 1,
        'DeviceName':'1#反應罐',
        'Status': 0, #裝置執行狀態
        'OverheadFlow':0 ,#'頂流量',
        'ButtomsFlow': 0, #'低流量'
        'Power': 0, #功率
    }
    Collector={
            'CollectorId': 1,
            'CollectorName':'1#採集器',
            'Status': 0,
            'DeviceList':[tank4C9],
            } 
    Collector={
         'CollectorId': 1,
         'CollectorName':'1#採集器',
         'Status': 0,
         'DeviceList':[tank4C9],
         }  

    if request.is_websocket():
        try:
            while True:   

                opc = OpenOPC.client()
                opc.connect('Matrikon.OPC.Simulation')
                tank4C9['OverheadFlow']= opc['Random.Int1']
                tank4C9['ButtomsFlow']= opc['Random.Int2']
                tank4C9['Power']= opc['Random.Int4']
                opc.close()
                request.websocket.send(\
                    json.dumps( {"rows":[Collector],'total':1}))
                time.sleep(2)
 
        finally:
            client.disconnect()

  解讀:上文程式碼與原來的主要差別就是從被動重新整理(UI請求後)讀去opc服務的tag位號值,變成間隔time.sleep(2)秒讀取資料後通過request.websocket.send到UI端。

3. 重構UI端程式碼

   這裡django與Flask的差別就是無須新建一個新的專案,當前專案我們就可以通過重構tank4C9.html頁面程式碼來使用websocket實時推送功能。

  重構後tank4C9.html程式碼如下: 

<html>
<head><title></title>
    <script src="https://lib.sinaapp.com/js/jquery/2.0.2/jquery-2.0.2.min.js"></script>
    </head>

<body>

    <div>
        Status:    <strong id="divStatus">0</strong>
    </div>
    <div>
        OverheadFlow:    <strong id="divOverheadFlow">0</strong>
    </div>
    <div>
        ButtomsFlow:    <strong id="divButtomsFlow">0</strong>
    </div>
    <div>
        Power:    <strong id="divPower">0</strong>
    </div>


    <div>
        pushCount:    <strong id="divpushCount">0</strong>
    </div>


    <script>
        //JQuery 程式碼入口
        $(document).ready(function(){
 

            if ("WebSocket" in window) {
                //連線server--TagCurValue
                var ws = new WebSocket("ws://127.0.0.1:8090/pushCollector/");
                ws.onmessage = function (evt) {
                    // 接收資料
                    d = JSON.parse(evt.data);
                    collector= d.rows[0]
                    for (i = 0; i < collector.DeviceList.length; i++){
                        device = collector.DeviceList[i]
                        $("#divStatus").html(device.Status);
                        $("#divOverheadFlow").html(device.OverheadFlow);
                        $("#divButtomsFlow").html(device.ButtomsFlow);
                        $("#divPower").html(device.Power);
                        $("#divpushCount").html(device.Count);

                    }                   

                };
            } 
        });

    </script>

</body>
</html>

   解讀:UI端程式碼通過ws.onmessage事件更新頁面顯示,對照上一張的ajax輪詢模式的程式碼,程式碼的主體結構和功能並沒有大的變化,只是採用了一種的新的資料傳遞方式而已。

  注意: 

  var ws = new WebSocket("ws://127.0.0.1:8090/pushCollector/");

  pushCollector/ url最後那個“/”,這個點是django與flask的一個差別,否則我們建立這個websocket時會收到301錯誤提示! 

4. 釋出pushCollectorData url

  專案的urls釋出這新的url介面地址,這例我們保留原來的getCollectorData,程式碼如下: 

# Uncomment next two lines to enable admin:
#from django.contrib import admin

from django.urls import path
from Collector import views

urlpatterns = [
    # Uncomment the next line to enable the admin:
    #path('admin/', admin.site.urls)

    path('tank4C9/', views.tank4C9),
    path('getCollectorData/', views.getCollectorData),
    path('pushCollectorData/', views.pushCollectorData),
    
]

4. 除錯執行效果

5. 小結

  本小節我們通過websocket的主動推送方式,完成了實時監控畫面從後臺服務端主動推送到UI端的技術架構迭代,這個過程我們也演示了專案迭代的方式,迭代推進專案功能點的好處非常明顯也就是在一個版本滿足需求的前提下,可以相對從容的採用新的技術和方案升級產品改進效能。

  例子我們保留了原來的getCollectorData url,實際的專案開發也是通過增加新推送方法的方式來組織進行的,這樣新的升級也同時滿足原有ajax模式的後臺訪問方式。從而避免升級過程中,前後臺升級版本不一致導致原有頁面不能正常訪問,避免系統已釋出就“崩潰”的“災難”問題。

  這裡多說一下敏捷程式設計下的“小步快跑,快速迭代”模式下,一些團隊遇到的問題就是一開始極簡設計滿足當下要求,然後在不斷功能迭代過程中專案產品架構技術快速老化,可是團隊還是不斷的增加功能點,而沒有人員關心技術架構優化和調整。最終,導致問題越積越多,架構越來越難用,產品構建越來越慢,最後等待一次徹底的專案“重構”。一些“好的”專案應該在過程中逐步演化程式碼結構來滿足不斷擴張的功能需求。

  敏捷程式設計的前提是要有一套體系來做保證的,需求管理、程式碼重構、單元測試等等,比如:程式碼重構在敏捷程式設計專案過程中就非常重要,一開始簡單滿足需求,一旦發現引入新的需求程式碼不能很好的滿足需求的變化時,引入好的設計模式,採用程式碼重構的方式來優化程式碼結構,並通過迴歸單元測試來保證新的程式碼結構能夠正常通過原來的單元測試。盲目的採用敏捷程式設計又沒有采用它有效管理的一整套機制,最後陷入專案泥潭的,只能說是沒有理解好“敏捷”的核心要素罷了。

&n