整合营销服务商

电脑端+手机端+微信端=数据同步管理

免费咨询热线:

科技:基于 HTML5 WebGL 的水泥工厂数据可视化系统

如今的制造行业,基于数据进行生产策略制定与管理已经成为一种趋势,特别是 工业4.0 的浪潮下,数据战略已经成为很多制造企业的优先战略,而数据可视化以更直观的方式,帮助指导决策,成为数据分析传递信息的重要工具。通过数据可视化系统助力实现数据驱动的工业世界,为 工业4.0 提供更加灵活、敏捷、高效、个性化的数据支撑。

今天就给大家带来一个采用 Hightopo 的 HT for Web 产品实现了一个水泥工厂可视化系统。

系统预览

<script src="https://lf3-cdn-tos.bytescm.com/obj/cdn-static-resource/tt_player/tt.player.js?v=20160723"></script>

系统实现

本案例共有七个系统:

  • 数据概况 -- 展示全厂年月时间单位的各项数据概况
  • 窑系统运行 -- 用窑工艺流程动画展示窑系统实时运行状态
  • 系统运行情况 -- 用动画流程图展示整个系统运行情况
  • 生料质量控制 -- 用图表和流程图展示各种生料的配比情况
  • 材料质量控制 -- 用动画流程图展示各种材料的配比情况
  • 煤粉质量控制 -- 用图表和流程图对煤粉质量进行监控
  • 智能物流 -- 通过 3D 场景实时监控进出厂车辆,和各项原料运输情

子系统页面切换

切换不同的系统时,左侧菜单和顶部标题是不需要切换的,所以我们把需要切换的内容部分别放在不同的 Block 中,Block 类型,本身不绘制任何内容,用于作为其它节点的父节点,可以与子节点同步大小,当它隐藏或显示时,所有的节点都会跟着隐藏或显示。所以当我们切换子系统时只需要控制对应的 Block 显示隐藏,而不需要去加载切换多张图纸。


流向地图

在数据概况页面中,流向地图展示年度水泥向各地区的销售情况,这里我们用 Shape 类型绘制线段来连接源地和汇地,用流动效果表示销售关系。流动效果只需引入 HT 的 ht-flow.js 插件,即可通过简单的属性设置实现,代码如下:

// 获取线段的父节点
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 下,遍历其子节点设置样式,代码如下:

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

为了使动画看起来更顺畅,我给一些节点加上透明度动画,设置节点透明度的代码如下:

//设置节点透明度
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);
        },
    });
}


总结

工业互联网是工业发展的必经之路,我们国家是一个工业大国,正处在工业转型升级的关键时刻,面临着人工成本上升、原材料价格波动、贸易竞争日益加剧等问题,迫切需要提高效率、降低生产成本。只有坚定不移地推动工业互联网落地,加快更多企业的数字化转型和智能化改造,才有能让在全球化竞争中立于不败之地。可视化作为智能化数字化的最后一环,让复杂抽象的数据变得真正可知可感,帮助决策者发现规律,洞悉未来,为企业提速增效。

2019 我们也更新了数百个工业互联网 2D/3D 可视化案例集,在这里你能发现许多新奇的实例,也能发掘出不一样的工业互联网:《分享数百个 HT 工业互联网 2D 3D 可视化应用案例 》,更多行业应用实例可以点击“了解更多” 。

者:刘萍萍(国家高级营养师,高级健康管理师)

在有些人看来,水不能反复烧,也不能残留一部分水再烧,因为会导致亚硝酸盐、钙、镁等物质不断积累,人喝了这样的水会中毒,甚至致癌。其实,水反复烧还能喝吗?水壶里残留一部分水再烧是不是不健康?隔夜的水能喝吗?

水反复烧还能喝吗?

有些人认为,水反复烧会导致水中亚硝酸盐增多,人喝了这样的水会引发中毒甚至致癌。

饮用水里含有的亚硝酸盐,一般是由水中原有的硝酸盐转化而来的。在饮用水反复烧开后,由于高温缺氧,部分硝酸盐转化为亚硝酸盐。另外,由于水的蒸发,也会导致亚硝酸盐浓度增加[1]。

摄入含有低水平亚硝酸盐的食物可补充人体内的亚硝酸盐。但如果一次性摄入过多的亚硝酸盐,则容易引起急性中毒,使血液中具有正常携氧能力的低铁血红蛋白氧化成高铁血红蛋白,失去携氧能力,造成组织缺氧,称为高铁血红蛋白血症。

当亚硝酸盐摄入量达到0.2-0.5克的时候,可导致中毒,超过3克的时候,可致人死亡[2]。

