前言
2018年7月,信息化部印发了《工业互联网平台建设及推广指南》和《工业互联网平台评价方法》,掀起了 工业互联网 的浪潮,并成为热词写入了报告中。同为信息发展下的产物 建筑智能化集成系统 也是兼具着信息与管理的重要体现,数据化信息通过可视化的管理系统展示出一套互联网智能优化的解决方案,而本系列文章便结合了 HT 的 2D/3D 可视化工具的运用,结合面板的数据展示和大楼建模场景的可视化管理,前面的内容已经讲解了 3D 模型和 2D 面板的融合体现,本次的内容将带您探讨 智慧楼宇管理系统、电梯监控系统 以及 停车场管理系统 的实现方式和整体系统的联合优化体现。
界面简介及效果预览
智慧楼宇管理系统优化效果
主要包括冷站、热站和中央末端智慧群控的联合作用,以及楼层智慧照明,通过清晰的动画体现出整栋大楼智慧节能运作的流程,可以通过面板详情的演示细致地介绍每个场景的作用以及串联的用处。
电梯以及楼层监控效果
可视化地实时监控电梯在楼层间的工作运行状态,并且能够准确地浏览每个电梯内的实时监控画面。
停车场管理系统监控效果
停车场作为现在楼宇监控不可缺失的一环,这里主要可以体现出实时的车位监控,通过简单的动画演示来表现出整个停车场车辆的运行状态,方便管理。
代码实现
一、 智慧楼宇管理系统优化效果的实现
在点击智慧楼宇管理系统的按钮后,场景视角会根据 moveCamera 转移到大楼正视的视角后,大楼整体呈现渐变虚化后透明展示大楼内部信息,这个动画是封装了一个 tweenColor 颜色变化的过渡效果,从一种颜色 rgba 状态上改变色值和透明度来体现视觉上的变化。在效果展示上有一种可视化浸入式查看大楼内部信息的体验,具体的 3D 模型属性的变化可参考 <HT 的 3D 手册>。
// 通过 moveCamera 改变 eye 和 center 来移动场景视角为大楼的正视面
moveCamera(g3d, [134, 399, 1617], [7, 40, 144], {
duration: 2000,
easing: t => t * t,
finishFunc: () => {
// 开启场景大楼模型的可透明为 true
this.building.eachChild(c => {
c.s({
'shape3d.transparent': true,
});
});
// 大楼模型线框的颜色变化
tweenColor(this.building, 'wf.color', 'rgba(72,149,232,1)', 'rgba(56,156,255,0.03)', {
duration: 2000,
easing: t => t
});
// 大楼模型整体染色的颜色变化
tweenColor(this.building, 'shape3d.blend', 'rgba(120,180,255,1)', 'rgba(120,180,255,0)', {
duration: 2000,
easing: t => t,
finishFunc: () => {
// 楼层设置为可见
this.floor.eachChild(c => {
setNodeVisible(c, true);
});
this.floorLighting = 1;
// 显示大楼建筑信息的动画
this.showBuilding();
}
});
}
});
颜色变化函数的实现是传入参数来改变节点的颜色属性变化值:
- node:改变颜色的图元节点;
- startColor:起始颜色的 rgba 值;
- endColor:变化后颜色的 rgba 值;
- animParams:过渡动画参数;
tweenColor(node, property, startColor, endColor, animParams) {
animParams = animParams || {};
if (!animParams.frames && !animParams.duration)
animParams.duration = 5000;
if (!animParams.easing)
animParams.easing = t => t;
startColor = ht.Default.toColorData(startColor);
endColor = ht.Default.toColorData(endColor);
const dx = endColor[0] - startColor[0];
const dy = endColor[1] - startColor[1];
const dz = endColor[2] - startColor[2];
const da = endColor[3] - startColor[3];
const postAction = animParams.postAction;
animParams.action = (v, t) => {
const x = startColor[0] + v * dx;
const y = startColor[1] + v * dy;
const z = startColor[2] + v * dz;
const a = (startColor[3] + v * da) / 255;
node.s(property, ('rgba(' + ([x, y, z, a]).join(', ')) + ')');
if (postAction) postAction(('rgba(' + ([x, y, z, a]).join(', ')) + ')');
}
return ht.Default.startAnim(animParams);
}
大楼透明化后展示内部信息,此时我们可以看到楼层的照明系统从底楼逐一向上亮起也是通过这种方法实现的。而后大楼两侧的系统介绍面板是通过改变面板的缩放属性来实现延展的效果:
// 面板显示
showPanel(data) {
ht.Default.startAnim({
duration: 1000,
easing: t => t,
action: (v,t) => {
data.setScaleX(data.getScaleX() + (1 - data.getScaleX()) * v);
}
});
}
// 面板隐藏
hidePanel(data) {
ht.Default.startAnim({
duration: 1000,
easing: t => t,
action: (v,t) => {
data.setScaleX(data.getScaleX() + (0 - data.getScaleX()) * v);
}
});
}
二、电梯以及楼层监控效果的实现
电梯是大楼日常管理必不可少的一环,而通过可视化场景便可实时对接电梯运行的情况。电梯动画效果的实现原理是判断当前电梯所处楼层的位置和下一层随机楼层的位置,来实现楼梯运行动画,本栋大楼划分设定为每层楼层高50,所以只需要得到电梯所处的高度就很容易可以判断出所处的楼层,而电梯面板也是通过这个判断来实时展示楼层的信息。
具体实现的伪代码如下:
elevatorAnimation(data) {
const g3d = this.g3d;
const tag = data.getTag();
const e = data.getElevation();
const label = data.getChildAt(0);
// 判断现在所处楼层
let now = Math.ceil(e / 50);
// 下一层楼层取1~7随机数
let next = randomNumber(1, 7);
// 根据现在的楼层和下一个楼层,判断电梯运行的范围
let range = numBetween(now, next);
this.animationElevatorMap[tag] = ht.Default.startAnim({
duration: range * 800,
easing: t => t,
action: (v, t) => {
// 电梯运行位置设定
data.setElevation(now < next ? (e + (range * 50) * v) : (e - (range * 50) * v));
// 设置电梯楼层面板显示并根据电梯位置设定
if (!label) return;
const floor = Math.ceil(data.getElevation() / 50);
if (floor === label.a('text')) return;
label.a('text', floor);
// 手动刷新电梯面板信息
g3d.invalidateShape3dCachedImage(label);
},
finishFunc: () => {
// 销毁电梯间隔动画
delete this.timeoutElevatorMap[tag];
// 执行电梯间隔动画后回调电梯运行动画
this.timeoutElevatorMap[tag] = setTimeout(() => {
this.elevatorAnimation1(data);
}, Math.floor(Math.random() * 5000) + 2000);
}
});
}
三、停车场管理系统监控效果的实现
停车场的车位信息可以直观地在 3D 场景中去实现管理,而车辆进出的可视化控制也可以用简单的管道动画去实现,实时监控车辆进出去调用这个动画就能很直观地反应停车场的实时情况。而停车场的具体实现方案是通过车辆节点的前进管道和倒退管道去调用封装的管道动画来实现车辆行驶和倒车入库的动画效果:
每个车辆的停车动画都包含着 forwardPath 和 backwardPath 两条路线,分别对应着车辆的前进路线和倒车路线,具体的行驶伪代码如下:
park(car, key = 'Path', finishFunc) {
const dm = car.dm();
const tag = car.getTag();
const forwardPath = dm.getDataByTag(tag + '_forward' + key);
const backwardPath = dm.getDataByTag(tag + '_backward' + key);
this.animationMap[tag] = move(car, forwardPath, 'forward', undefined, 24, {
pathEndFunc: () => {
this.animationMap[tag].stop();
this.animationMap[tag] = move(car, backwardPath, 'backward', undefined, undefined, {
pathEndFunc: () => {
this.animationMap[tag].stop();
delete this.animationMap[tag];
if (finishFunc) finishFunc();
return true;
}
});
return true;
}
});
}
move 是节点沿着路径平滑移动的封装函数,主要参数为:
- node:动画节点;
- path:运行路径;
- direction:节点朝向 forward | backward;
- animParams:动画参数;
通过绘制一条运行路线的管道,ht.Default.getLineCacheInfo() 得到这条管道的点位和分割信息 cache,然后管道信息通过 ht.Default.getLineLength() 得到管道的长度,并且通过 ht.Default.getLineOffset() 来获取连线或者管道指定比例的偏移信息,从而达到移动的效果,注意的是,这里还设定了 direction 来规定动画节点的朝向,主要是为了通过 node.lookAtX() 来获取节点下一个面对的朝向的位置信息,并设置节点此时的位置,从而达到节点沿着路径平滑移动的效果。
move(node, path, direction, step = 6, interval = 75, animParams) {
let cache = path.__cache__;
if (!cache)
cache = path.__cache__ = ht.Default.getLineCacheInfo(path.getPoints(), path.getSegments());
const len = ht.Default.getLineLength(cache);
animParams = animParams || {};
const face = direction === 'forward' ? 'front' : direction === 'backward' ? 'back' : direction;
let currentLen = 0;
const pathEndFunc = animParams.pathEndFunc;
const action = animParams.action;
animParams.action = (v, t) => {
if (currentLen >= len) {
// 档 pathEndFunc 返回 true 是,认为是要结束动画, 不执行后面档 action
if (pathEndFunc && pathEndFunc())
return;
}
currentLen = currentLen % len;
const offset = ht.Default.getLineOffset(cache, currentLen);
const point = offset.point;
node.lookAtX([point.x, node.getElevation(), point.z], face);
node.p3(point.x, node.getElevation(), point.z);
currentLen = currentLen + step;
if (action) action();
};
return loop(animParams.action, interval);
}
与此同时,我们还可以看到车辆行驶到车位或者离开时,车位上方的红绿灯则表示着这个车位的停放信息,是根据车辆的情况实时设定车位的状况,通过改变其信号灯 image 的 json 图标并手动刷新缓存来实现的。而缓存机制对于整体场景的流畅度是至关重要的,对于一些不必要实时刷新的面板信息,我们可以采取缓存的方式,并且在下一次更新的时候调用 Graph3dView.invalidateShape3dCachedImage(node)来手动刷新这个节点,从而大大提高了场景的性能,有关 3D 面板的属性可以参考 <HT 的 3D 手册 billboard 公告板>。
updateLight(view, light, color) {
light.s('shape3d.image', 'symbols/parking/' + color + 'Light.json');
view.invalidateShape3dCachedImage(light);
}
总结
IBMS 智能化集成系统管理结合数据信息、大楼模型以及每个系统的场景模型,完整地体现出一套系统之间的功能串联。在一栋大楼的功能上,每个子系统负责着各自信息数据的管理和操作,但是通过智能化集成管理系统的管理,便可将每部分子系统的数据信息汇总到一起去可视化 3D/2D 工具上完整地体现。在未来科技的进步下,也许不再需要亲临现场管理着整栋的日常运行,一套可视化的智能化集成管理系统就可以轻松解决日常维护的繁琐,充分的数据也可以实时地反映设备与大楼的关联信息。
2019 我们也更新了数百个工业互联网 2D/3D 可视化案例集,在这里你能发现许多新奇的实例,也能发掘出不一样的工业互联网:https://mp.weixin.qq.com/s/ZbhB6LO2kBRPrRIfHlKGQA
同时,你也可以查看更多案例及效果:https://www.hightopo.com/demos/index.html