触发light work rule的一种情况

The lightwork rule is invoked when CR construction involves too much work and no
current block or PI block is available in the cache for block cleanouts. Any additional disk

I/O to serve the CR block will kick the lightwork rule.

表my_test所在block 1/23146 该资源(BL)的master node为instance B。

从以下语句可以查到该资源的master node:

select * from gv$dlm_ress where resource_name like '%5a6a%BL%';


场景1 :instance A 上修改表 update my_test set name=name where id=1;

              alter systsem flush buffer_cache;

  //查看v$cr_block_server里的light_works

              Instance B上select * from my_test; //对其进行10046 trace


trace 文件如下:

PARSING IN CURSOR #2 len=21 dep=0 uid=0 oct=3 lid=0 tim=1410754418661475 hv=42
15432227 ad='7efae218'
select * from my_test
END OF STMT
PARSE #2:c=0,e=7419,p=0,cr=0,cu=0,mis=0,r=0,dep=0,og=1,tim=1410754418661472
EXEC #2:c=0,e=6469,p=0,cr=0,cu=0,mis=0,r=0,dep=0,og=1,tim=1410754418673271
WAIT #2: nam='SQL*Net message to client' ela= 1 driver id=1650815232 #bytes=1 
p3=0 obj#=0 tim=1410754418673305
WAIT #2: nam='db file sequential read' ela= 3705 file#=1 block#=23145 blocks=1   //由于是资源master node,所以没有授权的等待
 obj#=9917 tim=1410754418677187
WAIT #2: nam='db file sequential read' ela= 536 file#=1 block#=23146 blocks=1 obj#=9917 tim=1410754418677870 //由于是资源master node,所以没有授权的等待
WAIT #2: nam='gc cr block 2-way' ela= 338 p1=2 p2=105 p3=29 obj#=0 tim=1410754418678404  //undo segment header 
WAIT #2: nam='gc cr disk read' ela= 172 p1=2 p2=175 p3=30 obj#=0 tim=1410754418678767      //由于该block1/23146 有修改,需要instance A 上的undo(master node是                                                                                                                                                                                  instance A)做cr block。由于该undo block不在instance A的buffer中,所以                                                                                                                                                                               该事件是授权instance B直接去disk 读取。此时instance A会触发 light works

WAIT #2: nam='db file sequential read' ela= 1927 file#=2 block#=175 blocks=1 obj#=0 tim=1410754418680720//从disk 读取ubdo block
FETCH #2:c=1000,e=7456,p=3,cr=5,cu=0,mis=0,r=1,dep=0,og=1,tim=1410754418680805
WAIT #2: nam='SQL*Net message from client' ela= 29558 driver id=1650815232 #bytes=1 p3=0 obj#=0 tim=1410754418710408
WAIT #2: nam='SQL*Net message to client' ela= 0 driver id=1650815232 #bytes=1 p3=0 obj#=0 tim=1410754418710467
FETCH #2:c=0,e=28,p=0,cr=1,cu=0,mis=0,r=1,dep=0,og=1,tim=1410754418710486
*** 2015-10-12 09:15:31.348
WAIT #2: nam='SQL*Net message from client' ela= 6429515 driver id=1650815232 #bytes=1 p3=0 obj#=0 tim=1410754425145184
STAT #2 id=1 cnt=2 pid=0 pos=1 obj=9917 op='TABLE ACCESS FULL MY_TEST (cr=6 pr=3 pw=0 time=7447 us)'




场景1 :instance B上修改表 update my_test set name=name where id=1;

              alter systsem flush buffer_cache;

  //查看v$cr_block_server里的light_works

              Instance A 上select * from my_test; //对其进行10046 trace


PARSING IN CURSOR #1 len=21 dep=0 uid=0 oct=3 lid=0 tim=1410755503651449 hv=4215432227 ad='7eedf5b8'
select * from my_test
END OF STMT
PARSE #1:c=999,e=1533,p=0,cr=0,cu=0,mis=1,r=0,dep=0,og=1,tim=1410755503651446
EXEC #1:c=0,e=41,p=0,cr=0,cu=0,mis=0,r=0,dep=0,og=1,tim=1410755503651545
WAIT #1: nam='SQL*Net message to client' ela= 2 driver id=1650815232 #bytes=1 p3=0 obj#=9917 tim=1410755503651572
WAIT #1: nam='gc cr grant 2-way' ela= 195 p1=1 p2=23145 p3=4 obj#=9917 tim=1410755503652020  // 由于资源master node在instance B且在任何instance buffer cache

//里找不到该block,所以授权直接从disk 读取



WAIT #1: nam='db file sequential read' ela= 354 file#=1 block#=23145 blocks=1 obj#=9917 tim=1410755503652410
WAIT #1: nam='gc cr grant 2-way' ela= 158 p1=1 p2=23146 p3=1 obj#=9917 tim=1410755503652723 // 由于资源master node在instance B且在任何instance buffer cache

//里找不到该block,所以授权直接从disk 读取


WAIT #1: nam='db file sequential read' ela= 187 file#=1 block#=23146 blocks=1 obj#=9917 tim=1410755503652934
WAIT #1: nam='gc cr block 2-way' ela= 293 p1=4 p2=137 p3=53 obj#=0 tim=1410755503653383 //请求undo segment header,并接收到;此处从disk读取到my_test的block需要undo 去构建cr block

 “gc current grant 2-way” – Indicates that no current block was received because it was not cached in any instance. Instead a global grant was given, enabling the requesting instance to read the block from disk or modify it.


