Map<number | string, DeviceInfo>

时间: 2025-09-03 19:15:55 AIGC 浏览: 7
`Map<number | string, DeviceInfo>` 是 TypeScript 中的泛型类型定义。在 TypeScript 里,泛型是一种允许创建可重用组件的工具,它可以让组件支持多种数据类型。 ### 语法解释 - `Map` 是 TypeScript 内置的一种数据结构,它存储键值对,其中每个键都是唯一的。 - `<number | string, DeviceInfo>` 是泛型参数部分。`number | string` 表示键的类型可以是数字或者字符串,这是一个联合类型,意味着键既可以是数字,也可以是字符串;`DeviceInfo` 是值的类型,表明这个 `Map` 中存储的值必须是 `DeviceInfo` 类型。 ### 使用方法 以下是一个使用 `Map<number | string, DeviceInfo>` 的示例代码: ```typescript // 假设 DeviceInfo 是一个接口 interface DeviceInfo { name: string; model: string; } // 创建一个 Map 实例 const deviceMap: Map<number | string, DeviceInfo> = new Map(); // 添加元素 const device1: DeviceInfo = { name: 'Device A', model: 'Model 1' }; deviceMap.set(1, device1); const device2: DeviceInfo = { name: 'Device B', model: 'Model 2' }; deviceMap.set('device2', device2); // 获取元素 const retrievedDevice1 = deviceMap.get(1); const retrievedDevice2 = deviceMap.get('device2'); // 遍历 Map deviceMap.forEach((value, key) => { console.log(`Key: ${key}, Name: ${value.name}, Model: ${value.model}`); }); ``` ### 应用场景 - **设备管理系统**:在一个设备管理系统中,可以使用这种类型的 `Map` 来存储设备信息。设备的标识可能是设备编号(数字),也可能是设备的唯一名称(字符串),而值则是设备的详细信息,如设备名称、型号等。 - **配置管理**:在配置管理中,配置项的键可能既有数字类型的 ID,也有字符串类型的名称,值则是对应的配置信息。
阅读全文

相关推荐

