基於 HTML5 WebGL 的 水泥工廠可視化系統

前言

    現在的製造行業,基於數據進行生產策略制定與管理已經成爲一種趨勢,特別是工業4.0的浪潮下,數據戰略已經成爲不少製造企業的優先戰略,而數據可視化以更直觀的方式,幫助指導決策,成爲數據分析傳遞信息的重要工具。經過數據可視化系統助力實現數據驅動的工業世界,爲工業4.0提供更加靈活、敏捷、高效、個性化的數據支撐。今天就給你們帶來一個採用HightopoHT for Web產品實現了一個水泥工廠可視化系統。html

系統預覽

 

 

本案例共有七個子系統:node

  • 數據概況-- 展現全廠年月時間單位的各項數據概況
  • 窯系統運行-- 用窯工藝流程動畫展現窯系統實時運行狀態
  • 系統運行狀況-- 用動畫流程圖展現整個系統運行狀況
  • 生料質量控制-- 用圖表和流程圖展現各類生料的配比狀況
  • 熟料質量控制-- 用動畫流程圖展現各類熟料的配比狀況
  • 煤粉質量控制-- 用圖表和流程圖對煤粉質量進行監控
  • 智能物流-- 經過 3D 場景實時監控進出廠車輛,和各項原料運輸情

子系統頁面切換

      切換不一樣子系統時,左側菜單和頂部標題是不須要切換的,因此咱們把須要切換的內容部分別放在不一樣的 Block 中,Block 類型,自己不繪製任何內容,用於做爲其它節點的父節點,能夠與子節點同步大小,當它隱藏或顯示時,全部子節點都會跟着隱藏或顯示。因此當咱們切換子系統時只須要控制對應的 Block 顯示隱藏,而不須要去加載切換多張圖紙。json

流向地圖

 

    在數據概況頁面中,流向地圖展現年度水泥向各地區的銷售狀況,這裏咱們用 Shape 類型繪製線段來鏈接源地和匯地,用流動效果表示銷售關係。流動效果只需引入 HT 的ht-flow.js插件,便可經過簡單的屬性設置實現,代碼以下:ide

// 獲取線段的父節點
this.flowParent = dm.getDataByTag('saleFlowParent'); // 遍歷獲得全部線段
this.flowParent.eachChild(child => { // 開啓流動,設置流動樣式
 child.s({ // 開啓流動
        'flow': true, // 設置流動組中最大元素的尺寸
        'flow.element.max': 4, // 設置流動組中的元素的漸變陰影中心顏色
        'flow.element.shadow.begincolor': '#49e5fe', // 設置流動組中的最大元素的漸變陰影尺寸
        'flow.element.shadow.max': 16, // 設置流動組中的元素的漸變陰影邊緣顏色
        'flow.element.shadow.endcolor': 'rgba(73, 229, 254, 0)',
    });
});

 窯系統動畫

    在窯系統運行頁面中,窯工藝流程動畫很直觀的展現了窯系統實時運行狀態。畫面中火焰、水和熟料在傳送帶上運輸的動畫效果,爲了在性能較差的設備上也能流暢運行,我經過切換不一樣矢量圖形的方式實現。這裏用到了 HT 矢量中狀態機制,先繪製多個不一樣的矢量組件,每一個組件均可以定義狀態來決定本身在哪一個狀態下顯示,只要經過 data.s('state') 修改節點狀態就能夠實現以下效果:函數

    使用一個定時器,不斷地改變節點的狀態值,相關代碼以下:工具

this._stateTimer = setInterval(() => {
    stateNodes.forEach(node => { this.stateAnimation(node);
    });
}, 180); //切換狀態
stateAnimation(node) {
    let stateIndex = (node.a('stateIndex') || 0) % stateEnum.length,
        state = stateEnum[stateIndex].value;
    node.s('state', state);
    node.a('stateIndex', ++stateIndex);
}

流程圖動畫

    流程圖中流動線一樣是使用ht-flow.js插件實現。因爲圖紙上的線段比較多,我把不一樣的線段分組放在不一樣的 Block 下,遍歷其子節點設置樣式,代碼以下:佈局

//設置流動屬性
 setNodeFlow (data, value) { if (data instanceof ht.Block) {
        data.eachChild(child => { this.setNodeFlow(child, value);
        });
    } else if (data.getDisplayName() === 'line'){
        data.s({ 'flow': value, 'flow.element.max': 4, 'flow.element.count': 1, 'flow.count': 5, 'flow.step': 10 });
    }
} //設置虛線流動屬性
setNodeDashFlow(data, value) { if (data instanceof ht.Block) {
        data.eachChild(child => { this.setNodeDashFlow(child, value);
        });
    } else if (data.getDisplayName() === 'border'){ if (value) {
            data.s({ 'shape.dash.flow': true, 'shape.dash': true });
        } else {
            data.s({ 'shape.dash.flow': false, 'shape.dash': false });
        }
    }
}

    爲了使動畫看起來更順暢,我給一些節點加上透明度動畫,設置節點透明度的代碼以下:性能

//設置節點透明度
setNodeOpacity (data, value = 0.5) { if (data instanceof ht.Block) {
        data.eachChild(child => { this.setNodeOpacity(child, value);
        });
    } else {
        data.s('opacity', value);
    }
}

    接下來只須要依次執行動畫:動畫

