隨着工業物聯網和互聯網技術的普及和發展,人工填料的方式已經逐漸被機械設備取代。工業廠商減少誤操做、提高設備安全以及追求高效率等製造特色對設備的要求越發高標準、嚴要求。同時機械生產之後還需聽從整個項目流程的規範管理,如何實行管理與交接也是一大嚴峻的挑戰。所以,整個生產流程中還應該制定一套關於管理流程的可視化界面。html
在工業過程控制中,按被控對象的實時數據採集的信息與給定值比較產生的偏差的比例、積分和微分進行控制的控制系統,簡稱 PID 控制系統。PID 控制生產環境具備適應性強,魯棒性強,使用方便等特色。進料系統則涉及到超高壓技術,在流水線系統中普遍應用,可以實現設備半自動化或自動化送料做業,解決傳統進料方式計量不許、工做環境污染以及工人勞動強度高等問題,從而實現高效的流水線加工。結合 PID 和自動化部署,能夠爲電力、機械、冶金、化工、食品、紡織等工業或者民用行業供需。本篇文章經過搭建危險廢物進料系統的 2D 場景以及數據界面展現,幫助咱們瞭解如何使用 HT 實現一個可視化的 PID 控制進料系統。web
項目地址預覽: 基於 HTML5 的 PID-進料系統可視化界面 http://www.hightopo.com/demo/PID-feed-system/json
api
1 dataModel = new ht.DataModel(); //數據容器,用來存取數據節點Node 2 graphView = new ht.graph.GraphView(dataModel); //拓撲組件 3 graphView.addToDOM(); //將組件添加到body中
上述代碼添加組件到body中所用的是 addToDom 方法,HT組件通常會嵌入BorderPane、SplitView和TabView等容器中使用,而最外層的HT組件則須要用戶手工將 getView() 返回的底層 div 元素添加到頁面的 DOM 元素中,這裏須要注意的是,當父容器大小變化時,若是父容器是 BorderPane 和 SplitView 等這些HT預約義的容器組件,則HT的容器會自動遞歸調用孩子組件 invalidate 函數通知更新。但若是父容器是原生的 html 元素, 則 HT 組件沒法獲知須要更新,所以最外層的 HT 組件通常須要監聽 window 的窗口大小變化事件,調用最外層組件 invalidate 函數進行更新。安全
爲了最外層組件加載填充滿窗口的方便性,HT 的全部組件都有 addToDOM 函數,其實現邏輯以下,其中 iv 是 invalidate 的簡寫:app
1 addToDom = function(){ 2 var self = this, 3 view = self.getView(), //獲取組件的底層div 4 style = view.style; 5 document.body.appendChild(view); //將組件底層div添加到body中 6 style.left = '0'; //默認全部組件的position都設置爲absolute絕對定位 7 style.right = '0'; 8 style.top = '0'; 9 style.bottom = '0'; 10 window.addEventListener('resize',function(){ self.iv(); },false); //窗口改變大小,調用刷新函數 11 }
將視圖默認方法重置:異步
1 graphView.setPannable(false); //禁用經過鼠標拖拽進行平移操做 2 graphView.setRectSelectable(false); //禁用拓撲上進行框選操做 3 graphView.setMovableFunc(()=>{false}); //禁用移動過濾器函數
在 2D 編輯器上建立 2D 圖形會生成 JSON 文件,引入生成場景須要進行反序列化:webstorm
1 ht.Default.xhrLoad('displays/industry/PID-進料系統.json',function(text){ 2 var json = ht.Default.parse(text); //解析爲JSON對象 3 dataModel.deserialize(json); //反序列化爲場景 4 })
在 HT 中,Data 類型對象構造時內部會自動被賦予一個 id 屬性,可經過 data.getId() 和 data.setId( id ) 獲取和設置,Data 對象添加到 DataModel 以後不容許修改 id 值,可經過 dataModel.getDataById (id ) 快速查找 Data 對象。可是通常建議 id 屬性由 HT 自動分配,用戶業務意義的惟一標示可存在 tag 屬性上,經過 Data#setTag( tag ) 函數容許任意動態改變 tag 值,經過DataModel#getDataByTag(tag) 可查找到對應的 Data 對象,並支持經過 DataModel#removeDataByTag( tag ) 刪除 Data 對象。咱們這邊經過在 JSON 中設置 Data 對象的 tag 屬性,在代碼中經過 dataModel.getDataByTag( tag ) 函數來獲取該 Data 對象:編輯器
1 { 2 "c": "ht.Node", 3 "i": 407, 4 "p": { 5 "displayName": "抓手的結", 6 "parent": { 7 "__i": 403 8 }, 9 "tag": "gripKnot", 10 "image": "symbols/symbol factory/垃圾處理/抓手的結.json", 11 "position": { 12 "x": -569.62125, 13 "y": -117.05025 14 }, 15 "width": 50, 16 "height": 25 17 }, 18 "s": { 19 "select.width": 0 20 } 21 },
1 var gripRightPaw = dataModel.getDataByTag('gripRightPaw'); 2 var girpLeftPaw = dataModel.getDataByTag('grapLeftPaw'); 3 var gripKnot = dataModel.getDataByTag('gripKnot');
ide
1 //循環水流動 2 function animation() { 3 var lineJson = {}; 4 var name = ''; 5 var speed = 20, 6 lastTime = Date.now(); 7 //循環獲取水流 tag,並設置初始化 shape.dash.offset 爲0 8 for (var i = 1; i <= 9; i++ ) { 9 if (i != 8) { 10 name = 'line'+i; 11 lineJson[name] = 0; 12 } 13 } 14 ht.Default.startAnim({ 15 duration: 5000, 16 action: function () { 17 var time = Date.now(), 18 deltaTime = (time - lastTime) / 1000; 19 for (var tags in lineJson) { 20 if (tags.split('e')[1] % 2) { 21 lineJson[tags] += deltaTime * speed; 22 } else { 23 lineJson[tags] -= deltaTime * speed; 24 } 25 var lines = dataModel.getDataByTag(tags); 26 lines.setStyle('shape.dash.offset',lineJson[tags]); 27 } 28 lastTime = time 29 }, 30 finishFunc: function () { 31 animation(); 32 //TODO... 也能夠在這裏異步調用下一個動畫 33 } 34 }) 35 }
該例首先根據已建立的循環水流(已綁定 tag 標籤)經過 for 循環以及 dataModel. getDataByTag 動態獲取 Data 節點,經過標籤名攜帶的數字判斷水流方向,最終使用 Data.setStyle(能夠簡寫爲 Data.s ) 設置虛線部分的偏移距離。
上面的循環水流爲例,若是 lineJson[tags] += value (定值) ,當用戶放大視圖時圖元數量減小,會多調用幾回 anim 中的 action 函數,流動速度增快,縮小同理。所以採用 value = speed * deltaTime 的解決方式,解決視圖在不一樣縮放 zoom 的狀況下播放速度不一致的問題,具體原理以下:
1 //global 2 var lastTime = Date.now(); 3 var distance = 0; //距離 4 var speed = 20; //速度 5 //action 6 ht.Default.startAnim({ 7 duration:5000, 8 action:function(){ 9 var time = Date.now(); 10 var deltaTime = (time - lastTime) / 1000; 11 distance += speed * deltaTime; 12 lastTime = time; 13 }, 14 finishFunc:function(){//TODO} 15 })
1 dataModel.addScheleTask({ 2 interval, //調度間隔 3 beforeAction(){}, //調度開始以前的動做 4 action(){}, //調度任務 5 afterAction(){} //調度結束以後的動做 6 })
也可使用 callLater 進行實現,ht 內置函數封裝了很是多關於動畫有趣且實操性強的 api ,有興趣能夠進入官網 ( https://www.hightopo.com )進行了解和學習,也能夠線上申請 framework 的試用包。
1 var fp = new ht.widget.FormPane(); //建立面板對象 2 fp.setWidth(200); 3 fp.setHeight(100); 4 fp.setRowHeight(30); //面板行高 5 fp.setPadding(16); 6 fp.getView().className = 'main'; //節點設置類名後能夠直接在 style 中設置屬性,說白了 fp.getView() 就是一個普通的 DOM 節點 7 fp.addRow([{ //經過 addRow 方法添加文本以及進度條等內容 8 id:'text', 9 element:'Current Speed === 20', 10 align:'center' 11 }],[0.1]); 12 fp.addRow([{ 13 id:'speed', 14 slider:{ //進度條 15 min:0, 16 max:100, 17 value:20, //當前進度值 18 step:1, 19 onValueChanged(){ value改變時觸發函數 20 var speed = fp.v('speed'); 21 fp.v('text','Current Speed === ' + speed); 22 } 23 } 24 }],[0.1]); 25 document.body.appendChild(fp.getView());
此時,咱們只要把以前定義的 speed 指向 fp.v('speed') ,就能夠簡單地實現數據視圖綁定:
1 function animation(fp){ 2 var lineJson = {}; 3 var name = ''; 4 var lastTime = Date.now(); 5 var speed; 6 for (var i = 1; i <= 9; i++ ) { 7 if (i != 8) { 8 name = 'line'+i; 9 lineJson[name] = 0; 10 } 11 } 12 ht.Default.startAnim({ 13 duration: 5000, 14 action: function () { 15 speed = fp.v('speed'); 16 var time = Date.now(), 17 deltaTime = (time - lastTime) / 1000; 18 for (var tags in lineJson) { 19 if (tags.split('e')[1] % 2) { 20 lineJson[tags] += deltaTime * speed; 21 } else { 22 lineJson[tags] -= deltaTime * speed; 23 } 24 var lines = dataModel.getDataByTag(tags); 25 lines.setStyle('shape.dash.offset',lineJson[tags]); 26 } 27 lastTime = time; 28 }, 29 finishFunc: function () { 30 animation(fp); 31 } 32 }) 33 }
另外一種是經過 HT 的矢量圖形庫,矢量圖形採用點、線或多邊形的圖形描述方式,解決了 png 、jpg 等格式圖片在縮放過程當中出現失真現象。建立矢量圖形能夠經過常規編輯器如 webstorm、webstorm 經過代碼編寫,也能夠經過 HT-2D 編輯器直接建立圖形,基本上不須要操做代碼就能夠簡單地建立出圖形,有學過 3dmax 或者 CAD 製圖的同窗對此應該都不陌生。在編輯器的不斷完善下,內部已經有許多優秀的圖標和組件案例,這邊就能夠直接引用一些小案例,首先須要建立一張圖紙,而後直接拉取一個自制圖標,相似 legend 的效果都是繪線畫出來的,更改文字部分就能夠直接看到效果了。
關鍵的仍是功能性組件,圖標展現顯示界面,功能性組件支持事件的觸發,首先在控件裏面拉取 slider 圖標,而後到組件欄拉取 slider 組件,設置控件的最大值、最小值和默認值等一系列參數。
能夠得知咱們即將改變的值有兩個,一個是 slider ,一個是文本的值,默認20,咱們給這兩個 Data 對象綁定惟一標籤,分別爲 sliderValue 以及 textValue,先經過進度條的當前值改變文本的值:
1 var sliderValue = dataModel.getDataByTag('sliderValue'); 2 var textValue = dataModel.getDataByTag('textValue'); 3 sliderValue.a('ht.onChange',function(){ //value改變觸發事件 4 textValue.a('textValue',sliderValue.a('ht.value')); 5 })
而後animation拿到進度條的當前值,指向speed:
1 function animation(data) { 2 var lineJson = {}; 3 var name = ''; 4 var lastTime = Date.now(); 5 var speed; 6 for (var i = 1; i <= 9; i++ ) { 7 if (i != 8) { 8 name = 'line'+i; 9 lineJson[name] = 0; 10 } 11 } 12 ht.Default.startAnim({ 13 duration: 5000, 14 action: function () { 15 speed = data.a('ht.value'); 16 var time = Date.now(), 17 deltaTime = (time - lastTime) / 1000; 18 for (var tags in lineJson) { 19 if (tags.split('e')[1] % 2) { 20 lineJson[tags] += deltaTime * speed; 21 } else { 22 lineJson[tags] -= deltaTime * speed; 23 } 24 var lines = dataModel.getDataByTag(tags); 25 lines.setStyle('shape.dash.offset',lineJson[tags]); 26 } 27 lastTime = time; 28 }, 29 finishFunc: function () { 30 animation(data); 31 } 32 }) 33 }
固然也能夠自定義多個 slider 分別控制不一樣的動畫,具體如何實現仍是全憑需求而定。
不侷限於 2D 可視化場景,與 3D 相關生產環境的可視化場景模擬也有許多案例,以下:
3D水泥工廠工藝流程:http://www.hightopo.com/demo/CementFactory/
3D高爐鍊鐵工業流程:http://www.hightopo.com/demo/large-screen-puddling/