Chinaunix首页 | 论坛 | 博客
  • 博客访问: 951907
  • 博文数量: 110
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 1997
  • 用 户 组: 普通用户
  • 注册时间: 2013-12-15 22:39
个人简介

HT for Web 3D. HT for modern web and mobile app development. www.hightopo.com

文章分类

全部博文(110)

文章存档

2020年(1)

2018年(28)

2017年(21)

2016年(10)

2015年(28)

2014年(19)

2013年(3)

我的朋友

分类: HTML5

2018-06-25 11:36:31

工业企业中生产线处于高速运转,由工业设备所产生、采集和处理的数据量远大于企业中计算机和人工产生的数据,生产线的高速运转则对数据的实时性要求也更高。破解这些大数据就是企业在新一轮制造革命中赢得竞争力的钥匙。因此,工业生产可视化系统是工业制造业的最佳选择。

工业生产可视化是将虚拟现实技术有机融入了工业监控系统,系统展现界面以生产厂房的仿真场景为基础,对各个工段、重要设备的形态都进行复原,作业流转状态可以在厂房视图当中直接显示。在单体设备视图中,机械设备的运行模式直接以仿真动画的形式展现,通过图像、三维动画以及计算机程控技术与实体模型相融合,实现对设备的可视化表达,使管理者对其所管理的设备有形象具体的概念。同时,对设备运行中产生的所有参数一目了然,从而大大减少管理者的劳动强度,提高管理效率和管理水平。

以下是基于 HT for Web()的裙房监控系统的具体解决方案图:

 
基于 HT for Web()的 HTML5 技术除了应用在工业行业外,现在越来越多用户实用 HT 在城市服务、农业、楼宇、管廊、安防、BIM、隧道等等传统的领域进行实时联网监控,通过数字孪生双胞胎的方式将实体设备与具体的数据传输过去,方便管理员进行管理,也为监管省去了很大一笔人力开销。
最初客户给出的需求是实现动画以及开关动画的功能,控制动画和灯光的开关占据屏幕的比例不需要太大,按照人类习惯的操作方式来说,放在右上角是最合适的,剩下的整屏就由 3D 场景来填充,主次分明。

3D 场景搭建

要将工业生产可视化,3D 的展现是必不可少,HT 需要创建一个 3D 组件,搭建靠 2 行代码来实现:

点击(此处)折叠或打开

  1. var g3d = new ht.graph3d.Graph3dView();// Hightopo 的 3D 组件(三维场景地基)
  2. g3d.addToDOM();

HT 的组件一般都会嵌入 BorderPane()、SplitView()和 TabView ()等容器中使用,而最外层的 HT 组件则需要用户手工将 getView() 返回的底层 div 元素添加到页面的 DOM 元素中,这里需要注意的是,当父容器大小变化时,如果父容器是 BorderPane 和 SplitView 等这些HT预定义的容器组件,则 HT 的容器会自动递归调用孩子组件 invalidate 函数通知更新。但如果父容器是原生的 html 元素, 则 HT 组件无法获知需要更新,因此最外层的 HT 组件一般需要监听 window 的窗口大小变化事件,调用最外层组件 invalidate 函数进行更新。
为了最外层组件加载填充满窗口的方便性,HT 的所有组件都有 addToDOM 函数,其实现逻辑如下,其中 iv 是 invalidate 的简写:

点击(此处)折叠或打开

  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';//ht 默认将所有的组件的position都设置为absolute绝对定位
  7.     style.right = '0';
  8.     style.top = '0';
  9.     style.bottom = '0';
  10.     window.addEventListener('resize', function () { self.iv(); }, false);//窗口大小改变事件,调用刷新函数
  11. }

接下来我们要向场景中添加各种模型,用代码生成模型是非常痛苦的,我们将整个场景的模型都放到一个 JSON 文件中,并通过 ht.Default.xhrLoad 方法将这个 JSON 转换为 3D 场景显示在界面上:

