import valveAngleConfig from '@/utils/valveAngleConfig.json' import ValveControlParam from './ValveControlParam' const appType = { web: 1, //web端 app: 2 //手机端 } const dcMsgType = { default: 0, //默认 ok: 1, //成功 err: -1 //失败 } const dcEventType = { interface: 0, // 界面操作 send: 1, // 命令控制设备 refresh: 2, // 命令刷新设备 refreshApi: 3 // 接口刷新阀门 } /** * 设备控制 */ class deviceControl { appType = null /** * @param {Object} 初始化方法 */ constructor(type, eventHandler) { this.appType = type this.eventHandler = eventHandler // this.onEventHandler({ // success: dcMsgType.ok, // message: "初始化成功" // }); } get topic() { if (this.topics.newV.length && this.topics.newV.length) { const newV = new Set(this.topics.newV) const oldV = this.topics.oldV.filter((x) => !newV.has(x)) return { oldV: oldV, newV: this.topics.newV } } else { return this.topics } } // #region 变量 /** * @param {landData} 地块信息 */ land = null /** * @param {valves} 阀门列表 */ wo = [] /** * @param {valves} 未绑定阀门列表 */ valves = [] /** * @param {valves} 是所有设备的通讯信息 */ dataObj = {} /** * @param {stations} 基站列表 */ stations = [] /** * @param {others} 其他列表 */ others = { header: null, agricultural: null, deviceMp: null } /** * @param {topics} 订阅topic */ topics = { oldV: [], newV: [] } /** * 数据筛选 */ forward = false screenKey = '' screen = { all: 0, online: 0, offline: 0, watering: 0, unwater: 0, error: 0 } // #endregion // #region 初始化数据 /** * 设置数据 * @param {Object} land 地块 * @param {Object} data 数据集合 */ setData(land, data) { this.land = land this.dataObj = data.deviceData this.setStations(data.station) //设置基站 this.setValves(data.valve) //未绑定的阀门数据 //设置设备数据(除阀门外的设备) this.setOtherDevices({ header: data.header, //首部 agricultural: data.agricultural, //农情设备 deviceMp: data.mp //墒情 }) //出水口数据 this.setWaterOutlet(land.id, data.wo).then((msg) => { if (msg) { this.onEventHandler({ success: dcMsgType.ok, message: msg }) } }) } /** * 设置阀门数据 * @param {Object} landId 地块id * @param {Object} wo 数据集合 */ setWaterOutlet(landId, wo) { const that = this let sendMsg = '' let topic = [], loadData = [] return new Promise((resolve, reject) => { if (wo && wo.length) { // 数据排序 wo.sort(commonUtil.compareDataNew('landGroup', 'branchCanalCode')) wo.forEach((item, i) => { item.isHidden = false // 隐藏 item.isOpened = false // 是否有打开的阀门 // 阀门数据设置 if (item.device) { item.dataKey = item.device.deviceCode topic.push('jsy/iot/push/' + item.device.deviceCode) that.checkValveState(item.device) //设置阀门的状态 if (item.device.disable) { if (Object.values(item.device.disable).some((value) => value === true)) { item.device.disable['show'] = true } else { item.device.disable['show'] = false } } else { item.device.disable = { show: false } } } //便利出水口设备 if (item.children && item.children.length) { item.children.sort(commonUtil.compareDataNew('outletCode', 'multiPortCode')) item.children.forEach((x, j) => { x.isHidden = false // 隐藏 // 阀门数据设置 if (x.device) { x.dataKey = x.device.deviceCode topic.push('jsy/iot/push/' + x.device.deviceCode) that.checkValveState(x.device) //设置阀门的状态 if (x.device.disable) { if (Object.values(x.device.disable).some((value) => value === true)) { x.device.disable['show'] = true } else { x.device.disable['show'] = false } } else { x.device.disable = { show: false } } } }) } }) if (that.wo && that.wo.length && landId == that.land.id) { // 更新数据 if (wo.length == that.wo.length) { for (var i = 0; i < that.wo.length; i++) { let item = that.wo[i] if (item.id == wo[i].id) { item.device = wo[i].device item.dataKey = wo[i]?.device?.deviceCode item.children = wo[i].children } that.wo[i] = item } } else { // 替换数据 that.wo = wo sendMsg = '数据变动,重新加载' } } else { // 替换数据 that.wo = wo } } else { that.wo = [] } that.addTopics(topic) that.screenDataByType() that.startRunningTimer(); // // 如果数据有效,查询相关数据 // if(that.valves.length){ // //给设备匹配时间 // this.matchTime("all"); // } resolve(sendMsg) }) } /** * 设置阀门数据 * @param {Object} data */ setValves(data) { let topic = [] if (data && data.length) { data.forEach((item) => { topic.push('jsy/iot/push/' + item.deviceCode) }) this.valves = data } else { this.valves = [] } this.addTopics(topic) } /** * 设置基站数据 * @param {Object} data */ setStations(data) { let topic = [] if (data && data.length) { data.forEach((item) => { topic.push('jsy/iot/push/' + item.deviceCode) item['hidden'] = false }) this.stations = data } else { this.stations = [] } this.addTopics(topic) } /** * 设置设备数据(除阀门外的设备) * @param {Object} data {key,date} */ setOtherDevices(data) { for (const key in data) { this.others[key] = data[key] // if (data[key] && data[key].length) { // this.others[key] = data[key]; // } else { // this.others[key] = null; // } } } /** * 添加tioic * @param {Object} topic */ addTopics(topic) { topic = [...new Set([...this.topics.newV, ...topic])] this.topics.newV = topic } // #endregion // #region 自动刷新 runningTimer = true /** * 自动刷新页面数据 */ startRunningTimer() { if (!this.runningTimer) return; // this.runningTimer = true; const that = this; setTimeout(() => { that.onEventHandler( that.land.id, dcEventType.refreshApi, null ) that.startRunningTimer(); }, 30 * 1000); } // #endregion // #region 数据筛选 /** * 判断阀门数据状态 * @param {Object} device */ checkValveState(device) { if (!device || !this.dataObj[device.deviceCode] || !this.dataObj[device.deviceCode].btnObj) { return } if (this.dataObj[device.deviceCode].refresh) { this.setRefreshCountdown(device.deviceCode) } for (const key in this.dataObj[device.deviceCode].btnObj) { if (!this.dataObj[device.deviceCode].btnObj[key]) { break } if (this.dataObj[device.deviceCode].btnObj[key]?.work) { switch (device.deviceTypeKey) { case 'butterflyValve': case 'valve': this.setCountdown(device.deviceCode, this.dataObj[device.deviceCode].btnObj[key].ct) break case 'fiveValve': this.setCountdown(`${device.deviceCode}_${key}`, this.dataObj[device.deviceCode].btnObj[key] .ct) break } } } } /** * 筛选设备数据 * isSort 是否需要排序,默认false */ screenDataByType(isSort = false) { // 如果在线就显示 不在线就隐藏 const that = this let all = 0, online = 0, offline = 0, watering = 0, unwater = 0, error = 0 let screenKey = that.screenKey let forward = that.forward that.wo.forEach((item) => { item.isHidden = false //行是否隐藏 item.isOpened = false //是否有打开的阀门 if (item.children && item.children.length) { item.children.forEach((item2) => { item2.isOpened = false let keys = [] // 状态数据统计 if (item2.device) { all++ const data = this.dataObj[item2.dataKey] if (data) { if (data.online) { online++ keys.push('online') } else { offline++ error++ keys.push('offline') keys.push('error') } if (data.valveOpen || data.valveWork) { item2.isOpened = true item.isOpened = true watering++ keys.push('watering') } else { unwater++ keys.push('unwater') } } else { offline++ unwater++ error++ keys = ['offline', 'unwater', 'error'] } } // 显示状态判断 if (screenKey == '') { item2.isHidden = false } else if (screenKey == 'all') { if (item2.device) { item2.isHidden = false } else { item2.isHidden = true } } else if (keys.includes(screenKey)) { item2.isHidden = false } else { item2.isHidden = true } }) // 如果阀门全部都不在线 就隐藏阀门组的折叠 // if (item.children.every(j => j.isHidden == true) && item.isHidden) { if (item.children.every((j) => j.isHidden == true)) { item.isHidden = true } else { item.isHidden = false } if (isSort) { if (forward) { item.children.sort((a, b) => Number(!a.isOpened) - Number(!b.isOpened)) } else { item.children.sort(commonUtil.compareDataNew('outletCode', 'multiPortCode')) } } } else { if (screenKey == '') { item.isHidden = false } else { item.isHidden = true } } }) if (isSort) { if (forward) { that.wo.sort((a, b) => Number(!a.isOpened) - Number(!b.isOpened)) } else { that.wo.sort(commonUtil.compareDataNew('landGroup', 'branchCanalCode')) } } this.screen.all = all this.screen.online = online this.screen.offline = offline this.screen.watering = watering this.screen.unwater = unwater this.screen.error = error } /** * 通过字符串筛选数据 */ screenDataByString(screenValue) { // 如果在线就显示 不在线就隐藏 const that = this that.screenKey = '' that.forward = false if (screenValue) { that.wo.forEach((item) => { if (item.children && item.children.length) { item.children.forEach((item2) => { // if (item2.showName.includes(screenValue) || item2.dataKey.includes(screenValue)) {} if (item2.showName.includes(screenValue)) { item2.isHidden = false } else { item2.isHidden = true } }) if (item.children.every((j) => j.isHidden == true)) { item.isHidden = true } else { item.isHidden = false } } }) } else { that.wo.forEach((item) => { item.isHidden = false if (item.children && item.children.length) { item.children.forEach((item2) => { item2.isHidden = false }) } }) } } /** * @param {Object} type 筛选状态:1在线 2离线 3警告 0/其他 全部 */ showType(key = '') { if (this.screenKey == key) return this.screenKey = key this.screenDataByType() } /** * 工作阀门 */ workingForward(value) { if (this.forward == value) return this.forward = value this.screenDataByType(true) } // #endregion // #region 控制 /** * 刷新设备 * @param {Object} mark * @param {Object} device */ refreshDeviceItem(mark, device) { if (!this.dbClickHandle(`refresh-${mark}`)) return if (device.deviceTypeId == 1 || ['valve', 'fiveValve', 'butterflyValve'].includes(device.deviceTypeKey)) { this.onEventHandler({ deviceCode: device.deviceCode, deviceTypeId: device.deviceTypeId }, dcEventType.refresh, device ) // if (this.dataObj[mark]) { // this.dataObj[mark].refresh = true // } else { // this.dataObj[mark] = { // online: false, //在线状态 // refresh: true, //刷新状态 // warning: [] // } // } // 设置刷新状态重置倒计时 this.setRefreshCountdown(mark) } else { // this.onEventHandler({ // deviceCode: device.deviceCode, // deviceTypeId: device.deviceTypeId, // }, dcEventType.refresh, device); // if (device?.data) { // device.data.refresh = true; // } else { // device["data"] = { // online: false, //在线状态 // refresh: true, //刷新状态 // } // } } } /** * 控制按钮事件 * @param {Object} item * @param {Object} index * @param {Object} value */ deviceControlHandle(item, index) { if (!this.dbClickHandle(`${item.dataKey}-${index}`)) return this.buildCommand(item, index) } /** * 组织命令 * @param {Object} item 当前对象 * @param {Object} index 蝶阀0关 1开,子设备索引默认都是1;三通阀为开关索引:1①开 2②开 3全开 4全关;五通阀为阀口1234对应1234口; * @param {Object} value 蝶阀:无需此参数,三通/五通:角度百分比 */ buildCommand(item, index, value = null) { let deviceTypeKey = item.device.deviceTypeKey // 配置的角度参数 const config = valveAngleConfig[deviceTypeKey] switch (item.device.deviceTypeKey) { case 'butterflyValve': const params = new ValveControlParam() params.deviceTypeId = item.device.deviceTypeId params.deviceCode = item.device.deviceCode params.valveNo = 1 //现在默认都是1; params.angleKey = config[index].key params.params = { index: index, value: value, } this.setBtnPreloading(item, config[index].key, index, value) this.onEventHandler(params.get(), dcEventType.send, item) break case 'valve': //value大于等于0或者索引为3(全开)4(全关)则直接发送命令,否则打开弹框 if ((value != null && value >= 0) || index == 3 || index == 4) { let keyIndex = null if (index == 1) { if (value == 0) { keyIndex = 0 } else if (value == 100) { keyIndex = 2 } else { keyIndex = 3 } } else if (index == 2) { if (value == 0) { keyIndex = 0 } else if (value == 100) { keyIndex = 4 } else { keyIndex = 5 } } else if (index == 3) { keyIndex = 1 } else if (index == 4) { keyIndex = 0 } const params = new ValveControlParam() params.deviceTypeId = item.device.deviceTypeId //设备类型id params.deviceCode = item.device.deviceCode //设备编码 params.valveNo = index params.angleKey = config[keyIndex].key params.anglePercent = value params.params = { index: index, value: value, } this.setBtnPreloading(item, config[keyIndex].key, index, value) this.onEventHandler(params.get(), dcEventType.send, item) } else { this.onEventHandler({ title: item.showName, extra: `${index}`, value: this.dataObj[item.dataKey].btnObj[`open${index}`].openAngle, other: { item: item, index: index } }, dcEventType.interface, item ) } break case 'fiveValve': //value大于等于0则直接发送命令,否则打开弹框 if (value != null && value >= 0) { let keyIndex = null if (value == 0) { keyIndex = 0 } else if (value == 100) { keyIndex = 1 } else { keyIndex = 2 } const params = new ValveControlParam() params.deviceTypeId = item.device.deviceTypeId //设备类型id params.deviceCode = item.device.deviceCode //设备编码 params.valveNo = index params.angleKey = config[keyIndex].key params.anglePercent = value params.params = { index: index, value: value, } this.setBtnPreloading(item, config[keyIndex].key, index, value) this.onEventHandler(params.get(), dcEventType.send, item) } else { this.onEventHandler({ title: item.showName, extra: `${index}`, value: this.dataObj[item.dataKey].btnObj[`open${index}`].openAngle, other: { item: item, index: index } }, dcEventType.interface, item ) } break default: this.onEventHandler({ success: dcMsgType.err, message: '未知的设备类型' }) break } } /** * 设置按钮的预加载状态 * @param {Object} item 当前对象 * @param {Object} angleKey 操作key * @param {Object} index 蝶阀0关 1开,子设备索引默认都是1;三通阀为开关索引:1①开 2②开 3全开 4全关;五通阀为阀口1234对应1234口; * @param {Object} value 蝶阀:无需此参数,三通/五通:角度百分比 */ setBtnPreloading(item, angleKey, index, value = null) { let deviceTypeKey = item.device.deviceTypeKey // 配置的角度参数 const config = valveAngleConfig[deviceTypeKey] let btnkey = '' switch (item.device.deviceTypeKey) { case 'butterflyValve': // 计时状态 this.clearCountdown(item.dataKey) if (angleKey == config[0].key) { this.dataObj[item.dataKey].btnObj.close.work = true this.dataObj[item.dataKey].btnObj.open.work = false } if (angleKey == config[1].key) { this.dataObj[item.dataKey].btnObj.close.work = false this.dataObj[item.dataKey].btnObj.open.work = true } break case 'valve': // 删除超时状态 this.clearCountdown(item.dataKey) if (index == 1) { if (angleKey == config[0].key) { btnkey = 'close' } else { btnkey = 'open1' } } else if (index == 2) { if (angleKey == config[0].key) { btnkey = 'close' } else { btnkey = 'open2' } } else if (index == 3) { btnkey = 'open' } else if (index == 4) { btnkey = 'close' } else { btnkey = 'close' } for (const key in this.dataObj[item.dataKey].btnObj) { if (!this.dataObj[item.dataKey].btnObj[key]) { return } if (key == btnkey) { this.dataObj[item.dataKey].btnObj[key].work = true this.dataObj[item.dataKey].btnObj[key].openAngle = value } else { this.dataObj[item.dataKey].btnObj[key].work = false } } break case 'fiveValve': btnkey = `open${index}` // 删除超时状态 this.clearCountdown(`${item.dataKey}_${btnkey}`) if (angleKey == config[0].key) { this.dataObj[item.dataKey].btnObj[btnkey].switchOn = false } else { this.dataObj[item.dataKey].btnObj[btnkey].switchOn = true } this.dataObj[item.dataKey].btnObj[btnkey].work = true this.dataObj[item.dataKey].btnObj[btnkey].openAngle = value break } } /** * 其他设备的双击事件 * @param {Object} mark * @param {Object} callBack */ othersClickHandle(mark, callBack) { if (!this.dbClickHandle(mark)) return if (typeof callBack === 'function') { callBack() } } // #endregion // #region 倒计时 refreshObj = {} //刷新的倒计时对象 countdownObj = {} //倒计时对象 timeoutObj = {} //超时对象 timingInProgress = false //倒计时状态 /** * 设置倒计时-刷新倒计时 * @param {Object} key 主键 */ setRefreshCountdown(key) { this.refreshObj[key] = 30 if (!this.timingInProgress) { this.timingInProgress = true this.countdownHandle() } } /** * 清理倒计时 * @param {Object} key 主键 */ clearRefreshCountdown(key) { if (this.refreshObj[key]) { delete this.refreshObj[key] this.dataObj[key].refresh = false } } /** * 设置倒计时-控制倒计时 * @param {Object} key 主键 * @param {Object} ct 倒计时 */ setCountdown(key, ct) { if (ct && ct != 0) { this.countdownObj[key] = parseInt(ct) if (!this.timingInProgress) { this.timingInProgress = true this.countdownHandle() } } } /** * 清理倒计时-控制倒计时 * @param {Object} key 主键 */ clearCountdown(key) { if (Object.keys(this.countdownObj).length != 0) { delete this.countdownObj[key] } if (Object.keys(this.timeoutObj).length != 0) { delete this.timeoutObj[key] } } /** * 倒计时处理 */ countdownHandle() { let refreshObj = this.refreshObj let countdownObj = this.countdownObj if (Object.keys(refreshObj).length === 0 && Object.keys(countdownObj).length === 0) { this.timingInProgress = false } else { if (Object.keys(refreshObj).length) { for (let key in refreshObj) { if (refreshObj[key] > 0) { this.refreshObj[key] = refreshObj[key] - 1 } else { delete this.refreshObj[key] this.dataObj[key].refresh = false } } } if (Object.keys(countdownObj).length) { for (let key in countdownObj) { if (countdownObj[key] > 0) { this.countdownObj[key] = countdownObj[key] - 1 } else { delete this.countdownObj[key] this.timeoutObj[key] = '超时' } } } setTimeout(() => { this.countdownHandle() }, 1000) } } // #endregion // #region 数据解析 /** * 解析数据 * @param {Object} topic * @param {Object} data */ handleMessage(topic, data) { if (!topic.includes('jsy/iot/push/')) { return } const that = this if (data.time < that.dataObj[data.deviceCode].time) { return } that.dataObj[data.deviceCode] = data if (data.btnObj) { for (const key in data.btnObj) { if (!data.btnObj[key]) { break } if (data.btnObj[key].work) { switch (data.deviceTypeKey) { case 'butterflyValve': case 'valve': that.setCountdown(data.deviceCode, data.btnObj[key].ct) break case 'fiveValve': that.setCountdown(`${data.deviceCode}_${key}`, data.btnObj[key].ct) break } } } } } /** * 按钮样式 * @param {Object} mark 数据主键 * @param {Object} key 按钮key */ // 判断按钮的状态 getBtnType(mark, key) { const obj = this.dataObj[mark] if (obj.btnObj[key].warning) { return 'warning' } if (obj.deviceTypeKey == 'valve' || obj.deviceTypeKey == 'butterflyValve') { if (obj.btnObj[key].switchOn || obj.btnObj[key].work) { if (key == 'close') { return this.appType == appType.web ? 'danger' : 'error' } else { return 'success' } } else { return 'info' } } if (obj.deviceTypeKey == 'fiveValve') { // if (obj.btnObj[key].switchOn || obj.btnObj[key].work) { // return 'success'; // } else { // return this.appType == appType.web ? 'danger' : "error"; // } if (obj.btnObj[key].switchOn) { return 'success' } else { return this.appType == appType.web ? 'danger' : 'error' } } } // #endregion // #region 双击操作 // 双击操作 doubleClick = true // 双击参数 dblClickThreshold = 500 dblClicObj = { time: 0, type: undefined } /** * 验证双击 * @param type 按钮类型 */ dbClickHandle(type) { const that = this if (!that.doubleClick) { return true } const timestamp = new Date().getTime() if (that.dblClicObj.type === type) { if (timestamp - that.dblClicObj.time < that.dblClickThreshold) { that.dblClicObj.type = undefined that.dblClicObj.time = 0 return true } else { that.dblClicObj.type = type that.dblClicObj.time = timestamp return false } } else { that.dblClicObj.type = type that.dblClicObj.time = timestamp return false } } // #endregion // #region 消息回调 //应用类型 type:0打开操作界面,1控制命令发送,2刷新命令发送 ,3接口刷新阀门数据 onEventHandler(params, type = null, item = null) { if (typeof this.eventHandler === 'function') { this.eventHandler(type, params, item) } } // #endregion } export { deviceControl, dcMsgType, dcEventType } /***************************** 公共方法 *****************************/ var commonUtil = { // 数据排序 compareDataNew(file1, file2) { return function(b, a) { if (b[file1] < a[file1]) { return -1 } else if (b[file1] > a[file1]) { return 1 } else { if (b[file2] < a[file2]) { return -1 } else if (b[file2] > a[file2]) { return 1 } else { return 0 } } } }, // 服务商 facilitator(type) { if (type) { if (type == '1') { return '移动' } else if (type == '2') { return '电信' } else if (type == '3') { return '联通' } } else { return '未知' } }, //格式化时间戳 getYMDHMS(timestamp) { let date = new Date(parseInt(timestamp)) let Year = date.getFullYear() let Moth = date.getMonth() + 1 < 10 ? '0' + (date.getMonth() + 1) : date.getMonth() + 1 let Day = date.getDate() < 10 ? '0' + date.getDate() : date.getDate() let Hour = date.getHours() < 10 ? '0' + date.getHours() : date.getHours() let Minute = date.getMinutes() < 10 ? '0' + date.getMinutes() : date.getMinutes() let Sechond = date.getSeconds() < 10 ? '0' + date.getSeconds() : date.getSeconds() let GMT = Year + '-' + Moth + '-' + Day + ' ' + Hour + ':' + Minute + ':' + Sechond return GMT }, findIndexByCode(arr, code) { if (arr.length) { var index1 = arr.findIndex((x) => x.device && x.device.deviceCode == code) if (index1 != -1) { return [index1] } else { let arrIndex = [] for (var i = 0; i < arr.length; i++) { if (arr[i].children && arr[i].children.length) { var j = arr[i].children.findIndex((x) => x.device && x.device.deviceCode == code) if (j != -1) { arrIndex = [i, j] break } } } return arrIndex } } else { return [] } } }