基于 HTML5 WebGL 的 水泥工厂可视化系统
前言
如今的制造行业,基于数据进行生产策略制定与管理已经成为一种趋势,特别是 工业4.0 的浪潮下,数据战略已经成为很多制造企业的优先战略,而数据可视化以更直观的方式,帮助指导决策,成为数据分析传递信息的重要工具。通过数据可视化系统助力实现数据驱动的工业世界,为 工业4.0 提供更加灵活、敏捷、高效、个性化的数据支撑。今天就给大家带来一个采用 hightopo 的 ht for web 产品实现了一个水泥工厂可视化系统。
系统预览
本案例共有七个子系统:
- 数据概况 -- 展示全厂年月时间单位的各项数据概况
- 窑系统运行 -- 用窑工艺流程动画展示窑系统实时运行状态
- 系统运行情况 -- 用动画流程图展示整个系统运行情况
- 生料质量控制 -- 用图表和流程图展示各种生料的配比情况
- 熟料质量控制 -- 用动画流程图展示各种熟料的配比情况
- 煤粉质量控制 -- 用图表和流程图对煤粉质量进行监控
- 智能物流 -- 通过 3d 场景实时监控进出厂车辆,和各项原料运输情
子系统页面切换
切换不同子系统时,左侧菜单和顶部标题是不需要切换的,所以我们把需要切换的内容部分别放在不同的 block 中,block 类型,本身不绘制任何内容,用于作为其它节点的父节点,可以与子节点同步大小,当它隐藏或显示时,所有子节点都会跟着隐藏或显示。所以当我们切换子系统时只需要控制对应的 block 显示隐藏,而不需要去加载切换多张图纸。
流向地图
在数据概况页面中,流向地图展示年度水泥向各地区的销售情况,这里我们用 shape 类型绘制线段来连接源地和汇地,用流动效果表示销售关系。流动效果只需引入 ht 的 插件,即可通过简单的属性设置实现,代码如下:
// 获取线段的父节点 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); }
流程图动画
流程图中流动线同样是使用 插件实现。由于图纸上的线段比较多,我把不同的线段分组放在不同的 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 函数属性里的代码:
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
转自:https://www.cnblogs.com/htdaydayup/p/12393328.html