联合国粮农组织和世界卫生组织联合食品添加剂专家委员会(JECFA)规定亚硝酸盐的每日允许摄入量为每公斤体重0-0.2毫克[2]。对于一个体重60公斤的成年人来说,相当于每天亚硝酸盐的允许摄入量为0-12毫克。

那么,反复烧开的水里有多少亚硝酸盐呢?

2007年,上海市崇明县疾病预防控制中心的一篇研究显示,对于新鲜桶装矿泉水,随着反复加热次数的增多,热水中的亚硝酸盐含量逐渐升高,在加热到52次后,亚硝酸盐的含量为2.3微克/升[3]。

2010年,衡阳师范学院的一项研究,则把桶装纯净水反复加热181次,结果水中亚硝酸盐的含量在3-4微克/升之间[4]。

不管是加热52次,还是181次,结果都小于我国包装饮用水(矿泉水除外)、矿泉水中亚硝酸盐的最高限量值5微克/升、100微克/升[5]。

更为关键的是,如果想要达到0.2克亚硝酸盐这个急性中毒的剂量,可不是随便喝几杯水就能做到的,需要一次性喝好几万升水。很显然,这是不可能的。

如果是自来水加热呢?

2011年,哈尔滨工业大学则对符合国家标准的自来水进行反复烧开5次的研究,结果并没有检测到亚硝酸盐的存在。即便人为加入一定量的亚硝酸盐,随着煮沸次数的增加,亚硝酸盐的质量也没有提高[1]。

在我们日常生活中,把饮用水反复加热几十上百次的现象并不多见,只要本身是合格的饮用水,大可不必担心亚硝酸盐含量超标导致中毒的问题。

记住那句简单粗暴的话——脱离剂量谈毒性都是耍流氓。

至于致癌也是不用担心的。因为亚硝酸盐并不是致癌物,在特定条件下生成的“亚硝胺”才是致癌物[6]。

因此,我们一般无需担心喝几杯反复烧开的水,会发生亚硝酸盐中毒,或是致癌的问题。

水壶里残留一部分水再烧是不是不健康?

有些人疑惑,如果水壶里残留一部分水再烧是不是不健康?如果是指亚硝酸盐超标的问题,那是不需要担心的,因为道理与饮水机里的水反复加热是一样的。

如果是说水质变硬,有害物质增加的问题,同样也不用太担心。

先说水质变硬的问题。水的硬度主要是水中钙、镁等矿物质的作用,如果水中矿物质多,水质就偏硬,反之则偏软。

如果本身是比较硬的水,含有较多的钙、镁,在加热煮沸后,会生成碳酸钙等不溶性物质,俗称水垢,沉淀下来,结果反而是降低水的硬度。

如果本身就是基本不含杂质的纯净水,水壶里残留一部分水再烧,虽然一定程度上会使里面的钙、镁矿物质累积,但最终同样可以生成水垢沉淀下来。

关键是,钙、镁这类矿物质本身就不是什么对人体有害的物质,喝较硬的水一定程度上还可以帮助我们补充镁、钙等矿物质。有一些研究显示,硬度较高的水可能对多种心血管疾病具有有益作用[7]。

再说有害物质增加的问题,如果本身就是合格的饮用水,只要不被有害物质污染,那么,有害物质不会无中生有,就算因为是上一次残留的部分水重新加热,导致有害物质累积,也不会太多,一方面,是因为一般带盖子烧水,蒸发掉的水不多,另一方面,是因为会加新的水进去一起烧。

另外,这个有害的前提是,有害物质总是在一次次加热中沉淀下来的,那万一有害物质可以挥发掉呢?反复加热岂不是在降低有害物质含量?

所以,水壶里残留一部分水再烧是否不健康这个问题,是不需要太担心的。

隔夜的水能喝吗?

隔夜水也是很多人担心的,担心细菌污染,担心亚硝酸盐增多。

其实,对于烧开的白开水来说,因为经过高温煮沸,水中大部分细菌已经被杀死了。如果能够合理存放,不需要担心细菌滋生的问题。当然,如果没有密封保存、温度过高等,则存在这方面的风险。

至于亚硝酸盐的问题,有研究发现煮沸后放置的隔夜水中亚硝酸盐含量为1090微克/升[8],已经超过了我国现行《GB 5749-2006 生活饮用水卫生标准》中规定的亚硝酸盐限量值1000微克/升[9]。

不过,如前面所说,亚硝酸盐摄入量至少达到0.2克时才可引起急性中毒,因此,喝一两杯隔夜水不会那么容易就亚硝酸盐中毒。

