基于 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

相关文章
相关标签/搜索