//開始流程圖動畫
start() {
    let {eo, eoInput, eoLine1, eoKind, eoCalu} = this;
    //工況輸入透明度動畫
    this.gv.enableFlow(30);
    this.setNodeOpacity(eo);
    this.setNodeFlow(eo, false);
    (new Promise((resolve, reject) => {
        this.animtion = startAnim({
            frames: 16,
            interval: 5,
            finishFunc: () => {resolve()},
            action: (v, t) => {
                this.setNodeOpacity(eoInput, 0.5 + 0.5 * v);
            }
        });
    })).then(() => {
        //連線連線透明動畫,流動
        return new Promise((resolve, reject) => {
            this.animtion = startAnim({
                frames: 12,
                interval: 10,
                finishFunc: () => {
                    this.setNodeFlow(eoLine1, true);
                    this.timer = setTimeout(() => {resolve()}, 1500);
                },
                action: (v, t) => {
                    this.setNodeOpacity(eoLine1, 0.5 + 0.5 * v);
                }
            });
        })
    }).then(() => {
        //軟計算透明動畫
        return new Promise(resolve => {
            this.animtion = startAnim({
                frames: 16,
                interval: 5,
                finishFunc: () => {resolve()},
                action: (v, t) => {
                    this.setNodeOpacity(eoKind, 0.5 + 0.5 * v);
                    this.setNodeOpacity(eoCalu, 0.5 + 0.5 * v);
                }
            });
        });
    }).then(() => {
        //軟計算透明虛線流動
        return new Promise(resolve => {
            this.setNodeDashFlow(eoKind, true);
            this.setNodeDashFlow(eoCalu, true);
            this.timer = setTimeout(() => {
                this.setNodeDashFlow(eoKind, false);
                this.setNodeDashFlow(eoCalu, false);
                resolve();
            }, 3000);
        });
    }).then(() => {
        ......
    })
}

智能物流

    前面六個子系統均爲 2D 界面,而智能物流頁面則是嵌入了一個 3D 場景。實現方式是經過定義 HT 矢量 JSON 的renderHTML函數屬性,可實如今 GraphView 拓撲圖上,嵌入任意第三方 HTML DOM 元素。不過這裏也要注意一點,HT 的圖紙是 Canvas 實現的,renderHTML 的 DOM 元素必定在 Canvas 之上,使用 renderHTML 的 DOM 與常規 Canvas 上繪製的圖元不可能有層級控制可能性。下面展現一下 renderHTML 函數屬性裏的代碼:ui

renderHTML : function (data, gv, cache) { // 避免重複建立g3d
    if (!cache.g3d) { // 建立 3D 視圖組件
        var g3d = cache.g3d = new ht.graph3d.Graph3dView(); // 佈局函數,根據圖元的位置信息擺放HTML元素
        g3d.layoutHTML = function () {
            gv.layoutHTML(data, g3d, true);
        }; // 阻止事件冒泡
        g3d.getView().addEventListener('mousedown', function (event) {
            event.stopPropagation();
        });
        g3d.getView().addEventListener('touchstart', function (event) {
            event.stopPropagation();
        });
    } // 獲取圖元自定義屬性sceneURL的值
    var sceneURL = data.a('sceneURL'); // 獲取圖元自定義屬性onPostDeserialize的值
    var onPostDeserialize = data.a('onPostDeserialize'); // 當圖元自定義屬性sceneURL改變時,清除舊dataModel,反序列化新的sceneURL
    if (cache.g3d.sceneURL !== sceneURL) {
        cache.g3d.dm().clear();
        cache.g3d.sceneURL = sceneURL; if (sceneURL) {
            cache.g3d.deserialize(sceneURL, function (json, dm, g3d, datas) { // 在反序列化後的回調函數中,執行onPostDeserialize函數
                onPostDeserialize && onPostDeserialize(json, dm, g3d, datas);
            });
        }
    } return cache.g3d;
}

     3D場景嵌入後,接下來實現水泥廠內的車輛動畫。根據後臺傳來車輛進入工廠的數據,咱們建立運載不一樣原料的車輛模型,讓它們沿着不一樣的路徑抵達對應的廠房。一樣是用 Shape 類型事先繪製好路徑,根據 Shape 的 Points 和 Segments 信息,實現車輛沿着路徑行駛動畫。相關代碼以下:

carAnimation(car, path, duration) { // 車輛行駛動畫
 ht.Default.startAnim({
            duration: duration,
            easing: Easing.easeNone,
            action: function (v, t) { // 設置偏移量
                let offset = Math.floor(v * 100); // 根據偏移量獲得在路徑上的點座標
                let position = ht.Default.getPercentPositionOnPoints(path.getPoints(), path.getSegments(), offset); // 根據偏移量獲得在路徑上的點於路徑切線角度
                let angle = ht.Default.getPercentAngle(path.getPoints(), path.getSegments(), offset); // 設置車輛位置座標及旋轉角度
 car.setX(position.x);
                car.setY(position.y);
                car.setRotationY(Math.PI / 2 - angle);
            },
        });
    }

總結

    工業互聯網是工業發展的必經之路,咱們國家是一個工業大國,正處在工業轉型升級的關鍵時刻,面臨着人工成本上升、原材料價格波動、貿易競爭日益加重等問題,迫切須要提升效率、下降生產成本。只有堅決不移地推進工業互聯網落地,加快更多企業的數字化轉型和智能化改造,纔有能讓在全球化競爭中立於不敗之地。可視化做爲智能化數字化的最後一環,讓複雜抽象的數據變得真正可知可感,幫助決策者發現規律,洞悉將來,爲企業提速增效。

    還有更多的可視化案例能夠參考:https://www.hightopo.com/demos/index.html

相關文章
相關標籤/搜索