前言

  随着工业物联网和互联网技术的普及和发展,人工填料的方式已经逐渐被机械设备取代。工业厂商减小误操作、提升设备安全以及追求高效率等制造特点对设备的要求愈加高标准、严要求。同时机械生产以后还需遵从整个项目流程的规范管理,如何实行管理与交接也是一大严峻的挑战。因此,整个生产流程中还应该制定一套关于管理流程的可视化界面。

  在工业过程控制中,按被控对象的实时数据采集的信息与给定值比较产生的误差的比例、积分和微分进行控制的控制系统,简称 PID 控制系统。PID 控制生产环境具有适应性强,鲁棒性强,使用方便等特点。进料系统则涉及到超高压技术,在流水线系统中广泛应用,能够实现设备半自动化或自动化送料作业,解决传统进料方式计量不准、工作环境污染以及工人劳动强度高等问题,从而实现高效的流水线加工。结合 PID 和自动化部署,可以为电力、机械、冶金、化工、食品、纺织等工业或者民用行业供需。本篇文章通过搭建危险废物进料系统的 2D 场景以及数据界面展示,帮助我们了解如何使用 HT 实现一个可视化的 PID 控制进料系统。

  项目地址预览: 基于 HTML5  的 PID-进料系统可视化界面 http://www.hightopo.com/demo/PID-feed-system/

效果预览

  整体协作场景

  抓斗操作场景

  进料场景

代码构建

  搭建场景

  该文主要实现的是 2D 场景,我们需要用到拓扑组件的相关 api 搭建基础场景:

 dataModel = new ht.DataModel(); //数据容器,用来存取数据节点Node
graphView = new ht.graph.GraphView(dataModel); //拓扑组件
graphView.addToDOM(); //将组件添加到body中

  上述代码添加组件到body中所用的是 addToDom 方法,HT组件一般会嵌入BorderPane、SplitView和TabView等容器中使用,而最外层的HT组件则需要用户手工将 getView() 返回的底层 div 元素添加到页面的 DOM 元素中,这里需要注意的是,当父容器大小变化时,如果父容器是 BorderPane 和 SplitView 等这些HT预定义的容器组件,则HT的容器会自动递归调用孩子组件 invalidate 函数通知更新。但如果父容器是原生的 html 元素, 则 HT 组件无法获知需要更新,因此最外层的 HT 组件一般需要监听 window 的窗口大小变化事件,调用最外层组件 invalidate 函数进行更新。

  为了最外层组件加载填充满窗口的方便性,HT 的所有组件都有 addToDOM 函数,其实现逻辑如下,其中 iv 是 invalidate 的简写:

 addToDom = function(){
var self = this,
view = self.getView(), //获取组件的底层div
style = view.style;
document.body.appendChild(view); //将组件底层div添加到body中
style.left = '0'; //默认所有组件的position都设置为absolute绝对定位
style.right = '0';
style.top = '0';
style.bottom = '0';
window.addEventListener('resize',function(){ self.iv(); },false); //窗口改变大小,调用刷新函数
}

  将视图默认方法重置:

 graphView.setPannable(false); //禁用通过鼠标拖拽进行平移操作
graphView.setRectSelectable(false); //禁用拓扑上进行框选操作
graphView.setMovableFunc(()=>{false}); //禁用移动过滤器函数

  在 2D 编辑器上创建 2D 图形会生成 JSON 文件,引入生成场景需要进行反序列化:

 ht.Default.xhrLoad('displays/industry/PID-进料系统.json',function(text){
var json = ht.Default.parse(text); //解析为JSON对象
dataModel.deserialize(json); //反序列化为场景
})

  在 HT 中,Data 类型对象构造时内部会自动被赋予一个 id 属性,可通过 data.getId() 和 data.setId( id ) 获取和设置,Data 对象添加到 DataModel 之后不允许修改 id 值,可通过 dataModel.getDataById (id ) 快速查找 Data 对象。但是一般建议 id 属性由 HT 自动分配,用户业务意义的唯一标示可存在 tag 属性上,通过 Data#setTag( tag ) 函数允许任意动态改变 tag 值,通过DataModel#getDataByTag(tag) 可查找到对应的 Data 对象,并支持通过 DataModel#removeDataByTag( tag ) 删除 Data 对象。我们这边通过在 JSON 中设置 Data 对象的 tag 属性,在代码中通过 dataModel.getDataByTag( tag ) 函数来获取该 Data 对象:

 {
"c": "ht.Node",
"i": 407,
"p": {
"displayName": "抓手的结",
"parent": {
"__i": 403
},
"tag": "gripKnot",
"image": "symbols/symbol factory/垃圾处理/抓手的结.json",
"position": {
"x": -569.62125,
"y": -117.05025
},
"width": 50,
"height": 25
},
"s": {
"select.width": 0
}
},
 var gripRightPaw = dataModel.getDataByTag('gripRightPaw');
