element报错 “t.getFullYear is not a function” ,TypeError:date.getHours is not a function

本文详细解析了在使用Element UI的日期选择器组件时遇到的类型错误问题,阐述了错误发生的原因在于初始化时未对时间数据进行有效验证,特别是在后台返回数据为null时,页面未能做出正确判断,导致TypeError异常。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

 

	<el-date-picker
			 v-model="setDate"
			 type="date"
			 format="yyyy 年 MM 月 dd 日"
			 value-format="yyyy-MM-dd"
			 placeholder="选择日期"
	 >
	 </el-date-picker>



    setDate:''

总结:类型错误导致报错,date里面的值得类型不对,应该为字符串,而非其他类型

           t.getFullYear is not a function” ,TypeError:date.getHours is not a function   注意查看类型

错误原因:初始化时未对时间进行验证,当后台数据返回为null时,页面没做判断

判空处理

``` Page({ data: { boli1_imageUrl: '', boli2_imageUrl: '', boli3_imageUrl: '' }, takePhoto(type) { const watermarkTexts = { boli1: '剥离前照片', boli2: '实施现场照片', boli3: '竣工验收现场照片' }; wx.chooseImage({ count: 1, success: res => { const tempFile = res.tempFilePaths[0]; const key = `${type}_imageUrl`; const canvasId = `watermarkCanvas_${type}`; const text = watermarkTexts[type]; if (!text) { console.error('无效的type参数:', type); return; } this.setData({ [key]: tempFile }, () => { this.addWatermark(canvasId, text); }); } }); }, // ...其他拍照事件绑定保持不变... addWatermark(canvasId, text) { const ctx = wx.createCanvasContext(canvasId); const date = new Date(); // 安全生成时间戳 function padNumber(num) { return num.toString().padStart(2, '0'); } const timestamp = `${date.getFullYear()}-${padNumber(date.getMonth()+1)}-${padNumber(date.getDate())} ${padNumber(date.getHours())}:${padNumber(date.getMinutes())}`; // 绘制水印逻辑 ctx.setFontSize(14); ctx.setFillStyle('rgba(255, 0, 0, 0.6)'); ctx.setTextBaseline('top'); // 计算文字位置 const textWidth = ctx.measureText(text).width; const timeWidth = ctx.measureText(timestamp).width; const maxWidth = Math.max(textWidth, timeWidth); // 绘制背景框 ctx.setFillStyle('rgba(255, 255, 255, 0.3)'); ctx.fillRect(280 - maxWidth - 5, 360 - 40, maxWidth + 10, 35); // 绘制文字 ctx.setFillStyle('red'); ctx.fillText(text, 280 - textWidth - 5, 360 - 35); ctx.fillText(timestamp, 280 - timeWidth - 5, 360 - 20); ctx.draw(); } });```TypeError: Cannot read property 'toString' of undefined at h3.fillText (WAServiceMainContext.js?t=wechat&s=1742780707430&v=3.7.9:1) at I (WAServiceMainContext.js?t=wechat&s=1742780707430&v=3.7.9:1) at h3.p (WAServiceMainContext.js?t=wechat&s=1742780707430&v=3.7.9:1) at li.addWatermark (boliqu.js:189) at li.<anonymous> (boliqu.js:145) at Object.o.safeCallback (WASubContext.js?t=wechat&s=1742780707430&v=3.7.9:1) at Rr.triggerSetDataCallbacks (WASubContext.js?t=wechat&s=1742780707430&v=3.7.9:1) at WASubContext.js?t=wechat&s=1742780707430&v=3.7.9:1 at pe (WASubContext.js?t=wechat&s=1742780707430&v=3.7.9:1) at de (WASubContext.js?t=wechat&s=1742780707430&v=3.7.9:1)(env: Windows,mp,1.06.2301160; lib: 3.7.9)
03-25
const axios = require('axios'); const fs = require('fs'); const path = require('path'); const logDirectory = path.join(__dirname, 'log'); const express = require('express'); const app = express(); const cors = require('cors'); app.use(express.json()); app.use(express.urlencoded({ extended: true })); app.use(cors()); const mqtt = require('mqtt'); const WebSocket = require('ws'); const ws = require('ws'); const schedule = require('node-schedule'); //测试服 const wsUrl = 'ws://192.168.1.252:8852/eam/websocket/lora'; // const wsUrl = 'ws://192.168.1.195:8081/eam/websocket/lora'; let wsClient = new ws(wsUrl); if(!fs.existsSync(logDirectory)) { fs.mkdirSync(logDirectory); } function getLogFileName(){ const date = new Date(); const year =date.getFullYear(); const month = (date.getMonth() + 1).toString().padStart(2, '0'); const day = date.getDate().toString().padStart(2, '0'); return path.join(logDirectory, `${year}-${month}-${day}.txt`); } function cleanOldLogs(){ const files = fs.readdirSync(logDirectory); const now = new Date(); files.forEach(file => { const filePath = path.join(logDirectory, file); const fileStats = fs.statSync(filePath); const fileDate = new Date(fileStats.mtime); const diffTime = Math.abs(now - fileDate); const diffDays = Math.ceil(diffTime / (1000 * 60 * 60 * 24)); if (diffDays > 15){ fs.unlinkSync(filePath); } }); } function logMessage(message) { const logFileName = getLogFileName(); const logStream = fs.createWriteStream(logFileName, { flags: 'a' }); logStream.write(message); logStream.end(); } const originalLog = console.log; const originalError = console.error; console.log = (...args) => { const message = `${new Date().toISOString()} - LOG:${args.join(' ')}\n`; logMessage(message); originalLog.apply(console, args); }; console.error = (...args) => { const message = `${new Date().toISOString()} - ERROR:${args.join(' ')}\n`; logMessage(message); originalError.apply(console, args); }; //以上为日志相关以及依赖库引用 setInterval(cleanOldLogs, 24 * 60 * 60 * 1000); //redis const redis = require('redis'); const { status } = require('express/lib/response'); const { off } = require('process'); const client = redis.createClient({ // url: 'redis://192.168.1.252:6379' url: 'redis://127.0.0.1:6379' }); client.on('error', (err) => { console.error('Redis Client Error', err); }); client.connect().then(() => { console.log('Connected to Redis'); client.select(0) .then(() => { console.log('已切换到数据库', 0); }) .catch(error => { console.error('数据库切换失败', error); }); }).catch((err) => { console.error('Failed to connect to Redis', err); }); const Redis_KEY_PATTERNS = { MODEL_DEVEUI: (custid, specid) => `LORA:MODEL:POWER:${custid}:${specid}`, MODEL_VAL: (custid, specid) => `LORA:MODEL:POWER:${custid}:${specid}:VAL` }; const modelDataStore = { deviceMap: new Map(), //key: devEUI value: {custid,specid} specDeviceMap: new Map(), //key: {custid}-{specid} value: Set<devEUI> thresholdMap: new Map(), //key: {custid}-${specid} value:{work,await} } wsClient.on('open', () => { console.log('WebSocket连接成功'); }); //监听消息事件,增加查询保存设备列表以及对应规格信号数据 wsClient.on('message', (message) => { const msg = message.toString(); if (msg.startsWith('modelPower-') || msg.startsWith('modelDevices-')){ console.log('系统更新内容:',msg); const [type, key] = msg.split('-'); const parts = key.split(':'); const custid = parts[3]; const specid = parts[4]; if (type === 'modelDevices'){ refreshModelDevices(custid, specid); } else if (type === 'modelPower'){ refreshModelThresholds(custid, specid); } return; } else if (msg !== 'lora_refresh' && msg !== '连接成功!'){ resetHours(message); } else { // console.log('收到消息:',msg); } }); // WebSocket 客户端连接断开后的回调 wsClient.on('close', () => { console.error('WebSocket连接已关闭'); reconnect(); }); // WebSocket 客户端连接错误后的回调 wsClient.on('error', (error) => { console.error('WebSocket 错误: ', error); wsClient.close(); }); const messageQueue = []; //Websocket 重连 function reconnect() { console.log('正在重连……'); if(wsClient){ wsClient.close(); }; setTimeout(() => { wsClient = new WebSocket(wsUrl); wsClient.on('open', () => { console.log('WebSocket连接成功'); while (messageQueue.length > 0) { const message = messageQueue.shift(); wsClient.send(message); } }); wsClient.on('close', () => { console.error('WebSocket连接已关闭'); reconnect(); }); wsClient.on('error', (error) => { console.error('WebSocket 错误: ', error); wsClient.close(); }); wsClient.on('message', (message) => { const msg = message.toString(); if (msg.startsWith('modelPower-') || msg.startsWith('modelDevices-')){ const [type, key] = msg.split('-'); const parts = key.split(':'); const custid = parts[3]; const specid = parts[4]; if (type === 'modelDevices'){ refreshModelDevices(custid, specid); } else if (type === 'modelPower'){ refreshModelThresholds(custid, specid); } return; } else if (msg !== 'lora_refresh' && msg !== '连接成功!'){ resetHours(message); } else { console.log('收到消息:',msg); } }); }, 10000); }; const epcCache = new Map(); function getCurrentTime() { const now = new Date(); const year = now.getFullYear(); const month = (now.getMonth() + 1).toString().padStart(2, '0'); const day = now.getDate().toString().padStart(2, '0'); const hours = now.getHours().toString().padStart(2, '0'); const minutes = now.getMinutes().toString().padStart(2, '0'); const seconds = now.getSeconds().toString().padStart(2, '0'); return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`; }; const options = { host: '123.207.3.132', port: 1883, username: 'admin', password: 'admin', }; const mqttClient = mqtt.connect(options); mqttClient.on('connect', () => { console.log('MQTT客户端已连接'); // 订阅主题 const topic = 'application/+/device/+/event/up'; mqttClient.subscribe(topic, { qos: 1 }, (err) => { if (err) { console.error('订阅失败:', err); } else { console.log(`已订阅主题: ${topic}`); } }); }); const knownDevEUIs = new Set(); const lastData = {}; let workHoursStorage = {}; let workCustid = {}; let workError = new Set(); let awaitHoursStorage = {}; let readDevices = [ { devEUI: '0080e10101010123', applicationID: '1', custid: 'GZYS' } ]; // 当客户端接收到消息时 mqttClient.on('message',handleMQTTMessage); async function handleMQTTMessage(topic, message) { const mqttMessage = JSON.parse(message); const mqttDataBase64 = mqttMessage.data; const mqttDataBytes = Buffer.from(mqttDataBase64, 'base64'); // 提取所需的字段 const { deviceName, applicationID, applicationName, devEUI, fPort } = mqttMessage; const gatewayName = mqttMessage.rxInfo[0].name; const gatewayID = mqttMessage.rxInfo[0].gatewayID; // 判断是否包含特定前缀 const containsPrefix = mqttDataBytes.toString('hex').startsWith('5a0001'); const rightData = mqttDataBytes.toString('hex').startsWith('53472000') && mqttDataBytes.toString('hex').length === 64; if (containsPrefix) { const mqttDataHex = mqttDataBytes.toString('hex'); // const epcStartIndex = 20;//从第二十位开始提取epc编号 ps:出现过一次epc编号并非在第二十位开始的情况,暂不清楚原因 const epcStartIndex = mqttDataHex.indexOf('e');//从第一个e开始提取epc编号 const epcLength = 24; const epc = (mqttDataHex.substring(epcStartIndex, epcStartIndex + epcLength)).toUpperCase();//已将字母转为大写 const mqttDataUtf8 = mqttDataBytes.toString('utf-8'); // 提取 readerSerialNumber const numberIndex = mqttDataUtf8.indexOf('N'); const numberLength = 20; const readerSerialNumber = (mqttDataUtf8.substring(numberIndex, numberIndex + numberLength).toUpperCase()).toString('UTF-8'); const epcCacheEntry = epcCache.get(epc); const currentTime = getCurrentTime(); if (epcCacheEntry && (new Date(currentTime) - new Date(epcCacheEntry.timestamp)) < 5000) { return; } // 准备要发送的数据 const backendData = { "cid": "lora", "message": "lora_rfid_inout", "custid": "GZYS", "time": getCurrentTime(), "devEUI": devEUI, "deviceName": deviceName, "readerSerialNumber": readerSerialNumber, "epc": epc }; // 将数据转换为 JSON 字符串 const mqttString = JSON.stringify(backendData); // 更新缓存中的 epc 编号和时间戳 epcCache.set(epc, { timestamp: currentTime }); } else if(rightData) { let mqttData = mqttDataBytes.toString('hex'); const openStart = 8; const openEnd = 10; const electricityStart = 12; const electricityEnd = 20; const powerStart = 20; const powerEnd = 28; const currentStart = 28; const currentEnd = 32; const voltageStart = 32; const voltageEnd = 36; const lightStart = 44; const lightEnd = 48; const USBStart = 48; const USBEnd = 56; const openHex = mqttData.substring(openStart, openEnd); const electricityHex = mqttData.substring(electricityStart, electricityEnd); const powerHex = mqttData.substring(powerStart, powerEnd); const currentHex = mqttData.substring(currentStart, currentEnd); const voltageHex = mqttData.substring(voltageStart, voltageEnd); const lightHex = mqttData.substring(lightStart,lightEnd); const USB = mqttData.substring(USBStart,USBEnd); const electricity = parseInt(electricityHex, 16) * 0.001; const power = parseInt(powerHex, 16) * 0.1; const current = parseInt(currentHex, 16) * 0.001; const voltage = parseInt(voltageHex, 16) * 0.01; const light = (parseInt(lightHex.substring(1,2), 16)).toString(2).padStart(4, '0'); const isReadDevices = readDevices.find(device => device.devEUI === devEUI && device.applicationID === applicationID ) if (isReadDevices){ const backendData = { "cid": "lora", "message": "lora_reader", "time": getCurrentTime(), "custid": isReadDevices.custid, "devEUI": devEUI, "USB ID": USB } const wsMessage = JSON.stringify(backendData); if (wsClient.readyState === WebSocket.OPEN){ console.log('读卡器:',wsMessage); } else { console.error(`心跳连接断开,读写器${devEUI}消息传递失败`); } return; } else { const deviceInfo = modelDataStore.deviceMap.get(devEUI); if (!deviceInfo){ console.error(`${devEUI}未配置规格型号`); return; } const thresholds = modelDataStore.thresholdMap.get(`${deviceInfo.custid}-${deviceInfo.specid}`); const backendData = { "cid": "lora", "message": "lora", "custid": deviceInfo.custid, "time": getCurrentTime(), "applicationID": applicationID, "applicationName": applicationName, "gatewayName": gatewayName, "gatewayID": gatewayID, "devEUI": devEUI, "deviceName": deviceName, "fPort": fPort, "data": mqttData }; console.log(`消息来自application/${applicationID}/devEUI/${devEUI},内容为${mqttData}`); // console.log(`电量: ${electricity} 度,获取数据为${electricityHex}`); // console.log(`功率: ${power} W,获取数据为${powerHex}`); // console.log(`电流: ${current} A,获取数据为${currentHex}`); // console.log(`电压: ${voltage} V,获取数据为${voltageHex}`); console.log(`警告灯: ${light},获取数据为${lightHex}`); console.log(`位置ID为${USB}`); console.log(`插座状态${openHex}`); await updateGatewayStatus(gatewayID, gatewayName); // 将数据转换为 JSON 字符串 let mqttString = JSON.stringify(backendData); const value = power > thresholds.work ? '\"work\"' : power > thresholds.await ? '\"await\"' : '\"on\"'; deviceStatus(backendData.custid,backendData.devEUI,value); if (USB !== '00000000' && USB != null) { if (value === '\"work\"') { const custid = backendData.custid; const devEUI_USB = `${backendData.devEUI}_${USB}`; if (!workHoursStorage[custid]){ workHoursStorage[custid] = {}; } if (workHoursStorage[custid][devEUI_USB]){ workHoursStorage[custid][devEUI_USB] += 10; } else { workHoursStorage[custid][devEUI_USB] = 10; } }else if (value === '\"await\"') { const custid = backendData.custid; const devEUI_USB = `${backendData.devEUI}_${USB}`; if (!awaitHoursStorage[custid]){ awaitHoursStorage[custid] = {}; } if (awaitHoursStorage[custid][devEUI_USB]){ awaitHoursStorage[custid][devEUI_USB] += 10; } else { awaitHoursStorage[custid][devEUI_USB] = 10; } } if (openHex === '00') { let params = { "deviceQueueItem": { "confirmed": true, "data": "U0cFAQE=", "devEUI": devEUI, "fPort": 2, "jsonObject": "" } } console.log(`${devEUI}加密狗已上线,允许插座通电`); sendRequests(params); } } else if ((USB === '00000000' || USB == null) && openHex === '01'){ let params = { "deviceQueueItem": { "confirmed": true, "data": "U0cFAQA=", "devEUI": devEUI, "fPort": 2, "jsonObject": "" } } console.log(`${devEUI}加密狗已离线,不允许插座通电`); sendRequests(params); } //转义字符串 function escapeString(str) { return str.replace(/\\/g, '\\\\') .replace(/"/g, '\\"'); } const dataString = "\"" + escapeString(mqttString) + "\""; const lightData = "\"" + escapeString(light) + "\""; if (!knownDevEUIs.has(`${USB}-${backendData.devEUI}`)) { knownDevEUIs.add(`${USB}-${backendData.devEUI}`); } const last = lastData[devEUI] || {}; const dataChanged = !last || last.USB !== USB; if(dataChanged || (last.light !== '0000' && light === '0000')) { lastData[devEUI] = { light: light, USB: USB, time: getCurrentTime(), custid: backendData.custid }; if(wsClient.readyState === WebSocket.OPEN) { if (!last.light || (last.light !== '0000' && light === '0000')) { client.set(`EAM:LORA:BILL:LIGHT:TYPE:${backendData.custid}_${backendData.devEUI}`,lightData); } wsClient.send(mqttString); console.log("位置改变",backendData.time); }else{ messageQueue.push(mqttString); } }else{ lastData[devEUI] = { light: light, USB: USB, time: getCurrentTime(), custid: backendData.custid }; if(light !== '0000' && wsClient.readyState === WebSocket.OPEN) { client.set(`EAM:LORA:BILL:LIGHT:TYPE:${backendData.custid}_${backendData.devEUI}`,lightData); wsClient.send(mqttString); console.log("灯光改变"); }; }; const expireTime = Date.now() + 3900000; client.ZADD(`LORA:GATEWAY:${USB}:${backendData.devEUI}`, {"score":expireTime,"value": dataString}) .catch(error => { console.error('error', error); }); } } else { let mqttData = mqttDataBytes.toString('hex'); const backendData = { 'cid': 'lora', 'message': 'lora', 'custid': 'GZYS', 'time': getCurrentTime(), 'applicationID': applicationID, 'applicationName': applicationName, 'gatewayName': gatewayName, 'gatewayID': gatewayID, 'devEUI': devEUI, 'deviceName': deviceName, 'fPort': fPort, 'data': mqttData }; console.error(`错误格式的数据来自application/${applicationID}/devEUI/${devEUI},内容为${mqttData}`); let mqttString = JSON.stringify(backendData); function escapeString(str) { return str.replace(/\\/g, '\\\\') .replace(/"/g, '\\"'); } const dataString = "\"" + escapeString(mqttString) + "\""; if (!workError.has(`${backendData.custid}-${backendData.devEUI}`)) { workError.add(`${backendData.custid}-${backendData.devEUI}`); wsClient.send(); } // const expireTime = Date.now() + 1296000000; //半个月 const expireTime = Date.now() + 24 * 60 * 60 * 1000; //一天 client.ZADD(`LORA:GATEWAY:${backendData.custid}:ERROR:${backendData.devEUI}`,{"score":expireTime,'value': dataString}) .catch(err => { console.error('保存错误日志时出现错误',err); }) } } function escapeString(str) { // return str.replace(/_/g, '\\_'); return str .replace(/\\/g, '\\\\') .replace(/"/g, '\\"') .replace(/:/g, '\\:') .replace(/;/g, '\\;'); } async function sendRequests(params) { for (let i = 0; i < 3; i++){ try { if (params.deviceQueueItem.devEUI === '0080e10101010110') { const res = await axios.post( `http://123.207.3.132:8080/api/devices/${params.deviceQueueItem.devEUI}/queue`, params, { headers: { Authorization: `Bearer ${jwt}`, } } ); console.log(`第${i+1}次触发成功`,res.status); } } catch (err){ console.error(`第${i+1}次触发失败`,err.response.data || err); } await new Promise(resolve => setTimeout(resolve, 1000)); } } setInterval(() => { Object.keys(workHoursStorage).forEach(custid => { const entries = workHoursStorage[custid]; const entriesArray = Object.keys(entries).map(devEUI_USB => { return `${devEUI_USB}:${entries[devEUI_USB]}`; }) const valueString = JSON.stringify(entriesArray.join(';')); // const value = "\"" + escapeString(valueString) + "\""; client.set(`LORA:GATEWAY:${custid}:WORKHOURS`, valueString) .catch(err => { console.error('工作时长存储错误',err); }); }); Object.keys(awaitHoursStorage).forEach(custid => { const entries = awaitHoursStorage[custid]; const entriesArray = Object.keys(entries).map(devEUI_USB => { return `${devEUI_USB}:${entries[devEUI_USB]}`; }) const valueString = JSON.stringify(entriesArray.join(';')); client.set(`LORA:GATEWAY:${custid}:AWAITHOURS`, valueString) .catch(err => { console.error('待机时长存储错误',err); }) }) }, 30000); const get = { 'cid': 'lora', 'message': 'lora_position_get_custid' }; const getString = JSON.stringify(get); const rule = new schedule.RecurrenceRule(); rule.hour = 23; rule.minute = 59; const job = schedule.scheduleJob(rule, async () => { try { wsClient.send(getString); await resetError(); await refreshAllThresholds(); } catch (err) { console.error('定时任务失败:',err); } }) async function resetError() { try { console.log('开始清理错误数据'); const errorKeys = await client.KEYS('LORA:GATEWAY:*:ERROR:*'); await Promise.all(errorKeys.map(async (key) => { try { const members = await client.zRange(key, 0, -1); const memberScores = await Promise.all(members.map(async (member) => ({ member, score: await client.zScore(key, member) }))); const toRemove = memberScores.filter(({ score }) => { const isValid = !isNaN(score) && score <= Date.now(); return isValid; }).map(({ member }) => member); if (toRemove.length > 0){ await client.zRem(key, toRemove); }else{ console.log(`${key}无过期数据`); } } catch (err) { console.error(`处理${key}失败:`,err); } })) } catch (err) { console.error('错误处理失败:',err); } } async function resetHours(message){ workHoursStorage = {}; awaitHoursStorage = {}; workCustid = message.toString().split(',').filter(element => element !== ''); console.log('已经调用resetHorus函数,获取到workCustid:',workCustid); await Promise.all( workCustid.map(custid => { const key1 = `LORA:GATEWAY:${custid}:WORKHOURS`; return client.get(key1) .then(reply => { return client.set(key1, ' ') .then(reply => { console.log('工作时长已重置',reply); }) }) .catch(err => { console.error('key1处理失败:', custid, err); }); }) ); await Promise.all(workCustid.map(custid => { const key2 = `LORA:GATEWAY:${custid}:AWAITHOURS`; return client.get(key2) .then(reply => { return client.set(key2, ' ') .then(reply => { console.log('待机时长已重置',reply); }) }) .catch(err => { console.error('key2处理失败:', custid, err); }); })) }; function deviceStatus(custid,devEUI,value){ if(value === '\"off\"'){ client.del(`EAM:LORA:BILL:LIGHT:TYPE:${custid}_${devEUI}`); client.setEx(`EAM:LORA:BILL:TYPE:${custid}_${devEUI}`,1800,value); }else{ client.setEx(`EAM:LORA:BILL:TYPE:${custid}_${devEUI}`,60,value); } } function checkAndClearOldData() { const currentTime = Date.now(); for (const devEUI in lastData) { if (currentTime - new Date(lastData[devEUI].time).getTime() > 60000) { deviceStatus(lastData[devEUI].custid,devEUI,'\"off\"'); const lightData = "\"" + '0000' + "\""; client.set(`EAM:LORA:BILL:LIGHT:TYPE:${lastData[devEUI].custid}_${devEUI}`, lightData); delete lastData[devEUI]; console.log(`设备 ${devEUI} 的状态已被删除,因为超过一分钟没有接收到数据。`); } }; //定期清理epcCache中的内容 // epcCache.forEach((value, key) => { // if (currentTime - value.timestamp > 5000) epcCache.delete(key); // }); } // 设置定时器,每分钟调用一次checkAndClearOldData函数 setInterval(checkAndClearOldData, 60000); async function refreshModelDevices(custid, specid) { try { const devices = await client.get(Redis_KEY_PATTERNS.MODEL_DEVEUI(custid, specid)); const validDevices = (devices || '').split(',').map(d => d.replace(/["']/g, '').trim()).filter(d => d !== ''); updateModelDevices(custid, specid, validDevices); console.log(`[设备更新] ${custid}-${specid} 完成,设备数量: ${validDevices.length}`); } catch (err) { console.error(`[设备更新] ${custid}-${specid} 失败:`, err.message); } } async function refreshModelThresholds(custid, specid) { try { const thresholdsVal = await client.get(Redis_KEY_PATTERNS.MODEL_VAL(custid, specid)); if (thresholdsVal){ const cleanedVal = thresholdsVal.replace(/["']/g, '').trim(); const parts = cleanedVal.split(',').map(s => s.trim()).filter(s => s !== ''); if (parts.length !== 3){ throw new Error(`非法阈值格式:${thresholdsVal}`); } const parsed = parts.map((s, idx) => { const num = parseFloat(s); return num; }); [, standby, work] = parsed; } updateModelThresholds(custid, specid, { standby, work }); console.log(`[阈值更新] ${custid}-${specid} 完成,新阈值: standby=${standby}, work=${work}`); } catch (err) { console.error(`[阈值更新] ${custid}-${specid} 失败:`, err.message); } } function updateModelDevices(custid, specid, newDevices) { const specKey = `${custid}-${specid}`; const oldDevices = modelDataStore.specDeviceMap.get(specKey) || new Set(); oldDevices.forEach(devEUI => { if (!newDevices.includes(devEUI)){ modelDataStore.deviceMap.delete(devEUI); } }); newDevices.forEach(devEUI => { if(!modelDataStore.deviceMap.has(devEUI)){ modelDataStore.deviceMap.set(devEUI, { custid, specid }); } }); modelDataStore.specDeviceMap.set(specKey, new Set(newDevices)); } function updateModelThresholds(custid, specid, thresholds) { const specKey = `${custid}-${specid}`; modelDataStore.thresholdMap.set(specKey, { await: thresholds.standby, work: thresholds.work }); } async function refreshAllThresholds() { try { const deviceKeys = await client.KEYS(Redis_KEY_PATTERNS.MODEL_DEVEUI('*','*')); const thresholdKyes = await client.KEYS(Redis_KEY_PATTERNS.MODEL_VAL('*','*')); await Promise.all(deviceKeys.map(key => { const [,,, custid, specid] = key.split(':'); return refreshModelDevices(custid, specid); })); await Promise.all(thresholdKyes.map(key => { const [,,, custid, specid] = key.split(':'); return refreshModelThresholds(custid, specid); })); // console.log('全量:',modelDataStore); } catch (err) { console.error('全量阈值同步失败:',err); } } refreshAllThresholds(); //设备上传数据设置到期删除 setInterval(() => { const currentTime = Date.now(); knownDevEUIs.forEach((key) => { const [USB, devEUI] = key.split('-'); const zSetName = `LORA:GATEWAY:${USB}:${devEUI}`; client.zRange(zSetName, "0", "-1","WITHSCORES") .then(members => { Promise.all(members.map(member => client.zScore(zSetName, member))) .then(scores => { scores.forEach((score, index) => { if (score <= currentTime) { client.zRem(zSetName, members[index]) .catch(err => { console.error('移除成员失败:', err); }); } }); }) .catch(err => { console.error('获取分数失败:', err); }); }) .catch(err => { console.error('获取数据失败:', err); }); }); }, 300000); // 当客户端断开连接时 mqttClient.on('close', () => { console.error('MQTT客户端已断开连接'); mqttReconnect(); }); function mqttReconnect() { console.log('MQTT重连中'); if (mqttClient){ mqttClient.end(true); }; setTimeout(() => { mqttClient = mqtt.connect(options); mqttClient.on('connect', () => { const topic = 'application/+/device/+/event/up'; mqttClient.subscribe(topic, { qos: 1 }, (err) => { if (err) { console.error('订阅失败:',err); } else { console.log('已订阅主题:',topic); } }) }); mqttClient.on('message', handleMQTTMessage); mqttClient.on('close', () => { console.error('MQTT客户端已断开连接'); mqttReconnect(); }); mqttClient.on('error', (err) => { console.error('MQTT客户端发生错误:', err); mqttClient.end(true); }); }, 5000); } // 当客户端发生错误时 mqttClient.on('error', (err) => { console.error('MQTT客户端发生错误:', err); mqttClient.end(true); }); let jwt = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhcGlfa2V5X2lkIjoiZjA5ZjViMWYtYTFjMy00NjZkLTkyNjItOGE1YTkzYTY1NTU5IiwiYXVkIjoiYXMiLCJpc3MiOiJhcyIsIm5iZiI6MTczMjU5MTM5Niwic3ViIjoiYXBpX2tleSJ9.32w2IsaBTTM8nZuKHxwwXJwT-mAeR2B5ubkDzMgU1XA'; let offGateways = []; //从redis中获取离线网关的名单 function loadGateways(){ client.keys(`LORA:GATEWAYSTATUS:*:*`).then(reply => { offGateways = []; reply.forEach(key => { const parts = key.split(':'); const organizationName = parts[2]; const gatewayID = parts[3]; offGateways.push({ organizationName, gatewayID }); }); }).catch(err => { console.error("获取离线网关名单时出错",err); }); } //获取绑定组织名称和ID function getOrgID(){ axios.get(`http://123.207.3.132:8080/api/organizations?limit=99999`, { headers: { Authorization: jwt, } }).then(response => { const orgMap = new Map(); response.data.result.forEach(org => { orgMap.set(org.id, org.name); }); checkGatewayStatus([...orgMap.keys()], orgMap); }) .catch(err => { console.error("获取ID出现错误",err) }) }; //网关工作情况 function checkGatewayStatus(organizationIDs,orgIdNameMap) { const promises = organizationIDs.map(async orgID => { try { const response = await axios.get(`http://123.207.3.132:8080/api/gateways?limit=99999&offset=0&organizationID=${orgID}`, { headers: { Authorization: jwt, } }); const rawOrgName = orgIdNameMap.get(orgID); const safeOrgName = rawOrgName.replace(/[^a-zA-Z0-9]/g, '_'); response.data.result.forEach(gateway => { const gatewayID = gateway.id; const redisKey = `LORA:GATEWAYSTATUS:${safeOrgName}:${gatewayID}`; const lastSeenAt = gateway.lastSeenAt ? new Date(gateway.lastSeenAt).getTime() : null; const isOffline = lastSeenAt && (Date.now() - lastSeenAt) > 32000; if (isOffline){ const exists = offGateways.some(gw => gw.gatewayID === gatewayID); if (!exists){ offGateways.push({ organizationName: rawOrgName, gatewayID }); } client.set(redisKey, '\"off\"').then(() => console.log(`[${safeOrgName}] 网关${gatewayID} 离线状态已记录`)) .catch(err => console.error(`[${safeOrgName}] Redis写入失败`, err)); } else { const index = offGateways.findIndex(gw => gw.gatewayID === gatewayID); if (index !== -1){ offGateways.splice(index, 1); } client.del(redisKey).then(() => console.log(`[${safeOrgName}] 网关${gatewayID} 在线状态已更新`)) .catch(err => console.error(`[${safeOrgName}] Redis删除失败`, err)); } }); } catch (error) { console.error("checkDevicesStatus函数出错", error); } }); Promise.all(promises).then(() => { console.log("离线网关列表",JSON.stringify(offGateways)); }); }; async function updateGatewayStatus(gatewayID, gatewayName) { const index = offGateways.findIndex(gw => gw.gatewayID === gatewayID); if (index !== -1){ const { organizationName } = offGateways[index]; offGateways.splice(index, 1); const safeOrgName = organizationName.replace(/[^a-zA-Z0-9]/g, '-'); const redisKey = `LORA:GATEWAYSTATUS:${safeOrgName}:${gatewayID}`; try { await client.del(redisKey); console.log(`[实时更新] 网关 ${gatewayName} (${gatewayID}) 重新上线`); } catch (err) { console.error(`[${organizationName}] Redis删除失败`, err); } } } loadGateways(); getOrgID(); setInterval(getOrgID, 15000); app.get('/health', async (req, res) => { const allClients = { redis: { connected: client.isReady, name: 'Redis数据库' }, mqtt: { connected: mqttClient.connected, name: 'MQTT消息队列' }, websocket: { connected: wsClient.readyState === WebSocket.OPEN, name: 'WebSocket连接' }, }; const statusReport = { status: 'healthy', allClients: {}, issues: [], timestamp: Date.now() }; let allHealthy = true; Object.entries(allClients).forEach(([key, client]) => { const isConnected = client.connected; statusReport.allClients[key] = isConnected ? 'connected' : 'disconnected'; if (!isConnected) { statusReport.issues.push({ clients: key, name: client.name, description: `${client.name}连接失败` }); allHealthy = false; } }); statusReport.status = allHealthy ? 'healthy' : 'unhealthy'; res.status(allHealthy ? 200 : 503).json(statusReport); }); app.listen(3000); process.on('unhandledRejection', (reason, promise) => { console.error('未处理的拒绝:', reason); }) process.on('uncaughtException', (err) => { console.error('未捕获的异常:', err); }) process.on('SIGINT', () => { console.log('程序正在关闭...'); mqttClient.end(); client.quit(); wsClient.close(); process.exit(); }); 遍历我的代码,查看哪里会发生修改const变量的情况,并且解释一下MQTT客户端发生错误: Error: read ECONNRESET,这是连接超时导致的错误吗
最新发布
07-18