基於HTML5 WebGL 的3D 棉花加工監控系統

基於HTML5 WebGL 的3D 棉花加工監控系統

前言

現在的棉花加工行業還停留在傳統的反應式維護模式當中,當棉花加下廠的設備突然出現故障時,控製程序需要更換。這種情況下,首先需要客戶向設備生產廠家請求派出技術人員進行維護,然後生產廠家才能根據情況再派人到現場進行處理。由於棉花加工設備分佈在台灣各地乃至出口到世界各地,從客戶反應問題到廠家派人到達現場的時間週期就會很長,少則一天,個別偏遠的地方可能會需要幾天,不同程度地影響到企業生產活動的繼續進行。傳統的反應式維護存在以下缺點:售後服務響應速度慢;維護成本高;生產效率低下;停車率高;管理成本高;無法應對合格工程師不足的情況。

遠程監控系統主要是通過分佈於棉花加工生產線各種設備的傳感器、開關信號、視頻監控設備、 PLC 控制器等裝置,通過智能聯網設備集成到互聯網和局域網上面,實現對生產、運營情況的隨時掌握,建立網絡範圍內的監控數據和網上知識資源庫,根據現場採集的設備運行數據進行遠程診斷和在線維修。

代碼實現

創建場景

首先是創建一個三維場景( hightopo.com/guide/guid ),通過將場景中的元素添加到保存數據的數據容器( hightopo.com/guide/guid )中即可:

vardm=newht.DataModel();// 数据容器
varg3d=newht.graph3d.Graph3dView(dm);// 三维组件
g3d.addToDOM();// 将三维组件添加到 body 体中

上面代碼中出現的addToDOM 方法是將調用此方法的組件通過getView 方法獲取到此組件的底層div,隨後將此div 添加到body 體中。 HT 的組件一般都會嵌入BorderPane、SplitView 和TabView 等容器中使用,而最外層的HT組件則需要用戶手工將getView() 返回的底層div 元素添加到頁面的DOM 元素中,這裡需要注意的是,當父容器大小變化時,如果父容器是BorderPane 和SplitView 等這些HT預定義的容器組件,則HT的容器會自動遞歸調用孩子組件invalidate 函數通知更新。但如果父容器是原生的html 元素, 則HT 組件無法獲知需要更新,因此最外層的HT 組件一般需要監聽window 的窗口大小變化事件,調用最外層組件invalidate 函數進行更新。

為了最外層組件加載填充滿窗口的方便性,HT 的所有組件都有addToDOM 函數,其實現邏輯如下,其中iv 是invalidate 的簡寫:

addToDOM=function(){varself=this,view=self.getView(),//獲取組件的底層divstyle=view.style;document.body.appendChild(view);//將組件底層div添加進body中style.left='0';//ht默認將所有的組件的position都設置為absolute絕對定位style.right='0';style.top='0';style.bottom='0';window.addEventListener('resize',function(){self.iv();},false);//窗口大小改變事件,調用刷新函數}

整個大環境搭建好了後,我們需要向場景中添加3D 模型,並進行位置的擺放,這裡採用的是將整個場景的模型以及模型的擺放放在一個JSON 格式的文件中,然後通過將這個JSON 文件反序列化到數據容器DataModel 中,即可呈現JSON 文件中的場景內容以及模型的擺放位置:

ht.Default.xhrLoad('scenes/抓棉机.json',function(text){// 加载 JSON 文件
dm.deserialize(text);// 将 JSON 反序列化到数据容器中
});

上面出現的ht.Default.xhrLoad 方法是一個封裝好的異步加載文件的函數,可以通過這種方法來加載JSON 文件,因為此方法為異步加載,所以如果要操作此函數反序列化後的數據容器中的元素,需要在此函數中進行後續的操作。

場景動畫

因為整個場景中的元素都是從此JSON 文件中反序列化出來的,此JSON 文件中保存的只有場景的內容,並不包括動畫以及交互,對於不同部分的元素的動畫也不同,我們需要單獨將這些元素取出來,這里通過dm.getDataByTag(tag)方法實現( hightopo.com/guide/guid ),此方法通過tag唯一標識來獲取節點的訊息:

varequipment=dm.getDataByTag('equipment');// 获取轧棉机的节点
varhand=dm.getDataByTag('hand');// 获取轧棉机“手”节点
varlight=dm.getDataByTag('light');// 获取轧棉机顶部的指示灯的节点

獲取到這些需要做動畫的節點後,這裡我用的是動畫插件setAnimation ( hightopo.com/guide/guid )方法來做的,動畫插件更進一步對動畫進行封裝,用戶只需用描述性的說明HT 即可自動實現動畫過程,動畫插件可以將圖元的一個或多個屬性值(如width、height、opacity 等)從一個值平滑的緩動至另一個值,同時提供了豐富的緩動方式用於實現各種效果。但是使用這個插件前得先引入ht-animation.js 文件:

<scriptsrc="ht.js"></script> <!--先引入ht.js--><scriptsrc="ht-animation.js"></script>

這裡總共有三個部分有動畫,採用的方法大致相同,這裡僅對整個軋棉機的機身的左右移動的動畫進行說明。

equipment.setAnimation({//動畫調用方法moveDown:{//定義了一個名為moveDown的動畫過程,這個動畫過程改變圖元的x軸坐標,將其從623變化至-256from:623,//動畫開始時的屬性值to:-256,//動畫結束時的屬性值interval:equipInterval,//動畫間隔,單位msnext:["moveUp"],//字符串類型,指定當前動畫完成之後,要執行的下個動畫,可將多個動畫融合onUpdate:function(value){//回調函數,動畫的每一幀都會回調此函數this.setX(value);//設置該節點的x軸的值為當前動畫from到to的值formPane.getItemById('xValue').element=value.toFixed(2);//獲取form表單上的xValue元素,同時改變此值formPane.iv();//表單內容變化後要通知表單進行刷新變化}},moveUp:{//定義了一個名為moveUp的動畫過程,這個動畫過程改變圖元的x軸坐標,將其從-256變化至623from:-256,to:623,interval:equipInterval,next:["moveDown"],onUpdate:function(value){this.setX(value);formPane.getItemById('xValue').element=value.toFixed(2);formPane.iv();}},start:['moveDown']// start是一個數組,用於指定要啟動的一個或多個動畫});

注意,要使用動畫,首先您需要調用ht.DataModel#enableAnimation(interval) 啟動全局動畫定時器,默認interval 為16ms,如果不設置此參數值,則DataModel 定時器每隔16ms 左右就會遍歷自己所有的Data,根據Data 的animation 配置執行動畫。

dm.enableAnimation();

表單創建

前面代碼中出現的form表單( hightopo.com/guide/guid ),是通過createForm方法創建的,此方法定義如下(PS:由於form表單的列表稍長,這裡就選取幾個比較有代表性的表單元素進行說明):

//創建form表單functioncreateForm(){varfp=newht.widget.FormPane();//創建表單組件實例fp.setWidth(200);//設置表單組件的寬度fp.setHeight(250);//設置表單組件的高度fp.getView().style.top='8%';//設置表單組件的樣式fp.getView().style.right=0;fp.getView().style.background='rgba(255, 255, 255, 0.3)';document.body.appendChild(fp.getView());//將表單組件的底層div添加到body體中varequipment=dm.getDataByTag('equipment');//通過tag唯一標識獲取元素varhand=dm.getDataByTag('hand');fp.setLabelAlign('right');//設置表單的文本對齊方式fp.addRow([//向表單中添加一行此方法的參數一為元素數組,可在一行中添加多個元素{//元素一顯示文本內容為“機器號”element:'機器號',color:'#fff'},{//元素二顯示文本內容為“Machine”element:'Machine',color:'#fff'}],[0.1,0.1]);//參數二為每個元素寬度訊息數組,寬度值大於1代表固定絕對值,小於等於1代表相對值,也可為80+0.3的組合fp.addRow([//向表單中添加一行此方法的參數為一個數組,可在一行中添加多個元素{//元素一顯示文本內容為“機器號”element:'抓棉機動畫',color:'#fff'},{//元素二顯示文本內容為一個按鈕元素button:{label:'開始',onClicked:function(){//按鈕點擊觸發事件,啟動軋棉機左右移動動畫dm.enableAnimation();//啟動全局動畫定時器varlight=dm.getDataByTag('light');if(!light)return;lightColor(light);colorTimer=setInterval(function(){lightColor(light);},1000);}}},{button:{label:'停止',onClicked:function(){//按鈕點擊觸發事件,關閉軋棉機左右移動動畫dm.disableAnimation();//停止全局動畫定時器clearInterval(colorTimer);varlight=dm.getDataByTag('light');if(light)light.s('all.color','rgba(0,0,255,0.51)');}}}],[0.2,0.1,0.1]);//將一行中的三個元素都分配寬度進行顯示fp.addRow([//向表單中添加一行此方法的參數為一個數組,可在一行中添加多個元素{//元素一顯示文本內容為“小車行走速度”element:'小車行走速度',color:'#fff'},{//元素二顯示文本內容為一個滑動條id:'slide',slider:{//滑動條value:equipInterval,// slider當前值min:0,// slider最小值max:300,// slider最大值step:20,// slider每移動一步的值onValueChanged:function(){// slider值變化觸發事件varanim=dm.getDataAnimation(equipment);//獲得參數data的動畫配置anim.moveDown.interval=this.getValue();//設置moveDown動畫的interval內容anim.moveUp.interval=this.getValue();//設置moveUp動畫的interval內容}}}],[0.1,0.1]);fp.addRow([//向表單中添加一行此方法的參數為一個數組,可在一行中添加多個元素{element:'X軸',color:'#fff'},{id:'xValue',element:equipment.getX().toFixed(2),//獲取軋棉機當前x軸值color:'#fff'}],[0.1,0.1]);returnfp;}

有趣的部分就說這麼多,如果有什麼建議和意見,歡迎留言或者私信~或者上官網查閱相關資料:

總結

以前對animation 動畫用的比較少,這次也是特地用它仔細研究一下animation 的機制,就animation 設置的動畫能夠以一種“平和”的方式進行值的變化,動畫也看起來比較有條理一些;當然還有能夠設置下一次動畫需要做什麼的操作,這個設計也非常的人性化;同時還能通過getDataAnimation 獲取對象的動畫配置,並通過此方法對對象的動畫進行重新配置,這些優點都是值得拿出來跟大家分享的。

What do you think?

Written by marketer

如何讓node 也支持從url 加載一個module?

導讀《React Native at Airbnb》—— 為什麼Airbnb 放棄了React Native?