点击(此处)折叠或打开

  1. var dm = g3d.dm();// 获取 HT 3D 组件的数据容器
  2. ht.Default.xhrLoad('scenes/system.json', function(text) {
  3.     dm.deserialize(text);// 将函数的 text(json)参数传给 deserialize 反序列化方法,可将 json 内容中的元素添加到 dataModel 数据容器中进行显示
  4. }

 ht.Default.xhrLoad 方法是一个异步加载 json 文件的方法,第一个参数为传入的 json 文件,路径是相对于 html 文件的,第二个参数是回调函数,在传入的 json 文件解析完毕之后做的操作。此方法为异步加载,因此需要对 dm 数据容器中的数据进行获取或操作的话,需要将获取/操作的代码写在 dm.deserialize(text) 方法之后,因为此时 dm 数据容器中才有节点。
上面将 JSON 文件发序列化到 dm 数据容器中后界面显示如下:

上图中整个场景的背景是我后期用代码添加的,通过前面的 addToDOM 函数可以知道我们可以通过 getView 方法获取 HT 3D 组件的底层 div,因此要在此 div 上添加一张背景图也就不难了。剩下的 3D 模型部分都是由 JSON 反序列化出来的。

场景中叶轮的转动

工业叶轮的转动不可能是整个模型在转动,而是中间的“滚轮”在转动,这要求设计师在创建模型的时候就将这个部分分离出来,然后我给此部分设置 tag 唯一标识为“yelun”,通过 dm.getDataByTag('yelun') 即可获取到这个节点,然后给这个节点设置旋转动画。
 HT 中调度进行的流程是,先通过 DataModel ()添加调度任务,DataModel 会在调度任务指定的时间间隔到达时, 遍历 DataModel 所有图元回调调度任务的 action 函数,可在该函数中对传入的 Data 图元做相应的属性修改以达到动画效果。
根据上面对调度任务的说明,我们了解到向 dm 数据容器中添加调度任务会遍历整个数据容器,数据容器中内容不多的时候可能感觉不到,但当数据容器中内容多且模型重的情况下,对 dm 数据容器进行过滤就非常有必要了,而且如果添加多个调度任务都遍历了整个数据容器,那么对电脑的性能要求可想而知。我一开始使用的时候就是遗漏了对 dm 数据容器的过滤,因为场景不大,所以一开始没有感觉,后来加了灯光后很重,就立马出现问题了,才发现遗漏了对 data 的过滤判断。
因此,调度任务传入的参数对象中 action 方法传入了一个 data 值,用于设置当前动画的对象,不是此对象的直接可以 return 掉,不做任何操作:

点击(此处)折叠或打开

  1. var task = [];
  2. var yelun = dm.getDataByTag('yelun');// 获取 tag 为 yelun 的节点
  3. // 创建一个动画调度任务
  4. task.yelunTask = {
  5.     interval: 100,// 动画持续时间
  6.     action: function(data) {// 动画内容
  7.         if (data !== yelun) return;
  8.         // 设置 yelun 节点的 x 轴旋转为当前 x 轴旋转值再加上 Math.PI/12
  9.         yelun.setRotationX(yelun.getRotationX() + Math.PI/12);
  10.     }
  11. }
  12. dm.addScheduleTask(task.yelunTask);

容器水位的上升下降

容器中水位的上升下降也是数字孪生(双胞胎)的体现,数据同步进行展现给监控人员,监控人员不需要进行数据采集系统也能直接获取当前数据。

这里将容器水位的上升下降放到一个动画调度任务里了,也就是说通过 dm 数据容器操作这个调度任务就能够同时操作这两个部分的动画,将上一小节中的 yelunTask 调度任务的 action 更改一下,因为上面的代码只对 yelun 节点进行了操作,我们需要对装水的容器也进行操作。首先获取装水的容器,这里将这个节点的唯一标识 tag 设置为“cylinder”:
var cylinder = dm.getDataByTag('cylinder');
然后更改调度中的 action 部分代码:

点击(此处)折叠或打开

  1. action: function(data) {
  2.     if (!(data === yelun || data === cylinder)) return;
  3.     // 叶轮转动
  4.     yelun.setRotationX(yelun.getRotationX() + Math.PI/12);
  5.  
  6.     // 容器水位变化
  7.     if (cylinder.getTall() === 100) cylinder.setTall(0);// 容器水位高度到达 100 的值时,重置为 0else cylinder.setTall(cylinder.getTall() + 1);
  8. }

叶轮转动故障展示

 
因为没有数据的传输,所以这边故障信息我只能自己造假数据了,我创建了一个 10 以内的整数随机数,判断这个值是否为 1,如果为 1 就将运作正常的图标变换成告警图标,同时我还通过这个值来设置 dm 数据容器添加/移除调度任务来控制当前叶轮转动/停止、容器水位变化与否:

点击(此处)折叠或打开

  1. var alarm = dm.getDataByTag('alarm');// 获取告警图标节点
  2. setInterval(function() {
  3.     var random = Math.floor(Math.random()*5);
  4.     if (random === 1) {
  5.         alarm.s('shape3d.image', 'symbols/电信/故障 2.json');// 设置告警图标节点为“故障”图标
  6.         dm.removeScheduleTask(task.yelunTask);// 将叶轮的动画加上
  7.     }
  8.     else {
  9.         alarm.s('shape3d.image', 'symbols/电信/正常 2.json');// 设置告警图标节点为“正常”图标
  10.         dm.addScheduleTask(task.yelunTask);// 移除叶轮的动画
  11.     }
  12. }, 1000);

开启/关闭动画

上一小节我们已经提到了开启/关闭动画的方式,这边我们运用 form 表单,手动操作动画的开启和关闭(注:这里只说明第一行的“水流开关”)。
工业互联网还有一个好处,就是能够远程来控制现场的开关,同时也能快速监测到设备是否故障,能够及时反馈给维修人员,减少了时间成本,以及降低了生产停滞的风险。
首先,我们需要创建一个 formPane ()表单组件,在这个表单组件中添加行数据,这边操作动画的开启和关闭我是用的 checkbox,值变化只有 true 和 false,这个情况用这个是比较优的选择。然后通过监听这个 checkbox 的值的变化事件,设置动画的开启(添加)或者关闭(移除)。

点击(此处)折叠或打开

  1. function createForm(task) {
  2.     var form = new ht.widget.FormPane();// 创建 form 表单组件对象
  3.     form.setWidth(160);// 设置表单组件的宽度
  4.     form.setHeight(90);// 设置表单组件的高度
  5.     // 设置表单组件底层 div 的样式属性
  6.     form.getView().style.right = '10px';
  7.     form.getView().style.top = '10px';
  8.     form.getView().style.background = 'rgba(255, 255, 255, 0.2)';
  9.     form.getView().style.borderRadius = '5px';
  10.     document.body.appendChild(form.getView());// 将 form 表单底层 div 添加到 body 体中
  11.  
  12.     // 水阀开启和关闭
  13.     form.addRow([// 给 form 表单添加一行数据
  14.         {
  15.             checkBox: {// 复选框类,HT 将此封装到 form 中 实际上创建了一个 ht.widget.CheckBox 组件
  16.                 label: '水流开关',// 设置 checkbox 的文本内容
  17.                 labelColor: '#fff',// 设置 checkbox 文本颜色
  18.                 selected: true,// 设置此 checkbox 是否选中
  19.                 onValueChanged: function() {// 监听值变化事件
  20.                     if (this.isSelected()) dm.addScheduleTask(task.arrowTask);// 如果这个 checkBox 选中,则添加动画(开启水阀)
  21.                     else dm.removeScheduleTask(task.arrowTask);// 如果这个 checkBox 未被选中,则移除动画(关闭水阀)
  22.                 }
  23.             }
  24.         }
  25.     ], [0.1]);// 设置这个行数据中列的宽度
  26.     return form;
  27. }

addRow 方法上面代码中一言两语解释不清楚,参考如下说明:
addRow(items, widths, height, params) 添加一行组件
  • items为元素数组,元素可为字符串、json 格式描述的组件参数信息、html 元素或者为 null 的空
  • widths 为每个元素宽度信息数组,宽度值大于 1 代表固定绝对值,小于等于 1 代表相对值,也可为 80+0.3 的组合
  • height 为行高信息,值大于 1 代表固定绝对值,小于等于 1 代表相对值,也可为 80+0.3 的组合,为空时采用默认行高
  • params 为 json 格式的额外参数,例如插入行索引以及行边框或背景颜色等,如 { index: 2, background: 'yellow', borderColor: 'red' }
上面代码中提到的 arrowTask 是对场景中的“箭头”流动添加的动画调度任务,通过控制 form 表单中 checkbox 复选框是否选中可直接操作 dm 是否添加/移除动画调度任务。

灯光的开启/关闭

控制灯光的开启和关闭,这里也是通过 form 表单上的 checkbox 复选框来进行操作的。一般建议不要使用灯光,渲染太烧性能了,这里只是为了效果而添加做一个说明。
首先我们需要创建一个“灯”节点,然后通过设置样式属性 setStyle 来设置灯的类型、颜色、灯照范围等等属性:

点击(此处)折叠或打开

  1. // 添加灯光
  2. var light = new ht.Light();// 创建一个灯节点(继承于 ht.Node) ()
  3. light.p3([15, 120, 50]);// 设置此节点的位置
  4. light.setTag('light');// 设置此节点的唯一标识
  5. dm.add(light);// 将此节点添加到 dm 数据容器中进行显示
  6. light.s({// 设置此节点的样式属性 setStyle 简写为 s
  7.     'light.type': 'point',// 设置灯类型
  8.     'light.color': 'rgb(252,252,149)',// 设置灯颜色
  9.     'light.range': 1400,// 设置灯照范围
  10.     '3d.visible': false// 设置此节点在 3d 上不可见
  11. });

 
然后在 form 表单上添加一行用来控制灯的开关、灯的颜色灯功能:

点击(此处)折叠或打开

  1. // 9、灯光开启和关闭 以及颜色切换
  2. form.addRow([// form 中添加一行 {
  3.     id: 'lightDisabled',// 设置此项的 id 值,可通过 form.getItemById 获取此项
  4.     checkBox: {// 复选框组件
  5.         label: '开关灯',// 设置复选框文本内容
  6.         labelColor: '#fff',// 设置复选框文本颜色
  7.         selected: true,// 设置复选框是否选中
  8.         onValueChanged: function() {// 监听值变化事件
  9.             dm.getDataByTag('light').s('light.disabled', !this.getValue());// 获取灯节点并设置是否关闭灯光效果,light.disabled 属性默认为false,可设置为true关闭灯效果
  10.        }
  11.     }
  12. },
  13. {
  14.     colorPicker: {// 颜色选择器组件
  15.         value: 'rgb(252,252,149)',// 设置当前值
  16.         instant: true,// 设置是否处于即时状态,将会实时改变模型值
  17.         onValueChanged: function() {// 监听值变化事件
  18.             dm.getDataByTag('light').s('light.color', this.getValue())// 设置灯的颜色为当前选中的颜色 }
  19.         }
  20.     }
  21. ], [0.1, 0.1]);

点击切换模型

 HT 将事件监听封装到 mi 事件()中,mi 方法中有多种事件,这里我们需要的是单击节点的事件监听 clickData 事件,通过判断事件类型 e.kind 是否为 clickData,之后对节点的设置模型即可:

点击(此处)折叠或打开

  1. var waterPump6 = dm.getDataByTag('水泵06');// 获取 tag 为“水泵06”的节点
  2. waterPump6.s({// 设置该节点的样式属性
  3.     'note': '点我切换模型',// 设置标注文字内容
  4.     'note.transparent': true,// 设置标注在 3D 下是否透明
  5.     'note.t3': [0, 0, -50],// 设置标注在 3D 下的偏移
  6.     'note.reverse.flip': true//设置标注背面是否显示正面的内容
  7. });
  8. g3d.mi(function(e) {// 监听 3D 组件上的事件
  9.     if(e.kind === 'clickData') {// 点击节点事件
  10.         // 模型点击切换
  11.         if (e.data === waterPump6 && e.data.s('shape3d') === 'models/裙房系统/水泵.json') e.data.s('shape3d', 'models/fengji.json');// 设置点击节点的 shape3d 样式属性
  12.         else if (e.data === waterPump6 && e.data.s('shape3d') === 'models/fengji.json') e.data.s('shape3d', 'models/裙房系统/水泵.json');// 设置点击节点的 shape3d 样式属性
  13.     }
  14. });

 HT 设置模型是通过设置节点的样式属性 node.setStyle(简写为 node.s)为 shape3d 来实现的。

点击隐藏/显示属性窗口

上面说到了事件的监听,既然同为点击事件,我们就在一个监听事件里面进行具体的操作即可,在上面的 if (e.kind === 'clickData') 判断中添加显示/隐藏属性窗口的逻辑:

点击(此处)折叠或打开

  1. var waterPump5 = dm.getDataByTag('水泵05');
  2. waterPump6.s({
  3.     'note': '点我切换模型',
  4.     'note.transparent': true,
  5.     'note.t3': [0, 0, -50],
  6.     'note.reverse.flip': true
  7. });
  8. g3d.mi(function(e) {
  9.     if(e.kind === 'clickData') {
  10.         // 模型点击切换
  11.         if (e.data === waterPump6 && e.data.s('shape3d') === 'models/裙房系统/水泵.json') e.data.s('shape3d', 'models/fengji.json');
  12.         else if (e.data === waterPump6 && e.data.s('shape3d') === 'models/fengji.json') e.data.s('shape3d', 'models/裙房系统/水泵.json');
  13.  
  14.         // 模型点击 隐藏/显示属性窗口
  15.         if (e.data === waterPump5) {// 判断点击的图元是否为 waterPump5
  16.              if(giveWater.s('3d.visible')) {// 判断当前属性窗口是否为显示状态
  17.                   giveWater.s('3d.visible', false);// 设置属性窗口不可见
  18.                   e.data.s('note', '点我显示属性窗口');// 更改标注中的显示内容
  19.              }
  20.              else {
  21.                   giveWater.s('3d.visible', true);// 设置属性窗口可见
  22.                   e.data.s('note', '点我隐藏属性窗口')// 更改标注中的显示内容 }
  23.              }
  24.       }
  25. });

文本内容/颜色变换

工业生产现场数据肯定是不断地变化,数字孪生的特性也在这里充分地展示出来,根据设备返回的数据不断地更新显示数据,清晰地展示了当前设备的动态。

通过 tag 获取场景中对应的属性窗口的节点,此节点为一个面板,相当于六面体有六面,这个节点类型就只有一面,并通过设置属性 shape3d.image 设置此节点上的图片为 tooltips.json 矢量图标()。矢量在 Hightopo(HT)中是矢量图形的简称,常见的 png 和 jpg 这类的栅格位图, 通过存储每个像素的颜色信息来描述图形,这种方式的图片在拉伸放大或缩小时会出现图形模糊,线条变粗出现锯齿等问题。 而矢量图片通过点、线和多边形来描述图形,因此在无限放大和缩小图片的情况下依然能保持一致的精确度。而且 HT 的矢量图形还有一个非常重要的特点,就是能够对矢量图形上的任何一个部分都进行数据绑定,也就是说上图中的五张图,我们可以只绘制一张图,通过数据绑定来改变这张图上的文本以及数值内容。
矢量图标中的数据绑定可以用在工业中的生产看板、大屏中的数据显示等等,都能够以一种高效的方式进行产品的整合。
矢量图形的数据绑定能够再写一篇文章进行阐述了,这里就不多提,大家自行去官网上查看“矢量手册”以及“数据绑定手册”,说明的比较详细。
获取到对应的节点之后,通过 node.a 方法可以获取和设置数据绑定()的属性,这里我们绑定的是文本内容“label”和数值“value”以及数值颜色“valueColor”:

点击(此处)折叠或打开

  1. var billboardArray = [];
  2. // 通过 tag 获取节点
  3. var temperature1 = dm.getDataByTag('回水温度1');// 获取 tag 为"回水温度1"的节点
  4. billboardArray.push(temperature1);
  5. var temperature2 = dm.getDataByTag('回水温度2');
  6. billboardArray.push(temperature2);
  7. var returnPress = dm.getDataByTag('回水压力');
  8. billboardArray.push(returnPress);
  9. var givePress = dm.getDataByTag('供水压力');
  10. billboardArray.push(givePress);
  11. var giveTemp = dm.getDataByTag('供水温度');
  12. billboardArray.push(giveTemp);
  13. var giveWater = dm.getDataByTag('供水流量');
  14. billboardArray.push(giveWater);
  15. // 文字标签内容变换
  16. billboardArray.forEach(function(billboard) {
  17.     billboard.a('label', billboard.getTag());// 设置数据绑定属性为 label 的属性值为当前节点的 tag 内容
  18. });
  19. // 文字标签数字变换+颜色变换 更改图标中绑定的 value 属性值
  20. setInterval(function() {
  21.     billboardArray.forEach(function(billboard) {
  22.         var random = Math.random()*100;
  23.         billboard.a('value', random.toFixed(2));
  24.  
  25.         // 设置图标中“数值内容颜色”
  26.         if (random > 70 && random <= 80) billboard.a('valueColor', '#00FFFF');
  27.         else if (random > 80 && random <= 90) billboard.a('valueColor', '#FFA000');
  28.         else if (random > 90) billboard.a('valueColor', '#FF0000');
  29.         else billboard.a('valueColor', '');
  30.     });
  31. }, 1000);

 工业互联网 3D 的可视化应用非常广泛,除了裙房监控系统,工业设备比如变压器的数据监控以及智能楼宇外还有很多很多,甚至可以运用到农业以及城市规划上......最后,有兴趣的朋友可以私信或留言交流一下技术或者交个朋友。

 


阅读(3771) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~