static Future<Map<String,dynamic>> getDeviceInfo() async {//}String> getDeviceInfo() async { DeviceInfoPlugin deviceInfo = DeviceInfoPlugin(); // String deviceInfoString = ''; Map<String,String> deviceMap = {}; try { if (Platform.isAndroid) { AndroidDeviceInfo androidInfo = await deviceInfo.androidInfo; // deviceInfoString = '${androidInfo.brand} ${androidInfo.model}'; deviceMap["brand"] = androidInfo.brand; deviceMap["model"] = androidInfo.model; deviceMap["version"] = androidInfo.version; } else if (Platform.isIOS) { IosDeviceInfo iosInfo = await deviceInfo.iosInfo; // deviceInfoString = '${iosInfo.name} ${iosInfo.model}'; deviceMap["brand"] = iosInfo.name; deviceMap["model"] = iosInfo.model; deviceMap["version"] = iosInfo.systemVersion; } else if (Platform.isWindows) { WindowsDeviceInfo windowsInfo = await deviceInfo.windowsInfo; // deviceInfoString = 'Windows ${windowsInfo.computerName}'; deviceMap["brand"] = "Windows"; deviceMap["model"] = windowsInfo.computerName; } else if (Platform.isMacOS) { MacOsDeviceInfo macOsInfo = await deviceInfo.macOsInfo; // deviceInfoString = 'MacOS ${macOsInfo.model}'; deviceMap["brand"] = "MacOs"; deviceMap["model"] = macOsInfo.model; } else if (Platform.isLinux) { LinuxDeviceInfo linuxInfo = await deviceInfo.linuxInfo; // deviceInfoString = 'Linux ${linuxInfo.prettyName}'; deviceMap["brand"] = "Linux"; deviceMap["model"] = linuxInfo.prettyName; } } catch (e) { // deviceInfoString = 'Error: $e'; deviceMap = {}; } // return deviceInfoString; return deviceMap; }我还想获取系统版本怎么获取呢

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,这是连接超时导致的错误吗

大家在看

recommend-type

lingo语法例子。。PPT

这个PPT是清华大学制作的 我觉得很好很全面 欢迎大家下载
recommend-type

宏碁Acer 4741G驱动合集 for winxp 官方版_宏碁个人笔记本XP驱动

宏碁Acer4741G驱动合集包含了显卡、网卡、声卡等驱动,都是xp版本,因为笔记本自带win7系统,有想要装xp系统的就需要这个驱动包。哈哈,买回来的Acer4741G,宏基的本本,性价比高,I5的CPU才4K多点,系统是win7家庭版,感觉还是XP好用,就要换回来了,想把驱动找全,欢迎下载体验
recommend-type

cpp-sdk-samples:适用于Windows和Linux的Affdex SDK的示例应用

cpp-sdk-samples:适用于Windows和Linux的Affdex SDK的示例应用
recommend-type

ray-optics:光学系统的几何光线追踪

射线光学 安装 要使用pip安装rayoptics ,请使用 > pip install rayoptics 或者,可以使用conda从conda - forge渠道安装rayoptics > conda install rayoptics --channel conda-forge 文献资料 射线光学位于“ 成像光学设计和分析工具 RayOptics是一个Python几何光学和成像光学库。 它为分析成像和相干光学系统提供了几何射线追踪基础。 在此基础上提供了许多标准的几何分析选项,例如横向射线和波前像差分析。 y-ybar图和镜头布局视图中近轴光线的图形编辑也支持光学系统的近轴布局。 支持导入Zemax .zmx和CODEV .seq文件。 RayOptics可用于Python脚本,Python和IPython外壳,Jupyter笔记本以及基于Qt的图形用户界面应用程序中。 笔记 该项
recommend-type

java读取kml文件数据

自己做的用java读取kml文件数据,并保存为json文件。方便其它地方解析。

最新推荐

recommend-type

tika-parser-font-module-3.1.0.jar中文-英文对照文档.zip

1、压缩文件中包含: 中文-英文对照文档、jar包下载地址、Maven依赖、Gradle依赖、源代码下载地址。 2、使用方法: 解压最外层zip,再解压其中的zip包,双击 【index.html】 文件,即可用浏览器打开、进行查看。 3、特殊说明: (1)本文档为人性化翻译,精心制作,请放心使用; (2)只翻译了该翻译的内容,如:注释、说明、描述、用法讲解 等; (3)不该翻译的内容保持原样,如:类名、方法名、包名、类型、关键字、代码 等。 4、温馨提示: (1)为了防止解压后路径太长导致浏览器无法打开,推荐在解压时选择“解压到当前文件夹”(放心,自带文件夹,文件不会散落一地); (2)有时,一套Java组件会有多个jar,所以在下载前,请仔细阅读本篇描述,以确保这就是你需要的文件。 5、本文件关键字: jar中文-英文对照文档.zip,java,jar包,Maven,第三方jar包,组件,开源组件,第三方组件,Gradle,中文API文档,手册,开发手册,使用手册,参考手册。
recommend-type

perl-SelfLoader-1.23-420.el8.tar.gz

# 适用操作系统:Centos8 #Step1、解压 tar -zxvf xxx.el8.tar.gz #Step2、进入解压后的目录,执行安装 sudo rpm -ivh *.rpm
recommend-type

tika-parser-audiovideo-module-3.1.0.jar中文-英文对照文档.zip

1、压缩文件中包含: 中文-英文对照文档、jar包下载地址、Maven依赖、Gradle依赖、源代码下载地址。 2、使用方法: 解压最外层zip,再解压其中的zip包,双击 【index.html】 文件,即可用浏览器打开、进行查看。 3、特殊说明: (1)本文档为人性化翻译,精心制作,请放心使用; (2)只翻译了该翻译的内容,如:注释、说明、描述、用法讲解 等; (3)不该翻译的内容保持原样,如:类名、方法名、包名、类型、关键字、代码 等。 4、温馨提示: (1)为了防止解压后路径太长导致浏览器无法打开,推荐在解压时选择“解压到当前文件夹”(放心,自带文件夹,文件不会散落一地); (2)有时,一套Java组件会有多个jar,所以在下载前,请仔细阅读本篇描述,以确保这就是你需要的文件。 5、本文件关键字: jar中文-英文对照文档.zip,java,jar包,Maven,第三方jar包,组件,开源组件,第三方组件,Gradle,中文API文档,手册,开发手册,使用手册,参考手册。
recommend-type

【故障诊断】基于matlab空气数据传感器在存在大气湍流的情况下故障检测和诊断【含Matlab源码 14132期】.zip

Matlab领域上传的视频是由对应的完整代码运行得来的,完整代码皆可运行,亲测可用,适合小白; 1、从视频里可见完整代码的目录内容 主函数:main.m; 调用函数:其他m文件;无需运行 运行结果效果图; 2、代码运行版本 Matlab 2019b;若运行有误,根据提示修改;若不会,私信博主; 3、运行操作步骤 步骤一:将所有文件放到Matlab的当前文件夹中; 步骤二:双击打开main.m文件; 步骤三:点击运行,等程序运行完得到结果; 4、仿真咨询 如需其他服务,可私信博主; 4.1 博客或资源的完整代码提供 4.2 期刊或参考文献复现 4.3 Matlab程序定制 4.4 科研合作
recommend-type

perl-Scalar-String-0.003-8.el8.tar.gz

# 适用操作系统:Centos8 #Step1、解压 tar -zxvf xxx.el8.tar.gz #Step2、进入解压后的目录,执行安装 sudo rpm -ivh *.rpm
recommend-type

HTML时间格式化工具及测试页面介绍

标题 "BoolStudio.github.io" 暗示这是一个与GitHub相关的在线资源,具体来说是与BoolStudio相关的网页地址。GitHub是一个著名的代码托管平台,它支持Git版本控制系统,允许用户在云端存储和共享代码。BoolStudio可能是GitHub上的一个用户或组织账户名称,而该页面可能是他们托管的项目或个人页面的入口。 描述中的信息包含了HTML元素和JavaScript代码片段。这段描述展示了一个测试页文件的部分代码,涉及到HTML的标题(title)和内嵌框架(iframe)的使用,以及JavaScript中Date对象的扩展功能。 从描述中我们可以分析出以下知识点: 1. HTML标题(Title): 在HTML中,`<title>`标签用于定义网页的标题,它会显示在浏览器的标题栏或页面的标签上。在描述中出现了`<title>现在时间</title>`,这表明网页的标题被设置为了“现在时间”。 2. 微软时间: 这可能指的是在网页中嵌入微软产品的日期和时间显示。尽管这部分内容在描述中被删除了,但微软时间通常与Windows操作系统的日期和时间显示相关联。 3. iframe元素: `<iframe>`标签定义了一个内嵌框架,可以在网页中嵌入另一个文档。在描述中出现的是`<iframe src"></iframe>`,这表示创建了一个空的iframe元素,其src属性为空,实际上没有嵌入任何内容。通常src属性会被设置为另一个HTML文档的URL,用来在当前页面中显示外部页面的内容。 4. JavaScript日期格式化: 描述中包含了一段JavaScript代码,这段代码扩展了Date对象的功能,允许它根据提供的格式字符串(fmt)返回格式化的日期和时间。例如,如果fmt是'y年M月d日 h时m分s秒',则该函数会按照这个格式返回当前日期和时间。 具体到代码实现,以下步骤展示了如何在JavaScript中扩展Date对象并格式化日期: - 首先创建了一个对象o,该对象包含日期和时间的不同部分,例如年(y)、月(M)、日(d)、时(h)、分(m)、秒(s)。 - 使用正则表达式检查格式字符串fmt中是否包含年份的占位符(y+),如果存在则将其替换为四位数的年份,不足部分用0补齐。 - 使用for...in循环遍历对象o中的每一个键值对,并使用正则表达式测试这些键(如年、月、日等)是否在格式字符串中存在。如果存在,则替换为相应的日期时间值。 这个知识点展示了在JavaScript中如何自定义对象的方法,以及如何使用正则表达式进行字符串的搜索和替换。 标签 "HTML" 表明这个文件主要涉及到超文本标记语言(HTML),这是构建网页内容的标准标记语言,它允许开发者使用标签来定义网页的结构和内容。HTML文件通常包含各种元素,如段落、链接、图片、表格和表单等。 压缩包子文件的文件名称列表中的 "BoolStudio.github.io-main" 可能指的是一个包含 "BoolStudio.github.io" 网站主要文件的压缩包。通常,网站项目会包含多个文件和资源,为了便于管理和分发,开发者会将这些文件打包成一个压缩文件。在本例中,"main" 可能表示该压缩包包含了网站的主要文件和代码。 通过对给定文件信息的分析,我们可以了解到网页设计和开发的相关知识点,特别是HTML文档结构、JavaScript对象和函数扩展以及如何在网站项目中组织文件。这些知识对于前端开发者来说是非常重要的,能够帮助他们创建更加动态和功能丰富的网页应用。
recommend-type

Elixir测试:从用例执行到覆盖率分析

### Elixir 测试:从用例执行到覆盖率分析 #### 1. 测试用例执行 在测试中,以 `Test` 结尾且包含 `use ExUnit` 的模块被视为测试用例。测试用例本质上是设置回调和测试的集合。测试用例的执行方式有两种:并发执行或顺序执行。使用 `async: true` 选项的测试用例将并发执行,其余的则按定义顺序(默认随机)顺序执行。 测试用例的执行流程如下: 1. 执行所有 `setup_all` 回调。 2. 执行所有测试及其特定的设置和拆卸回调。 `setup_all` 回调按定义顺序在同一进程中执行。在运行任何测试之前,测试用例将生成一个进程,在该进程中运行所有
recommend-type

Android Studio 时间延时

<think> 我们参考多个引用,总结在Android Studio中实现时间延时的方法。根据引用[1]和[2],我们知道有几种常见的延时方法,包括使用Handler的postDelayed方法、使用Timer和TimerTask以及使用Thread.sleep。引用[3]和[5]则展示了具体代码示例。 具体方法如下: 1. **使用Handler的postDelayed方法**(推荐在主线程中使用,避免直接操作UI线程的问题): ```java new Handler().postDelayed(new Runnable() { @Override
recommend-type

IMS Open Corpus Workbench:打造高效大型文本语料库管理工具

IMS Open Corpus Workbench(以下简称CWB)是一个强大的开源工具集,它专门用于管理和查询大型的、带有语言注释的文本语料库。这项工具有着广泛的应用领域,包括语言学研究、自然语言处理、人文科学研究等。 ### 标题知识点: #### 大型文本语料库的索引和查询工具 大型文本语料库指的是含有大量文本数据的数据库,其中包含的文本量通常以百万计。这些数据可能是书面文本、口语录音文字转写等形式。对于如此庞大的数据集,索引是必要的,它可以帮助研究者快速定位到感兴趣的片段,而查询工具则提供了从这些大量数据中提取特定信息的能力。 #### 开源 CWB作为一个开源工具,意味着其源代码对所有人开放,并且可以免费使用和修改。开源项目通常是由社区驱动,有着活跃的开发者和用户群体,不断对工具进行改进和拓展。这种模式促进了创新,并且有利于长期维护和升级。 ### 描述知识点: #### 管理和查询带有语言注释的文本 在语料库中,文本数据经常会被加上各种形式的语言注释,比如句法结构、词性标注、语义角色等。CWB支持管理这类富含语言信息的语料库,使其不仅仅保存原始文本信息,还整合了深层的语言知识。此外,CWB提供了多种查询语言注释数据的方式,使得用户可以针对特定的注释信息进行精确查询。 #### 核心组件:CQP(Corpus Query Processor) CQP是CWB中的核心组件,是一个高度灵活和高效的查询处理器。它支持在终端会话中交互式地使用,这为熟悉命令行界面的用户提供了一个强大的工具。同时,CQP也可以嵌入到其他程序中,比如Perl脚本,从而提供编程式的语料库访问方式。这为高级用户提供了一个强大的平台,可以编写复杂的查询,并将查询结果集成到其他程序中。 #### 基于Web的GUI CQPweb 除了命令行界面外,CWB还提供了一个基于Web的图形用户界面CQPweb,使得不熟悉命令行的用户也能够方便地使用CWB的强大功能。CQPweb通常允许用户通过网页直接构建查询,并展示查询结果,极大地降低了使用门槛。 ### 标签知识点: #### 开源软件 CWB作为开源软件,其主要特点和优势包括: - **社区支持**:开放源代码鼓励了全球开发者共同参与,提供错误修正、功能增强、新特性开发等。 - **定制化**:用户可以根据自己的需求对源代码进行修改,从而实现定制化的功能。 - **透明性**:源代码的开放确保了软件工作的透明性,用户可以清楚了解软件的工作原理和数据处理方式。 - **可靠性**:由于代码的公开性,很多用户和开发者可以共同审查代码,提高了软件的可靠性和安全性。 - **成本效益**:开源软件通常不需要支付昂贵的许可费用,对预算有限的个人和机构特别友好。 ### 压缩包子文件的文件名称列表知识点: #### cwb-3.0.0-osx-10.5-universal 这个文件名提供了关于该软件包的重要信息: - **cwb**:表示这是IMS Open Corpus Workbench的软件包。 - **3.0.0**:表示这个包的版本号,了解版本信息对于获取支持、查看更新日志、了解新特性等方面很重要。 - **osx**:表示这个软件包是为Mac OS X操作系统设计的。 - **10.5**:这个数字指明了这个软件包支持的操作系统版本至少是Mac OS X 10.5。 - **universal**:表明这个软件包是为不同架构的处理器(比如32位和64位)设计的通用二进制文件,提高了软件包的兼容性和可移植性。 综上所述,IMS Open Corpus Workbench是一个为处理带有语言注释的大型文本语料库而设计的开源工具集,它以高效且灵活的查询处理器CQP为核心,提供了命令行和基于Web的两种交互方式,极大地促进了语言学和语言技术领域的研究与应用。由于其开源特性,CWB得到了广泛的使用和不断的改进。
recommend-type

基于属性测试的深入解析与策略探讨

### 基于属性测试的深入解析与策略探讨 #### 1. 基于属性测试中的收缩机制 在基于属性的测试中,当测试失败时,像 `stream_data` 这样的框架会执行收缩(Shrinking)操作。收缩的目的是简化导致测试失败的输入,同时确保简化后的输入仍然会使测试失败,这样能更方便地定位问题。 为了说明这一点,我们来看一个简单的排序函数测试示例。我们实现了一个糟糕的排序函数,实际上就是恒等函数,它只是原封不动地返回输入列表: ```elixir defmodule BadSortTest do use ExUnit.Case use ExUnitProperties pro