线框模式:
机舱视角:
发电工艺:
整理思路:
场景部分:
这里把 3D 当做背景嵌套在 2D 场景中.
这样在初始化图纸的时候, 直接反序列化 2D 图纸即可.
事件部分:
2D 图纸中有很多的按钮, 通过它们来控制 3D 中的一些动画.
实现思路是在反序列化图纸的时候把 2D,3D 的 模型和视图对象挂载到 Windows 上, 这样在不同的场景中都可以获取到相应的数据模型.
环境部分:
风速, 风向, 变桨角度这些会在 2D,3D 中所表现, 所以可以把他们放到数据池里面, 方便管理.
具体代码实现:
场景搭建: 上面说了, 我们把 3D 当做背景嵌套在 2D 中, 所以只需要序列化 2D 即可, 里面需要进行背景判断的部分代码.
相关伪代码:
- graphView.deserialize('displays/demo / 风力发电机 / 风力发电机结构查看. json', function (JSON, dm, gv, datas) {
- if (JSON.title) document.title = JSON.title
- if (JSON.a['json.background']) {
- var bgJSON = JSON.a['json.background']
- if (bgJSON.indexOf('displays') === 0) {
- var bgGv = new ht.graph.GraphView()
- bgGv.deserialize(bgJSON)
- bgGv.addToDOM()
- graphView.addToDOM(bgGv.getView())
- }
- else if (bgJSON.indexOf('scenes') === 0) {
- var bgG3d = new ht.graph3d.Graph3dView()
- bgG3d.deserialize(bgJSON)
- bgG3d.addToDOM()
- graphView.addToDOM(bgG3d.getView())
- }
- graphView.handleScroll = function () { }
- }
- })
模拟风速: 每隔 30s, 随机产生一个值, 当做风速值.
相关伪代码:
- // 模拟风速
- mockWindSpeed() {
- return 8 + Math.random() * 12
- }
数据统计: 每隔 30s, 随机变换.
效果:
相关伪代码:
- // 指针和扇叶旋转的角度 进行变化
- var oldPointerValue = pitchSystem.a('pointer') || 0
- // 风机扇叶和变桨系统的旋转角度
- var newRotateAngular = (this.windSpeed - 8) * 7.5 * translateAngularRadian.radian
- var addPointerValue = newRotateAngular - oldPointerValue
- var oldWindSpeedClip = environmentalData.a('windSpeedClip') || 0
- var newWindSpeedClip = (this.windSpeed - 8) / 12
- var addWindSpeedClip = newWindSpeedClip - oldWindSpeedClip
- var anim = {
- duration: 1e3,
- easing: (v) => {
- return v * v
- },
- action: (v) => {
- var windSpeed = Number(this.windSpeed.toFixed(2))
- var Max = Number(MaxValue.toFixed(2))
- var average = Number(Aver.toFixed(2))
- var windSpeedClip = oldWindSpeedClip + (addWindSpeedClip * v)
- // 设置发电参数随机数据
- generator.a({ windSpeed })
- // 设置环境监测随机数据
- environmentalData.a({ windSpeed, windSpeedClip })
- // 设置统计参数随机数据
- statisticalParam.a({ average, Max, windSpeed })
- // 设置变航系统的指针角度
- pitchSystem.a('pointer', oldPointerValue + (addPointerValue * v))
- }
- }
- ht.Default.startAnim(anim)
这里涉及到角度和弧度的转换. 1° = Math.PI / 180°,1rad = 180° / Math.PI, 因为场景中使用的是弧度制, 所以需要把随机出的角度值转换成弧度.
这里解释下代码, 先获取到当前的值. 然后在加上 随机值 - 当前值. 比如当前值为 16, 随机出的数值有两种情况, 1: 比当前值大. 2: 比当前值小.
如果比当前值大的话, 比如 18, 那么就是这样 16 + (18 - 16) * v ( easing 函数运算后的值)
如果比当前值小的话, 比如 13, 那么就是这样 16 + (13 - 16) * v ( easing 函数运算后的值)
这样当随机的时候, 就会从当前值平滑的改变到目标值.
数据统计: 每隔 30s, 监测当前风机的故障信息.
效果:
这里使用了 table.JSON 文件, 通过修改 ht.dataSource 属性添加实时信息.
相关伪代码:
- var checkInternals = () => {
- /**
- * 故障信息
- * 变桨系统 主轴 偏航系统 齿轮箱 油冷装置 发电机 风冷装置
- */
- var FailureStatus = {
- // 正常状态
- status1: new Map([
- [0, ['舱内温度正常, 变桨角度正常.', 'i10']],
- [1, ['舱内温度正常, 主轴转速正常.', 'i9']],
- [2, ['偏航系统精确.', 'i8']],
- [3, ['齿轮箱温度正常.', 'i1']],
- [4, ['油冷装置温控表正常.', 'i3']],
- [5, ['发电机功率正常.', 'i5']],
- [6, ['风冷装置正常.', 'i2']]
- ]),
- // 异常状态
- status2: new Map([
- [0, ['变桨角度异常.', 'i10']],
- [1, ['主轴转速偏高.', 'i9']],
- [2, ['偏航系统出现偏移.', 'i8']],
- [3, ['齿轮箱温度偏高.', 'i1']],
- [4, ['油冷装置内积尘过多.', 'i3']],
- [5, ['发电机电流过大.', 'i5']],
- [6, ['风冷装置散热不足.', 'i2']]
- ]),
- }
- // 返回设备正常的状况 status 1: 正常 2: 不正常
- var mockQquipmentFailure = (status) => {
- var { rangeRandom } = common
- var index = rangeRandom(7)
- // 返回随机出来的设备情况
- return FailureStatus[`status${status}`].get(index)
- }
- var info = randomInfo[0]
- var targetTag = randomInfo[1]
- this.tableArr = table.a('ht.dataSource')
- var currentTimeFormat = DateUtil.formatHourTime(new Date())
- // 默认是正常 如果找到故障关键字的话 赋值为 异常
- var status = 'normal'
- var time = 0
- this.tableArr.push({ status, info, time: currentTimeFormat })
- }
我们需要两个 Map 数组方便进行取值操作. 一个是正常信息数组, 一个是异常信息数组. 利用随机值当做一个索引, 然后取到相对应的状态信息, 添加到 table 中.
如果当前的 status 为 normal, 说明是正常信息, 否则为异常信息. 异常信息的话就可以通过 table.JSON 的渲染回调函数 "drawCell": function(g, text, rect, option) { } 来修改它的颜色, 使其高亮.
偏航系统: 风机转动的过程中, 随着风的位置的不同, 通过偏航系统改变方向.
效果:
相关伪代码:
- /**
- * 随机偏航系统
- * @param { * }
- */
- randomYawSystem() {
- var { dm } = this
- var { d2d } = Windows
- var { rangeRandom } = common
- var poll = () => {
- // 随机数 30 - 50
- var random = 30 + rangeRandom(20)
- var cabin = dm.getDataByTag('cabin')
- // 将角度度换算成弧度 然后乘以随机数 实现随机风向
- var randomDegrees = translateAngularRadian.radian * random
- var defaultDegress = translateAngularRadian.radian * 180
- ht.Default.startAnim({
- duration: 1e3,
- action: (v) => {
- var oldValue = cabin.getRotationY()
- var newValue = randomDegrees
- var addValue = newValue - oldValue
- cabin.setRotationY(oldValue + addValue * v)
- }
- })
- }
- }
上面讲到过角度弧度转换, 这里先将随机出的角度转换成弧度, 然后赋值, 进行旋转.
变桨系统: 风速的变化影响风机扇叶的角度.
效果:
相关伪代码:
- var old3Value = whiteShell3Line.getRotationX()
- var old4Value = whiteShell4Line.getRotationX()
- var old5Value = whiteShell5Line.getRotationX()
- // 指针和扇叶旋转的角度 进行变化
- var oldPointerValue = pitchSystem.a('pointer') || 0
- // 风机扇叶和变桨系统的旋转角度
- var newRotateAngular = (this.windSpeed - 8) * 7.5 * translateAngularRadian.radian
- var addPointerValue = newRotateAngular - oldPointerValue
- whiteShell3Line.setRotationX(old3Value + ((newRotateAngular - oldPointerValue)))
- whiteShell4Line.setRotationX(old4Value + ((newRotateAngular - oldPointerValue)))
- whiteShell5Line.setRotationX(old5Value + ((newRotateAngular - oldPointerValue)))
先获取到每个扇叶当前的 X 轴旋转值, 再获取到需要旋转的角度值, 进行赋值.
风机启停: 风机的启动和停止
相关伪代码:
- var fanWireframe = d3d.getDataByTag('fanWireframe')
- fanWireframe.setRotationMode('zxy')
- this.allAnimManage = new Map([['fanRotate', null]])
- var fanRotating = (easeType) => {
- anim = ht.Default.startAnim({
- duration: 5e3,
- easing: (t) => easeIn(t),
- action: (v) => {
- fanWireframe.setRotationZ(fanWireframe.getRotationZ() + speed)
- // 风机轮毂旋转
- if (fanWireframe.getRotationZ() <= -6.28) {
- fanWireframe.setRotationZ(0)
- }
- // 风机扇叶 uv 偏移
- for (let i = 1; i <17; i++) {
- var node = d3d.getDataByTag(`q${i}`)
- node.s('shape3d.uv.offset', fanOffsetData(v)[i - 1])
- }
- // 暂停命令
- if (isStop) {
- stopFanRotate(fanWireframe.getRotationZ())
- anim.pause()
- anim = null
- }
- },
- finishFunc: () => {
- fanRotating(false)
- }
- })
- this.allAnimManage.set('fanRotate', anim)
- }
因为可以启动和停止, 那么我们就可以通过控制 ht.Default.startAnim() 的返回对象的 resume 和 pause 来达到效果.
所以我把风机旋转的动画添加到了全局对象中, 方便进行调用.
setRotationMode('zxy') 方法是设置三维旋转模式, 顺序是 z -> x -> y, 先进行 z 轴旋转, 再进行 x 轴旋转, 最后进行 y 轴旋转. 设置目的是为了避免坐标轴受外部旋转的影响.
风向: 根据风的角度, 判断当前是什么位置的风.
效果:
相关伪代码:
- // 判断风向
- var windDirection = (rotate) => {
- let direction
- switch (true) {
- case rotate === 0:
- direction = '南'
- break
- case rotate === 90:
- direction = '东'
- break
- case rotate === 180:
- direction = '北'
- break
- case rotate === 240:
- direction = '西'
- break
- case rotate> 0 && rotate <90:
- direction = '东南'
- break
- case rotate> 90 && rotate <180:
- direction = '东北'
- break
- case rotate> 180 && rotate <270:
- direction = '西北'
- break
- case rotate> 270 && rotate < 360:
- direction = '西南'
- break
- default:
- direction = '没有找到风向'
- }
- return direction
- }
- // 判断是哪个方向
- var angular = randomDegrees * translateAngularRadian.angular
- var direction = windDirection(angular)
将罗盘的指针角度放到 switch 进行判断, 如果找到对应的风向就返回.
总结
风力发电是一个工业互联网的典型例子, 我们可以通过对风机模型或者监测数据进行分析, 可以减轻我们工作复杂程度, 帮助我们快速了解发电内部结构及发电功能.
HT 能做的东西远远不止于此, 这需要我们丰富的想象力以及自身过硬的技术. 我希望可以通过这篇文章向大家传递一种能量, 让大家更有兴趣, 迸发更多新鲜的想法, 去做更多好玩的东西.
来源: https://www.cnblogs.com/xhload3d/p/12131708.html