var girpLeftPaw = dataModel.getDataByTag('grapLeftPaw');
var gripKnot = dataModel.getDataByTag('gripKnot');

  展开动画

  HT 对动画封装了 ht.Default.startAnim 函数,通过设置 duration 获取动画时长, action 函数里为执行的动画属性,以及 finishFunc 动画执行后的回调函数,该案例共置8个动画,包含自驱动以及异步动画。下面举第八个动画(循环水流动)为例来理解 ht 内置动画效果:

 //循环水流动
function animation() {
var lineJson = {};
var name = '';
var speed = 20,
lastTime = Date.now();
//循环获取水流 tag,并设置初始化 shape.dash.offset 为0
for (var i = 1; i <= 9; i++ ) {
if (i != 8) {
name = 'line'+i;
lineJson[name] = 0;
}
}
ht.Default.startAnim({
duration: 5000,
action: function () {
var time = Date.now(),
   deltaTime = (time - lastTime) / 1000;
for (var tags in lineJson) {
if (tags.split('e')[1] % 2) {
lineJson[tags] += deltaTime * speed;
} else {
lineJson[tags] -= deltaTime * speed;
}
var lines = dataModel.getDataByTag(tags);
lines.setStyle('shape.dash.offset',lineJson[tags]);
}
lastTime = time
},
finishFunc: function () {
animation();
//TODO... 也可以在这里异步调用下一个动画
}
})
}

  该例首先根据已创建的循环水流(已绑定 tag 标签)通过 for 循环以及 dataModel. getDataByTag 动态获取 Data 节点,通过标签名携带的数字判断水流方向,最终使用 Data.setStyle(可以简写为 Data.s ) 设置虚线部分的偏移距离。

  上面的循环水流为例,如果 lineJson[tags] += value (定值) ,当用户放大视图时图元数量减少,会多调用几次 anim 中的 action 函数,流动速度增快,缩小同理。因此采用 value = speed * deltaTime 的解决方式,解决视图在不同缩放 zoom 的情况下播放速度不一致的问题,具体原理如下:

 //global