WAIT #1: nam='gc current grant 2-way' ela= 145 p1=1 p2=23146 p3=33619969 obj#=9917 tim=1410755503653609//该事件得到授权修改current block


WAIT #1: nam='gc cr disk read' ela= 175 p1=4 p2=10900 p3=54 obj#=0 tim=1410755503653940 //得到授权读取undo block ,此时触发了instanceB的light works
WAIT #1: nam='db file sequential read' ela= 8112 file#=4 block#=10900 blocks=1 obj#=0 tim=1410755503662077 //读取undo block
FETCH #1:c=1000,e=10503,p=3,cr=5,cu=0,mis=0,r=1,dep=0,og=1,tim=1410755503662183
WAIT #1: nam='SQL*Net message from client' ela= 278 driver id=1650815232 #bytes=1 p3=0 obj#=0 tim=1410755503662497
WAIT #1: nam='SQL*Net message to client' ela= 0 driver id=1650815232 #bytes=1 p3=0 obj#=0 tim=1410755503662533
FETCH #1:c=0,e=25,p=0,cr=1,cu=0,mis=0,r=1,dep=0,og=1,tim=1410755503662549
*** 2015-10-12 09:34:11.202
WAIT #1: nam='SQL*Net message from client' ela= 15089808 driver id=1650815232 #bytes=1 p3=0 obj#=0 tim=1410755518752397
STAT #1 id=1 cnt=2 pid=0 pos=1 obj=9917 op='TABLE ACCESS FULL MY_TEST (cr=6 pr=3 pw=0 time=10483 us)'
=====================
PARSING IN CURSOR #1 len=55 dep=0 uid=0 oct=42 lid=0 tim=1410755518752653 hv=2217940283 ad='0'
alter session set events '10046 trace name context off'
END OF STMT
PARSE #1:c=0,e=37,p=0,cr=0,cu=0,mis=0,r=0,dep=0,og=0,tim=1410755518752651
EXEC #1:c=0,e=47,p=0,cr=0,cu=0,mis=0,r=0,dep=0,og=0,tim=1410755518752741


除了以上场景,block cleanout也会触发light work。可参考链接:

http://blog.youkuaiyun.com/loryliu/article/details/49070979

场景1 :instance A 上修改表 update my_test set name=name where id=1;

              alter systsem flush buffer_cache;

  //查看v$cr_block_server里的light_works

              Instance B上select * from my_test; //对其进行10046 trace

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
model.fit(X_train, y_train) File "D:\work\Environmental\Python3.1.2\Lib\site-packages\sklearn\base.py", line 1473, in wrapper return fit_method(estimator, *args, **kwargs) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "D:\work\Environmental\Python3.1.2\Lib\site-packages\sklearn\ensemble\_forest.py", line 363, in fit X, y = self._validate_data( ^^^^^^^^^^^^^^^^^^^^ File "D:\work\Environmental\Python3.1.2\Lib\site-packages\sklearn\base.py", line 650, in _validate_data X, y = check_X_y(X, y, **check_params) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "D:\work\Environmental\Python3.1.2\Lib\site-packages\sklearn\utils\validation.py", line 1273, in check_X_y X = check_array( ^^^^^^^^^^^^ File "D:\work\Environmental\Python3.1.2\Lib\site-packages\sklearn\utils\validation.py", line 924, in check_array array = array.astype(new_dtype) ^^^^^^^^^^^^^^^^^^^^^^^ File "D:\work\Environmental\Python3.1.2\Lib\site-packages\pandas\core\generic.py", line 6643, in astype new_data = self._mgr.astype(dtype=dtype, copy=copy, errors=errors) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "D:\work\Environmental\Python3.1.2\Lib\site-packages\pandas\core\internals\managers.py", line 430, in astype return self.apply( ^^^^^^^^^^^ File "D:\work\Environmental\Python3.1.2\Lib\site-packages\pandas\core\internals\managers.py", line 363, in apply applied = getattr(b, f)(**kwargs) ^^^^^^^^^^^^^^^^^^^^^^^ File "D:\work\Environmental\Python3.1.2\Lib\site-packages\pandas\core\internals\blocks.py", line 758, in astype new_values = astype_array_safe(values, dtype, copy=copy, errors=errors) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "D:\work\Environmental\Python3.1.2\Lib\site-packages\pandas\core\dtypes\astype.py", line 237, in astype_array_safe new_values = astype_array(values, dtype, copy=copy) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "D:\work\Environmental\Python3.1.2\Lib\site-packages\pandas\core\dtypes\astype.py", line 179, in astype_array values = values.astype(dtype, copy=copy) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "D:\work\Environmental\Python3.1.2\Lib\site-packages\pandas\core\arrays\datetimes.py", line 739, in astype return dtl.DatetimeLikeArrayMixin.astype(self, dtype, copy) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "D:\work\Environmental\Python3.1.2\Lib\site-packages\pandas\core\arrays\datetimelike.py", line 494, in astype raise TypeError(msg) TypeError: Cannot cast DatetimeArray to dtype float32 报错的原因是什么
07-11
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值