import valveAngleConfig from "@/utils/valveAngleConfig.json" import ValveControlParam from "./ValveControlParam" const dcMsgType = { default: 0, //默认 ok: 1, //成功 err: -1, //失败 } const dcEventType = { interface: 0, // 界面操作 send: 1, // 命令控制设备 refresh: 2, // 命令刷新设备 refreshApi: 3, // 接口刷新阀门 } /** * 设备控制 */ class deviceControl { /** * @param {Object} 初始化方法 */ constructor(eventHandler) { 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.setWaterOutlet(land.id, data.wo); //出水口数据 this.setValves(data.valve); //未绑定的阀门数据 this.setStations(data.station); //设置基站 let obj = { header: data.header, //首部 agricultural: data.agricultural, //农情设备 deviceMp: data.mp, //墒情 } this.setOtherDevices(obj); //设置设备数据(除阀门外的设备) } /** * 设置阀门数据 * @param {Object} landId 地块id * @param {Object} wo 数据集合 */ setWaterOutlet (landId, wo) { const that = this; let topic = [], loadData = []; 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.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 (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; that.onEventHandler({ success: dcMsgType.ok, message: "数据变动,重新加载" }); } } else { // 替换数据 that.wo = wo; } } else { that.wo = []; } that.addTopics(topic); that.screenDataByType(); // that.startRunningTimer(); // // 如果数据有效,查询相关数据 // if(that.valves.length){ // //给设备匹配时间 // this.matchTime("all"); // } } /** * 设置阀门数据 * @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 = false; /** * 自动刷新页面数据 */ startRunningTimer () { // if (this.runningTimer) return; // const that = this; // that.runningTimer = true; // setTimeout(() => { // that.onEventHandler(that.eventType.refreshApi, that.land.item.id) // }, 20 * 1000); } // #endregion // #region 数据筛选 /** * 判断阀门数据状态 * @param {Object} device */ checkValveState (device) { if (!device || !this.dataObj[device.deviceCode] || !this.dataObj[device.deviceCode].btnObj) { return; } 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: [] } } } 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; 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; 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; 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 倒计时 countdownObj = {} //倒计时对象 timeoutObj = {} //超时对象 timingInProgress = 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 countdownObj = this.countdownObj; if (Object.keys(countdownObj).length === 0) { this.timingInProgress = false; } else { 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) { console.error("data:", data); 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} appType 应用类型 1web 2手机 * @param {Object} mark 数据主键 * @param {Object} key 按钮key */ // 判断按钮的状态 getBtnType (appType, 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 appType == 1 ? '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 appType == 1 ? '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 []; } } }