如果是包装饮用水呢?有研究测定发现5种品牌饮用水,在打开隔夜后都开始检出亚硝酸盐,分别为1.5微克/升、1.3微克/升、5.0微克/升、2.1微克/升、1.9微克/升,但结果也都没有超出我国包装饮用水(矿泉水除外)、矿泉水中亚硝酸盐的最高限量值5微克/升、100微克/升[8]。

因此,对于隔夜水,只要能合理存放,如密封保存[10]、保持温度不过高等,一般来说,是可以放心喝的。当然,煮沸后的水还是不要放太久,尽量不要放置过夜,特别是天气比较热的情况下。

参考资料:

[1] 沈倩青,张光明.饮用水反复烧开对水质的影响.环境科学与技术, 2011, 34(5):128-130

[2] 国家食品药品监督管理总局(CFDA).关于亚硝酸盐的科学解读.2016年08月18日 发布.http://www.sda.gov.cn/WS01/CL1679/163400.html

[3] 梁成可,陈华.饮水机反复加热对桶装矿泉水中亚硝酸盐含量的影响.上海预防医学, 2007, 19(7):343-3446.

[4] 邹伶俐,张雪姣.桶装纯净水在使用过程中亚硝酸盐污染的初步研究

[5] 《食品中污染物限量》(GB 27620 2017)

[6] 香港食物安全中心.食物中的硝酸盐.http://www.cfs.gov.hk/sc_chi/multimedia/multimedia_pub/multimedia_pub_fsf_25_01.html5.

[7] 马冠生 主译.世界卫生组织(WHO)饮用水中的营养素.人民卫生出版社.http://apps.who.int/iris/bitstream/handle/10665/43403/9787117242295-chi.pdf?sequence=7&ua=1

[8] 鲍若晗.饮用水中亚硝酸盐的测定与分析.粮食流通技术.2016

[9] 《生活饮用水卫生标准》(GB 5749-2006)

[10] 王凡 , 韩曦 , 王重芳.某校茶炉饮用水高亚硝酸盐状况及产生机理研究.现代预防医学.2007

图片来源:图虫创意

版权声明:本文为春雨医生原创稿件,版权归属春雨医生所有,未经授权禁止转载。

(来源:春雨医生)

教程的效果基本上都是用图层样式来完成。大致过程:先设定好文字,文字选择类似液体的字体,然后给文字添加样式做出水滴质感,再在文字周围加上一些小水滴即可

完成效果

一,选取“文件/创建”菜单,打开“创建”或Ctrl+N对话框 名称为:液体字体做,宽度:为800像素, 高度:为600像素, “分辨率”为72 , “模式”为RGB色彩的文档,见下图1

二,在图层控制面版击创建图层按钮,创建一图层一,选取工具箱渐变工具(快捷键G),在工具选项栏中设定为线性渐变,之后点按可编辑渐变,弹出渐变编辑器。双击见下图2中的A处,设定颜色RGB分别为25二,25二,232。再双击P2中的B处,设定颜色RGB分别为2二十五,2十九,184。继续按键盘Shift不放结合鼠标从上到下拖下,给渐变效果,按键盘快捷键Ctrl+D取消选区,见下图2。完成图见下图3

三,在工具箱中选取横排文字工具 ,在画面中用鼠标点击后,出现一键入文字光标,在光标后键入“pop”,在工具选项栏中设定字体为“CroissantD”,设定字体大小为“238.46点”,设定消除锯齿为“锐利”,设定字体色彩为白色,单击新建文字变形,弹出变形文字对话框,设定样式为:扇形,勾选水平,弯曲为:-10%,水平扭曲为:0%,垂直扭曲为:0%,见下图4

四,双点击pop图层进入到图层样式,分别勾选投影、内阴影、外发光、内发光、斜面与浮雕、色彩叠加、光泽、描边选项

参数设定及此时效果图如下

五,在图层样板中,选取POP图层,拷贝一pop图层拷贝,双点击pop图层进入到图层样式,分别勾选投影、内阴影、斜面与浮雕选项。见下图16

详细参数设定及此时效果图如下

六,新建新图层按钮,在工具箱中选取椭圆选项框工具,继续在工作区拉出一椭圆选项框工具外形,设定前景为白色,继续按键盘快捷键Alt+Delete填充,并按键盘Ctrl+D取消,并双点击图层进入到图层样式,分别勾选投影、内阴影、内发光、斜面与浮雕、光泽选项,见下图21

详细参数设定见下图

断重复拷贝并调节大小和地方,处理完成效果图

==============================

公众号:春树镇

研究讨论:互联网技术,php开发,网站建议,app开发,html5开发,设计,小说,电影。