var lastTime = Date.now();
var distance = 0; //距离
var speed = 20; //速度
//action
ht.Default.startAnim({
duration:5000,
action:function(){
var time = Date.now();
var deltaTime = (time - lastTime) / 1000;
distance += speed * deltaTime;
lastTime = time;
},
finishFunc:function(){//TODO}
})

    ht 实现动画不仅可以使用 startAnim 来驱动,也可以采用按调度 addScheduleTask 进行实现,代码如下:

 dataModel.addScheleTask({
interval, //调度间隔
beforeAction(){}, //调度开始之前的动作
action(){}, //调度任务
afterAction(){} //调度结束之后的动作
})

  也可以使用 callLater 进行实现,ht 内置函数封装了非常多关于动画有趣且实操性强的 api ,有兴趣可以进入官网 ( https://www.hightopo.com )进行了解和学习,也可以线上申请 framework 的试用包。如果想要了解更多HT封装的动画进行操作,可以参考 https://www.cnblogs.com/xhload3d/p/9222549.html 等其他文章。

  可操作

  当然,HT 也汲取了订阅-发布模式的天然优势,通过驱动数据更改视图,更加直观地感受到数据与视图的绑定过程。以下提供2种 HT 提供的可操作界面,第一种是通过创建面板组件, HT 内部提供了包含 formPane 、borderPane、TablePane 等一系列通用面板组件,此处我们以  formPane 为例,首先在 index.html 主页面中引入 ht-form.js ,该文件封装了 formPane 面板的 api ,相关伪代码如下:

 var fp = new ht.widget.FormPane(); //创建面板对象
fp.setWidth();
fp.setHeight(); 
fp.setRowHeight(); //面板行高
fp.setPadding(); 
fp.getView().className = 'main'; //节点设置类名后可以直接在 style 中设置属性,说白了 fp.getView() 就是一个普通的 DOM 节点
fp.addRow([{ //通过 addRow 方法添加文本以及进度条等内容
id:'text',
element:'Current Speed === 20',
align:'center'
}],[0.1]);
fp.addRow([{
id:'speed',
slider:{ //进度条
min:,
max:,
value:, //当前进度值
step:,
onValueChanged(){ value改变时触发函数
var speed = fp.v('speed');
fp.v('text','Current Speed === ' + speed);
}
}
}],[0.1]);
document.body.appendChild(fp.getView());

  此时,我们只要把之前定义的 speed 指向 fp.v('speed') ,就可以简单地实现数据视图绑定:

 function animation(fp){
var lineJson = {};
var name = '';
var lastTime = Date.now();
var speed;
for (var i = 1; i <= 9; i++ ) {
if (i != 8) {
name = 'line'+i;
lineJson[name] = 0;
}
}
ht.Default.startAnim({
duration: 5000,
action: function () {
speed = fp.v('speed');
var time = Date.now(),
deltaTime = (time - lastTime) / 1000;
for (var tags in lineJson) {
if (tags.split('e')[1] % 2) {
lineJson[tags] += deltaTime * speed;
} else {
lineJson[tags] -= deltaTime * speed;
}
var lines = dataModel.getDataByTag(tags);
lines.setStyle('shape.dash.offset',lineJson[tags]);
}
lastTime = time;
},
finishFunc: function () {
animation(fp);
}
})
}

  另一种是通过 HT 的矢量图形库,矢量图形采用点、线或多边形的图形描述方式,解决了 png 、jpg 等格式图片在缩放过程中出现失真现象。创建矢量图形可以通过常规编辑器如 webstorm、webstorm 通过代码编写,也可以通过 HT-2D 编辑器直接创建图形,基本上不需要操作代码就可以简单地创建出图形,有学过 3dmax 或者 CAD 制图的同学对此应该都不陌生。在编辑器的不断完善下,内部已经有许多优秀的图标和组件案例,这边就可以直接引用一些小案例,首先需要创建一张图纸,然后直接拉取一个自制图标,类似 legend 的效果都是绘线画出来的,更改文字部分就可以直接看到效果了。

  关键的还是功能性组件,图标展示显示界面,功能性组件支持事件的触发,首先在控件里面拉取 slider 图标,然后到组件栏拉取 slider 组件,设置控件的最大值、最小值和默认值等一系列参数。

  可以得知我们即将改变的值有两个,一个是 slider ,一个是文本的值,默认20,我们给这两个 Data 对象绑定唯一标签,分别为 sliderValue 以及 textValue,先通过进度条的当前值改变文本的值:

 var sliderValue = dataModel.getDataByTag('sliderValue');
var textValue = dataModel.getDataByTag('textValue');
sliderValue.a('ht.onChange',function(){ //value改变触发事件
  textValue.a('textValue',sliderValue.a('ht.value'));
})

  然后animation拿到进度条的当前值,指向speed:

 function animation(data) {
var lineJson = {};
var name = '';
var lastTime = Date.now();
var speed;
for (var i = 1; i <= 9; i++ ) {
if (i != 8) {
name = 'line'+i;
lineJson[name] = 0;
}
}
ht.Default.startAnim({
duration: 5000,
action: function () {
speed = data.a('ht.value');
var time = Date.now(),
deltaTime = (time - lastTime) / 1000;
for (var tags in lineJson) {
if (tags.split('e')[1] % 2) {
lineJson[tags] += deltaTime * speed;
} else {
lineJson[tags] -= deltaTime * speed;
}
var lines = dataModel.getDataByTag(tags);
lines.setStyle('shape.dash.offset',lineJson[tags]);
}
lastTime = time;
},
finishFunc: function () {
animation(data);
}
})
}

  当然也可以自定义多个 slider 分别控制不同的动画,具体如何实现还是全凭需求而定。

  

  不局限于 2D 可视化场景,与 3D 相关生产环境的可视化场景模拟也有许多案例,如下:

  3D水泥工厂工艺流程:http://www.hightopo.com/demo/CementFactory/

  3D高炉炼铁工业流程:http://www.hightopo.com/demo/large-screen-puddling/

基于 HTML5 的 PID-进料系统可视化界面的更多相关文章

  1. 基于 HTML5 WebGL 的地铁站 3D 可视化系统

    前言 工业互联网,物联网,可视化等名词在我们现在信息化的大背景下已经是耳熟能详,日常生活的交通,出行,吃穿等可能都可以用信息化的方式来为我们表达,在传统的可视化监控领域,一般都是基于 Web SCAD ...

  2. 基于 HTML5 + WebGL 的地铁 3D 可视化系统

    前言 工业互联网,物联网,可视化等名词在我们现在信息化的大背景下已经是耳熟能详,日常生活的交通,出行,吃穿等可能都可以用信息化的方式来为我们表达,在传统的可视化监控领域,一般都是基于 Web SCAD ...

  3. 基于 HTML5 WebGL 的智慧楼宇可视化系统

    前言 可视化的智慧楼宇在 21 世纪是有急迫需求的,中国被世界称为"基建狂魔",全球高层建筑数量位居首位,所以对于楼宇的监控是必不可少.智慧楼宇可视化系统更多突出的是管理方面的功能 ...

  4. 基于 HTML5 WebGL 的发动机 3D 可视化系统

    前言     工业机械产品大多体积庞大.运输成本高,在参加行业展会或向海外客户销售时,如果没有实物展示,仅凭静态.简单的图片说明书介绍,无法让客户全面了解产品,不仅工作人员制作麻烦,客户看得也费力.如 ...

  5. 基于 HTML5 WebGL 的挖掘机 3D 可视化应用

    前言 在工业互联网以及物联网的影响下,人们对于机械的管理,机械的可视化,机械的操作可视化提出了更高的要求.如何在一个系统中完整的显示机械的运行情况,机械的运行轨迹,或者机械的机械动作显得尤为的重要,因 ...

  6. 基于 HTML5 WebGL 的加油站 3D 可视化监控

    前言 随着数字化,工业互联网,物联网的发展,我国加油站正向有人值守,无人操作,远程控制的方向发展,传统的人工巡查方式逐渐转变为以自动化控制为主的在线监控方式,即采用数据采集与监控系统 SCADA.SC ...

  7. 基于 HTML5 + WebGL 的无人机 3D 可视化系统

    前言 近年来,无人机的发展越发迅速,既可民用于航拍,又可军用于侦察,涉及行业广泛,也被称为“会飞的照相机”.但作为军事使用,无人机的各项性能要求更加严格.重要.本系统则是通过 Hightopo 的   ...

  8. 基于 HTML5 的工业互联网 3D 可视化应用

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

  9. 基于 HTML5 的 WebGL 3D 档案馆可视化管理系统

    前言 档案管理系统是通过建立统一的标准以规范整个文件管理,包括规范各业务系统的文件管理的完整的档案资源信息共享服务平台,主要实现档案流水化采集功能.为企事业单位的档案现代化管理,提供完整的解决方案,档 ...

随机推荐

  1. X-Admin&ABP框架开发-设置管理

    在网站开发中,设置是不可缺少的一环,如用户设置.系统设置.甚至是租户设置等.ABP对于设置的管理已经做了很好的处理,我们可以借助巨人的力量来完成我们的冒险. ABP官网地址:https://aspne ...

  2. Vue系列:滚动页面到指定位置实现

    方法1:scrollTop 滚动到某位置 方法2:scrollTo,scrollBy,scroll滚动到某位置 方法3:scrollIntoView() 实现滚动到具体某元素 需注意,上述3种方法都不 ...

  3. go 学习笔记之有意思的变量和不安分的常量

    首先希望学习 Go 语言的爱好者至少拥有其他语言的编程经验,如果是完全零基础的小白用户,本教程可能并不适合阅读或尝试阅读看看,系列笔记的目标是站在其他语言的角度学习新的语言,理解 Go 语言,进而写出 ...

  4. Romantic HDU - 2669(扩欧)

    #include<bits/stdc++.h> using namespace std; typedef long long LL; void gcd(LL a, LL b, LL &am ...

  5. python案例:实现一个函数版的名片管理系统

    本案例使用了自定义函数以及对字符串的常见操作.判断语句和循环语句等知识. 要求 必须使用自定义函数,完成对程序的模块化. 名片信息至少包括:姓名.电话.住址. 必须完成的功能:增.删.改.查.退出. ...

  6. 关于阿里云Mysql分页查询不走索引的问题

    需要修改阿里云中的MYSQL 配置参数 : eq_range_index_dive_limit 阿里云上默认是 10 , 这个参数 表示 in 查询 条件超过 10 个 就不走索引,走全表扫描.如果我 ...

  7. R-package XML 安装失败及解决方式

    安装R-package XML遇到的问题和解决方式 这个问题已经困扰了我很久很久一直找不到解决之法,后来终于找到了! Fedora 27, R 3.5.0, libxml和libxml2以及开发包均已 ...

  8. 如果有人问你 Dubbo 中注册中心工作原理,就把这篇文章给他

    注册中心作用 开篇首先想思考一个问题,没有注册中心 Dubbo 还能玩下去吗? 当然可以,只要知道服务提供者地址相关信息,消费者配置之后就可以调用.如果只有几个服务,这么玩当然没问题.但是生产服务动辄 ...

  9. 纯 CSS 实现绘制各种三角形(各种角度)

    一.前言 三角形实现原理:宽度width为0:height为0:(1)有一条横竖边(上下左右)的设置为border-方向:长度 solid red,这个画的就是底部的直线.其他边使用border-方向 ...

  10. NN入门,手把手教你用Numpy手撕NN(一)

    前言 这是一篇包含极少数学推导的NN入门文章 大概从今年4月份起就想着学一学NN,但是无奈平时时间不多,而且空闲时间都拿去做比赛或是看动漫去了,所以一拖再拖,直到这8月份才正式开始NN的学习. 这篇文 ...