// device_simulator.ts - 设备模拟器Worker import { connect, MqttClient } from "npm:mqtt@5.10.1"; import { ModbusManager } from "./modbus_manager.ts"; import { getDefaultPollInterval, parseRegistersFromString, createModbusPollConfigFromRegisters } from "./device_protocol_config.ts"; import { createModuleLogger, perfMonitor, logMemoryUsage } from "./debug_logger.ts"; interface DeviceInfo { ip: string; port: string; slaveId: string; deviceName: string; protocolType: string; brandName: string; registers: string; } interface DeviceWorkerMessage { type: "init" | "close" | "reconnect" | "action"; data?: any; } interface DeviceMainMessage { type: "ready" | "error" | "status" | "log" | "reset"; data?: any; } interface DeviceConfig { deviceId: string; deviceInfo: DeviceInfo; vdaInterface: string; mqtt: { brokerUrl: string; options: any; }; vehicle: { manufacturer: string; serialNumber: string; vdaVersion: string; }; // 设备创建时的动作参数 actionParameters?: Array<{ key: string; value: string; }>; modbus?: { host: string; port: number; unitId: number; poll: Array<{ fn: "readHoldingRegisters" | "readInputRegisters" | "readCoils" | "readDiscreteInputs"; start: number; len: number; mqttKey: string; bind?: string; }>; pollInterval: number; }; } interface Position { x: number; y: number; theta: number; mapId: string; mapDescription?: string; } interface NodeState { nodeId: string; sequenceId: number; nodeDescription?: string; nodePosition?: Position; released: boolean; } interface EdgeState { edgeId: string; sequenceId: number; edgeDescription?: string; released: boolean; trajectory?: Position[]; } interface AgvPosition { x: number; y: number; theta: number; mapId: string; mapDescription?: string; positionInitialized: boolean; localizationScore: number; deviationRange: number; } interface Velocity { vx: number; vy: number; omega: number; } interface BatteryState { batteryCharge: number; batteryVoltage: number; batteryHealth: number; charging: boolean; reach: number; } interface ErrorState { errorType: string; errorLevel: "WARNING" | "FATAL"; errorDescription: string; errorHint?: string; errorReferences?: Array<{ referenceKey: string; referenceValue: string }>; } interface SafetyState { eStop: string; fieldViolation: boolean; } interface ActionState { actionType: string; actionId: string; actionDescription?: string; actionStatus: "WAITING" | "INITIALIZING" | "RUNNING" | "PAUSED" | "FINISHED" | "FAILED"; resultDescription?: string; actionParameters?: Array<{ key: string; value: string }>; blockingType: "NONE" | "SOFT" | "HARD"; } interface AgvState { headerId: number; timestamp: string; version: string; manufacturer: string; serialNumber: string; orderId: string; orderUpdateId: number; zoneSetId: string; lastNodeId: string; lastNodeSequenceId: number; driving: boolean; paused: boolean; newBaseRequest: boolean; distanceSinceLastNode: number; operatingMode: "AUTOMATIC" | "SEMIAUTOMATIC" | "MANUAL" | "SERVICE" | "TEACHIN"; nodeStates: NodeState[]; edgeStates: EdgeState[]; agvPosition: AgvPosition; velocity: Velocity; loads: any[]; batteryState: BatteryState; errors: ErrorState[]; information: any[]; safetyState: SafetyState; actionStates: ActionState[]; waitingForInteractionZoneRelease: boolean; forkState?: any; } interface Connection { headerId: number; timestamp: string; version: string; manufacturer: string; serialNumber: string; connectionState: "ONLINE" | "OFFLINE" | "CONNECTIONBROKEN"; } class DeviceSimulator { private client!: MqttClient; private config!: DeviceConfig; private state!: AgvState; private connection!: Connection; private stateInterval?: number; private connectionInterval?: number; private headerId = 1; private isConnected = false; private deviceActions: ActionState[] = []; private modbus!: ModbusManager; private modbusPollTimer?: number; private logger = createModuleLogger("DEVICE_SIMULATOR"); private enableLog = false; private log(message: string) { if(this.enableLog){ const timestamp = new Date().toISOString(); const logMessage = `[${timestamp}] [Device ${this.config.deviceId}] ${message}`; console.log(logMessage); // 发送日志到主进程 self.postMessage({ type: "log", data: { message: logMessage } } as DeviceMainMessage); } } private sendMessage(type: string, data?: any) { self.postMessage({ type, data } as DeviceMainMessage); } private getFunctionCodeDescription(fnCode: string): string { const descriptions: Record = { "1": "Read Coils (读线圈)", "2": "Read Discrete Inputs (读离散输入)", "3": "Read Holding Registers (读保持寄存器)", "4": "Read Input Registers (读输入寄存器)", "5": "Write Single Coil (写单个线圈)", "6": "Write Single Register (写单个寄存器)", "15": "Write Multiple Coils (写多个线圈)", "16": "Write Multiple Registers (写多个寄存器)" }; return descriptions[fnCode] || `Unknown Function Code (未知功能码 ${fnCode})`; } private getFunctionCodeFromModbusFunction(modbusFunction: string): string { const functionMap: Record = { "readCoils": "1", "readDiscreteInputs": "2", "readHoldingRegisters": "3", "readInputRegisters": "4" }; return functionMap[modbusFunction] || "3"; // 默认为读保持寄存器 } private updateRegisterInformation(registerName: string, bind: string, values: number[], address: number, count: number, modbusFunction: string, timestamp: string): void { // 创建符合VDA 5050标准的信息项,将寄存器信息放到infoReferences中 const infoType = `CUSTOM_REGISTER`; const infoDescription = `REGISTER_${registerName.toUpperCase()}`; const informationItem = { infoType: infoType, infoDescription: infoDescription, infoLevel: "INFO", infoReferences: [ { referenceKey: "name", referenceValue: registerName }, { referenceKey: "bind", referenceValue: bind }, { referenceKey: "values", referenceValue: JSON.stringify(values) }, { referenceKey: "address", referenceValue: address.toString() }, { referenceKey: "count", referenceValue: count.toString() }, { referenceKey: "function", referenceValue: modbusFunction }, { referenceKey: "timestamp", referenceValue: timestamp } ] }; // 查找是否已存在相同寄存器的信息项 const existingIndex = this.state.information.findIndex(info => info.infoDescription === infoDescription ); if (existingIndex >= 0) { // 更新现有信息项 this.state.information[existingIndex] = informationItem; this.logger.trace("🔄 Updated register information", { registerName, infoType, address, newValues: values, timestamp }); } else { // 添加新信息项 this.state.information.push(informationItem); this.logger.trace("➕ Added new register information", { registerName, infoType, address, values, totalInformation: this.state.information.length }); } // 可选:限制information数组的大小,避免无限增长 const maxInformation = 100; // 最多保留100个信息项 if (this.state.information.length > maxInformation) { // 删除最旧的信息项(按时间戳排序) this.state.information.sort((a, b) => { const timeA = new Date(a.timeOfOccurrence).getTime(); const timeB = new Date(b.timeOfOccurrence).getTime(); return timeA - timeB; }); const removedItems = this.state.information.splice(0, this.state.information.length - maxInformation); this.logger.debug("🗑️ Removed old information items", { removedCount: removedItems.length, remainingCount: this.state.information.length }); } } async initialize(config: DeviceConfig) { this.config = config; this.logger.info("🚀 Initializing device simulator", { deviceId: config.deviceId, deviceName: config.deviceInfo?.deviceName, protocol: config.deviceInfo?.protocolType }); // console.log(config); logMemoryUsage("DEVICE_SIMULATOR"); try { await perfMonitor.measureAsync("device_initialization", async () => { await this.connectMqtt(); await this.setupModbus(); this.initializeState(); this.initializeConnection(); this.startPeriodicReporting(); }); this.logger.info("✅ Device simulator initialized successfully"); this.log("Device simulator initialized successfully"); this.sendMessage("ready"); } catch (error) { this.logger.error("❌ Failed to initialize device simulator", error); this.log(`Failed to initialize: ${(error as Error).message}`); // 如果初始化失败,请求重启worker if ((error as Error).message.includes("MQTT") || (error as Error).message.includes("timeout")) { this.log(`❌ Initialization failed due to MQTT issue, requesting worker reset`); this.sendMessage("reset", { reason: "init_failure", error: (error as Error).message }); } else { this.sendMessage("error", { error: (error as Error).message }); } } } private async connectMqtt() { this.log(`Connecting to MQTT broker: ${this.config.mqtt.brokerUrl}`); this.client = connect(this.config.mqtt.brokerUrl, this.config.mqtt.options); return new Promise((resolve, reject) => { const timeout = setTimeout(() => { const error = new Error("MQTT connection timeout"); this.log(`❌ MQTT connection timeout, requesting worker reset`); this.sendMessage("reset", { reason: "mqtt_timeout", error: error.message }); reject(error); }, 10000); this.client.on("connect", () => { clearTimeout(timeout); this.isConnected = true; this.log("Connected to MQTT broker"); this.setupMqttHandlers(); resolve(); }); this.client.on("error", (error: Error) => { clearTimeout(timeout); this.log(`❌ MQTT connection error: ${error.message}, requesting worker reset`); this.sendMessage("reset", { reason: "mqtt_error", error: error.message }); reject(error); }); }); } private async setupModbus() { this.logger.info("🔧 Setting up Modbus connection..."); // 从设备信息中动态创建Modbus配置 const host = this.config.deviceInfo.ip; const port = parseInt(this.config.deviceInfo.port) || 502; const unitId = parseInt(this.config.deviceInfo.slaveId) || 1; this.logger.debug("📋 Modbus connection parameters", { host, port, unitId, deviceName: this.config.deviceInfo.deviceName, brandName: this.config.deviceInfo.brandName, protocolType: this.config.deviceInfo.protocolType }); this.log(`Creating Modbus config from device info: ${host}:${port}, unitId: ${unitId}`); this.log(`Device: ${this.config.deviceInfo.deviceName} (${this.config.deviceInfo.brandName})`); this.log(`Protocol: ${this.config.deviceInfo.protocolType}`); // 获取设备品牌特定的轮询间隔 const pollInterval = getDefaultPollInterval(this.config.deviceInfo.brandName); this.logger.debug("⏱️ Poll interval configuration", { brandName: this.config.deviceInfo.brandName, pollInterval: `${pollInterval}ms` }); this.log(`Using poll interval for ${this.config.deviceInfo.brandName || "通用"}: ${pollInterval}ms`); let pollConfig: Array<{ fn: "readHoldingRegisters" | "readInputRegisters" | "readCoils" | "readDiscreteInputs"; start: number; len: number; mqttKey: string; bind?: string; }> = []; // 检查是否有寄存器配置 this.logger.trace("🔍 Checking for registers configuration in device info..."); // 首先检查 deviceInfo.registers let registersConfigString: string | undefined; if (this.config.deviceInfo.registers) { registersConfigString = this.config.deviceInfo.registers; this.logger.info("✅ Found registers configuration in deviceInfo.registers"); this.logger.trace("📝 Raw registers config from deviceInfo", { config: registersConfigString }); this.log("✅ Found registers configuration in deviceInfo.registers"); } else { // 如果 deviceInfo.registers 不存在,则检查 actionParameters const registersParam = this.config.actionParameters?.find(param => param.key === "registers"); if (registersParam && registersParam.value) { registersConfigString = registersParam.value; this.logger.info("✅ Found registers configuration in action parameters"); this.logger.trace("📝 Raw registers config from actionParameters", { config: registersConfigString }); this.log("✅ Found registers configuration in action parameters"); } } if (registersConfigString) { this.log(`📝 Raw registers config: ${registersConfigString}`); // 验证JSON格式 try { const parsedConfig = JSON.parse(registersConfigString); this.logger.debug("✅ Registers configuration JSON validation passed", { registersCount: Array.isArray(parsedConfig) ? parsedConfig.length : 'not_array', structure: parsedConfig }); this.log("✅ Registers configuration is valid JSON"); } catch (error) { this.logger.error("❌ Invalid JSON in registers configuration", { error: (error as Error).message, rawConfig: registersConfigString }); this.log(`❌ Invalid JSON in registers configuration: ${(error as Error).message}`); pollConfig = []; return; } // 解析寄存器配置 const registers = perfMonitor.measure("register_parsing", () => parseRegistersFromString(registersConfigString!) ); this.logger.info("📊 Register parsing completed", { totalRegisters: registers.length, registers: registers.map(reg => ({ name: reg.name, fnCode: reg.fnCode, address: reg.regAddress, count: reg.regCount })) }); this.log(`📊 Parsed ${registers.length} register definitions:`); if (registers.length === 0) { this.logger.warn("⚠️ No valid register definitions found after parsing"); this.log("⚠️ No valid register definitions found after parsing"); } registers.forEach((reg, index) => { this.logger.trace(`Register definition [${index + 1}]`, { name: reg.name, fnCode: reg.fnCode, fnDescription: this.getFunctionCodeDescription(reg.fnCode), address: reg.regAddress, count: reg.regCount }); this.log(` [${index + 1}] ${reg.name}:`); this.log(` - Function Code: ${reg.fnCode} (${this.getFunctionCodeDescription(reg.fnCode)})`); this.log(` - Address: ${reg.regAddress}`); this.log(` - Count: ${reg.regCount}`); }); // 使用解析的寄存器创建轮询配置 pollConfig = perfMonitor.measure("poll_config_creation", () => createModbusPollConfigFromRegisters(registers) ); this.logger.info("🔄 Polling configuration created", { totalTasks: pollConfig.length, tasks: pollConfig.map(config => ({ function: config.fn, startAddress: config.start, length: config.len, mqttKey: config.mqttKey })) }); this.log(`🔄 Created ${pollConfig.length} polling tasks from register definitions:`); pollConfig.forEach((config, index) => { this.log(` [${index + 1}] ${config.fn}:`); this.log(` - Start Address: ${config.start}`); this.log(` - Length: ${config.len}`); this.log(` - MQTT Key: ${config.mqttKey}`); }); } else { this.logger.warn("❌ No registers configuration found"); this.log("❌ No registers configuration found"); this.log("💡 Expected registers configuration in deviceInfo.registers or actionParameters"); this.log("📝 Example: [{\"fnCode\":\"6\",\"name\":\"button1\",\"regCount\":\"1\",\"regAddress\":\"1\"}]"); pollConfig = []; } // 动态创建Modbus配置 this.config.modbus = { host: host, port: port, unitId: unitId, poll: pollConfig, pollInterval: pollInterval }; this.modbus = new ModbusManager( this.config.modbus.host, this.config.modbus.port, this.config.modbus.unitId, (msg) => this.log(`[Modbus] ${msg}`) ); try { await this.modbus.init(); } catch (error) { const errorMessage = (error as Error).message; this.log(`❌ Modbus initialization failed: ${errorMessage}`); // Report initialization error without auto-reset this.sendMessage("error", { error: `Modbus initialization error: ${errorMessage}` }); throw error; } // 只有在有轮询配置时才启动循环读取任务 if (this.config.modbus.poll.length > 0) { this.modbusPollTimer = setInterval( () => { void this.pollModbus(); }, this.config.modbus.pollInterval ); this.log(`Modbus setup completed - polling ${this.config.modbus.poll.length} register groups every ${this.config.modbus.pollInterval}ms`); } else { this.log(`Modbus setup completed - no polling configured, only write operations will be available`); } } private async pollModbus() { try { // 确保Modbus配置存在 if (!this.config.modbus) { this.logger.error("❌ Modbus configuration not available for polling"); this.log("❌ Modbus configuration not available for polling"); return; } const pollStartTime = performance.now(); this.logger.debug("🔄 Starting Modbus polling cycle", { totalGroups: this.config.modbus.poll.length, pollInterval: this.config.modbus.pollInterval }); this.log(`🔄 Starting Modbus polling cycle - ${this.config.modbus.poll.length} register groups to read`); for (const [index, item] of this.config.modbus.poll.entries()) { const itemStartTime = performance.now(); try { this.logger.trace(`📖 Reading register group [${index + 1}/${this.config.modbus.poll.length}]`, { function: item.fn, startAddress: item.start, length: item.len, mqttKey: item.mqttKey }); this.log(`📖 [${index + 1}/${this.config.modbus.poll.length}] Reading ${item.fn} - Address: ${item.start}, Length: ${item.len}`); const values = await perfMonitor.measureAsync(`modbus_read_${item.mqttKey}`, async () => this.modbus.enqueueRead(item.fn, item.start, item.len) ); const readDuration = performance.now() - itemStartTime; this.logger.debug("✅ Modbus read successful", { mqttKey: item.mqttKey, values, duration: `${readDuration.toFixed(2)}ms`, valuesCount: Array.isArray(values) ? values.length : 1 }); this.log(`✅ Read successful: ${JSON.stringify(values)}`); // 将寄存器数据存储到information数组中 const registerName = item.mqttKey.split('/').pop() || `register_${item.start}`; const bind = item.bind || registerName; const registerValues = Array.isArray(values) ? values : [values]; const timestamp = new Date().toISOString(); // 更新或添加到information数组 this.updateRegisterInformation(registerName, bind, registerValues, item.start, item.len, item.fn, timestamp); this.logger.trace("📦 Register data stored in information", { registerName, address: item.start, count: item.len, values: registerValues, function: item.fn, totalInformation: this.state.information.length }); this.log(`📦 Stored register data in information: ${registerName} = ${JSON.stringify(registerValues)}`); } catch (error) { const readDuration = performance.now() - itemStartTime; const errorMessage = (error as Error).message; this.logger.error("❌ Modbus read error", { mqttKey: item.mqttKey, function: item.fn, startAddress: item.start, length: item.len, error: errorMessage, duration: `${readDuration.toFixed(2)}ms` }); this.log(`❌ Modbus read error for ${item.mqttKey}: ${errorMessage}`); this.log(`🔧 Failed operation: ${item.fn} at address ${item.start}, length ${item.len}`); // 检查是否为连接失效错误,如果是则请求重置worker if (this.isConnectionError(errorMessage)) { this.log(`❌ Modbus connection failure detected, requesting worker reset`); this.sendMessage("reset", { reason: "modbus_connection_failure", error: `Modbus connection error: ${errorMessage}` }); return; // 停止当前轮询周期 } } } const totalDuration = performance.now() - pollStartTime; this.logger.debug("🏁 Modbus polling cycle completed", { totalDuration: `${totalDuration.toFixed(2)}ms`, averagePerGroup: `${(totalDuration / this.config.modbus.poll.length).toFixed(2)}ms`, groupsProcessed: this.config.modbus.poll.length }); this.log(`🏁 Modbus polling cycle completed`); // 记录内存使用情况(每10次轮询记录一次) if (Math.random() < 0.1) { logMemoryUsage("DEVICE_SIMULATOR"); } } catch (error) { // 捕获整个轮询周期的未预期错误 const errorMessage = (error as Error).message; this.logger.error("❌ Unexpected error in Modbus polling cycle", { error: errorMessage, stack: (error as Error).stack }); this.log(`❌ Unexpected error in Modbus polling cycle: ${errorMessage}`); // 检查是否为连接相关错误 if (this.isConnectionError(errorMessage)) { this.log(`❌ Polling cycle connection failure detected, requesting worker reset`); this.sendMessage("reset", { reason: "modbus_polling_failure", error: `Modbus polling error: ${errorMessage}` }); } } } // 检查是否为连接相关错误的辅助方法 private isConnectionError(errorMessage: string): boolean { const connectionErrors = [ "no connection", "connection lost", "connection closed", "connection refused", "timeout", "timed out", "ECONNRESET", "ENOTCONN", "ECONNREFUSED", "ETIMEDOUT", "EPIPE", "ENETUNREACH", "EHOSTUNREACH" ]; return connectionErrors.some(keyword => errorMessage.toLowerCase().includes(keyword.toLowerCase()) ); } private setupMqttHandlers() { // 订阅instantActions主题 const instantActionsTopic = `${this.config.vdaInterface}/${this.config.vehicle.vdaVersion}/${this.config.vehicle.manufacturer}/${this.config.vehicle.serialNumber}/instantActions`; this.client.subscribe(instantActionsTopic, (err: Error | null) => { if (err) { this.log(`Failed to subscribe to ${instantActionsTopic}: ${err.message}`); } else { this.log(`Subscribed to ${instantActionsTopic}`); } }); this.client.on("message", (topic: string, message: Uint8Array) => { try { const data = JSON.parse(new TextDecoder().decode(message)); this.log(`📥 Received message on ${topic}`); if (topic.endsWith("/instantActions")) { this.handleInstantActions(data); } } catch (error) { this.log(`Error parsing message: ${(error as Error).message}`); } }); this.client.on("disconnect", () => { this.isConnected = false; this.log("❌ Disconnected from MQTT broker, requesting worker reset"); this.sendMessage("reset", { reason: "mqtt_disconnect", error: "MQTT broker disconnected" }); }); this.client.on("error", (error: Error) => { this.log(`❌ MQTT runtime error: ${error.message}, requesting worker reset`); this.sendMessage("reset", { reason: "mqtt_runtime_error", error: error.message }); }); } private initializeState() { const now = new Date().toISOString(); this.state = { headerId: this.headerId++, timestamp: now, version: this.config.vehicle.vdaVersion, manufacturer: this.config.vehicle.manufacturer, serialNumber: this.config.vehicle.serialNumber, orderId: "order_0", orderUpdateId: 0, zoneSetId: "zone_0", lastNodeId: "node_0", lastNodeSequenceId: 0, driving: false, paused: false, newBaseRequest: false, distanceSinceLastNode: 0, operatingMode: "AUTOMATIC", nodeStates: [], edgeStates: [], agvPosition: { x: 0, y: 0, theta: 0, mapId: "map_1", mapDescription: "Device Map", positionInitialized: true, localizationScore: 1.0, deviationRange: 0.1, }, velocity: { vx: 0, vy: 0, omega: 0, }, loads: [], batteryState: { batteryCharge: 85.5, batteryVoltage: 24.2, batteryHealth: 95.0, charging: false, reach: 8500, }, errors: [], information: [], safetyState: { eStop: "NONE", fieldViolation: false, }, actionStates: [], waitingForInteractionZoneRelease: false, }; } private initializeConnection() { const now = new Date().toISOString(); this.connection = { headerId: this.headerId++, timestamp: now, version: this.config.vehicle.vdaVersion, manufacturer: this.config.vehicle.manufacturer, serialNumber: this.config.vehicle.serialNumber, connectionState: "ONLINE", }; } private startPeriodicReporting() { // 每2秒发送状态 this.stateInterval = setInterval(() => { this.publishState(); }, 2000); // 每1秒发送连接状态 this.connectionInterval = setInterval(() => { this.publishConnection(); }, 1000); // 立即发送一次 this.publishState(); this.publishConnection(); } private publishState() { if (!this.isConnected) return; this.state.headerId = this.headerId++; this.state.timestamp = new Date().toISOString(); this.state.actionStates = [...this.deviceActions]; const topic = `${this.config.vdaInterface}/${this.config.vehicle.vdaVersion}/${this.config.vehicle.manufacturer}/${this.config.vehicle.serialNumber}/state`; const message = JSON.stringify(this.state, null, 2); this.client.publish(topic, message, (err?: Error) => { if (err) { this.log(`Failed to publish state: ${err.message}`); } else { // this.log(`📤 Published state (headerId: ${this.state.headerId})`); } }); } private publishConnection() { if (!this.isConnected) return; this.connection.headerId = this.headerId++; this.connection.timestamp = new Date().toISOString(); const topic = `${this.config.vdaInterface}/${this.config.vehicle.vdaVersion}/${this.config.vehicle.manufacturer}/${this.config.vehicle.serialNumber}/connection`; const message = JSON.stringify(this.connection, null, 2); this.client.publish(topic, message, (err?: Error) => { if (err) { this.log(`Failed to publish connection: ${err.message}`); } else { // this.log(`📤 Published connection (headerId: ${this.connection.headerId})`); } }); } private handleInstantActions(message: any) { this.log(`Processing instantActions message`); try { // 支持两种格式:标准VDA 5050的instantActions和实际使用的actions const actions = message.instantActions || message.actions || []; if (!Array.isArray(actions)) { this.log("❌ Invalid actions format - not an array"); return; } for (const action of actions) { // 检查是否为Modbus写操作 if (action.actionType === "deviceWrite") { this.handleModbusWrite(action); } else { this.processDeviceAction(action); } } } catch (error) { this.log(`❌ Error processing instantActions: ${(error as Error).message}`); } } private async handleModbusWrite(action: any) { this.log(`🔧 Processing Modbus write action: ${action.actionId}`); console.log("收到 Modbus 写入动作消息", action); try { // 提取设备信息 const getParam = (key: string) => action.actionParameters?.find((p: any) => p.key === key)?.value; const protocolType = getParam("protocolType"); const brandName = getParam("brandName"); const ip = getParam("ip"); const port = getParam("port"); const deviceName = getParam("deviceName"); const slaveId = getParam("slaveId"); const registersParam = getParam("registers"); this.log(`📋 设备信息: ${deviceName} (${brandName}) - ${ip}:${port}, SlaveID: ${slaveId}, 协议: ${protocolType}`); if (!registersParam) { this.log(`❌ Missing registers parameter in Modbus write action`); return; } // 解析寄存器数组 let registers: any[]; try { registers = JSON.parse(registersParam); if (!Array.isArray(registers)) { throw new Error("Registers parameter is not an array"); } } catch (error) { this.log(`❌ Failed to parse registers parameter: ${(error as Error).message}`); return; } this.log(`📝 解析到 ${registers.length} 个寄存器写入操作:`); registers.forEach((reg, index) => { this.log(` [${index + 1}] ${reg.name}: 功能码${reg.fnCode}, 地址${reg.regAddress}, 值${reg.regValue}`); }); // 创建动作状态 const actionState: ActionState = { actionType: action.actionType, actionId: action.actionId, actionDescription: `Modbus write ${registers.length} registers to ${deviceName}`, actionStatus: "RUNNING", actionParameters: action.actionParameters || [], blockingType: action.blockingType || "NONE", }; this.deviceActions.push(actionState); // 执行所有寄存器写操作 const writeResults: any[] = []; let successCount = 0; let failureCount = 0; for (const [index, register] of registers.entries()) { try { const address = Number(register.regAddress); const value = Number(register.regValue); const fnCode = register.fnCode; const registerName = register.name; this.log(`🔧 [${index + 1}/${registers.length}] 写入寄存器 ${registerName}: 地址${address}, 值${value}, 功能码${fnCode}`); // 根据功能码确定写入类型和数据格式 let functionType: "writeSingleRegister" | "writeMultipleRegisters"; let payload: number | number[]; if (fnCode === "6") { functionType = "writeSingleRegister"; payload = value; // 单个寄存器写入,使用单个数值 } else if (fnCode === "16" || fnCode === "10") { functionType = "writeMultipleRegisters"; payload = [value]; // 多个寄存器写入,需要数组格式 } else { throw new Error(`Unsupported function code: ${fnCode}`); } this.log(`📡 执行 ${functionType}(地址${address}, 数据${JSON.stringify(payload)})`); // 执行Modbus写操作 await this.modbus.enqueueWrite(functionType, address, payload); writeResults.push({ registerName, address, value, fnCode, status: "success" }); successCount++; this.log(`✅ 寄存器 ${registerName} 写入成功`); } catch (error) { const errorMsg = (error as Error).message; this.log(`❌ 寄存器 ${register.name} 写入失败: ${errorMsg}`); // 检查是否为连接失效错误 if (this.isConnectionError(errorMsg)) { this.log(`❌ Modbus write connection failure detected, requesting worker reset`); this.sendMessage("reset", { reason: "modbus_write_connection_failure", error: `Modbus write connection error: ${errorMsg}` }); return; // 停止当前写操作 } writeResults.push({ registerName: register.name, address: register.regAddress, value: register.regValue, fnCode: register.fnCode, status: "error", error: errorMsg }); failureCount++; } } // 更新动作状态 if (failureCount === 0) { actionState.actionStatus = "FINISHED"; actionState.resultDescription = `All ${successCount} registers written successfully`; this.log(`✅ 所有 ${successCount} 个寄存器写入完成`); } else if (successCount === 0) { actionState.actionStatus = "FAILED"; actionState.resultDescription = `All ${failureCount} registers failed to write`; this.log(`❌ 所有 ${failureCount} 个寄存器写入失败`); } else { actionState.actionStatus = "FINISHED"; actionState.resultDescription = `Partial success: ${successCount} succeeded, ${failureCount} failed`; this.log(`⚠️ 部分成功: ${successCount} 个成功, ${failureCount} 个失败`); } // 发布写操作结果的MQTT消息 const topic = `${this.config.vdaInterface}/${this.config.vehicle.vdaVersion}/${this.config.vehicle.manufacturer}/${this.config.vehicle.serialNumber}/writeResult`; const resultPayload = JSON.stringify({ timestamp: new Date().toISOString(), deviceId: this.config.deviceId, actionId: action.actionId, deviceInfo: { deviceName, brandName, ip, port, slaveId, protocolType }, writeResults, summary: { total: registers.length, success: successCount, failure: failureCount }, status: failureCount === 0 ? "success" : (successCount === 0 ? "error" : "partial") }); this.client.publish(topic, resultPayload); } catch (error) { this.log(`❌ Modbus write action failed: ${(error as Error).message}`); // 更新动作状态为失败 const actionState = this.deviceActions.find(a => a.actionId === action.actionId); if (actionState) { actionState.actionStatus = "FAILED"; actionState.resultDescription = `Modbus write action failed: ${(error as Error).message}`; } // 发布写操作失败的MQTT消息 const topic = `${this.config.vdaInterface}/${this.config.vehicle.vdaVersion}/${this.config.vehicle.manufacturer}/${this.config.vehicle.serialNumber}/writeResult`; const resultPayload = JSON.stringify({ timestamp: new Date().toISOString(), deviceId: this.config.deviceId, actionId: action.actionId, status: "error", error: (error as Error).message }); this.client.publish(topic, resultPayload); } } private processDeviceAction(action: any) { this.log(`🔧 Processing device action: ${action.actionType}`); // 创建动作状态 const actionState: ActionState = { actionType: action.actionType, actionId: action.actionId, actionDescription: action.actionDescription || `Device action: ${action.actionType}`, actionStatus: "RUNNING", actionParameters: action.actionParameters || [], blockingType: action.blockingType || "NONE", }; // 添加到设备动作列表 this.deviceActions.push(actionState); // 模拟设备操作 this.simulateDeviceOperation(actionState); } private simulateDeviceOperation(actionState: ActionState) { this.log(`🎯 Simulating device operation: ${actionState.actionType}`); // 根据动作类型执行不同的模拟操作 switch (actionState.actionType) { case "deviceSetup": this.simulateDeviceSetup(actionState); break; case "deviceWrite": this.simulateDeviceWrite(actionState); break; case "deviceRead": this.simulateDeviceRead(actionState); break; case "deviceStop": this.simulateDeviceStop(actionState); break; case "deviceDelete": this.simulateDeviceDelete(actionState); break; default: this.log(`⚠️ Unknown device action type: ${actionState.actionType}`); actionState.actionStatus = "FAILED"; actionState.resultDescription = `Unknown action type: ${actionState.actionType}`; } } private simulateDeviceSetup(actionState: ActionState) { this.log(`📱 Setting up device with parameters:`); if (actionState.actionParameters) { for (const param of actionState.actionParameters) { this.log(` ${param.key}: ${param.value}`); } } // 模拟设备设置过程 setTimeout(() => { actionState.actionStatus = "FINISHED"; actionState.resultDescription = "Device setup completed successfully"; this.log(`✅ Device setup completed for action ${actionState.actionId}`); }, 2000); } private simulateDeviceWrite(actionState: ActionState) { this.log(`✍️ Writing to device registers`); // 模拟写入操作 setTimeout(() => { actionState.actionStatus = "FINISHED"; actionState.resultDescription = "Device write operation completed"; this.log(`✅ Device write completed for action ${actionState.actionId}`); }, 1000); } private simulateDeviceRead(actionState: ActionState) { this.log(`📖 Reading from device registers`); // 模拟读取操作 setTimeout(() => { actionState.actionStatus = "FINISHED"; actionState.resultDescription = "Device read operation completed"; this.log(`✅ Device read completed for action ${actionState.actionId}`); }, 500); } private simulateDeviceStop(actionState: ActionState) { this.log(`🛑 Processing device stop operation`); if (actionState.actionParameters) { this.log(`📱 Stopping device with parameters:`); for (const param of actionState.actionParameters) { this.log(` ${param.key}: ${param.value}`); } } // 模拟设备停止过程 setTimeout(() => { actionState.actionStatus = "FINISHED"; actionState.resultDescription = "Device stop operation completed successfully"; this.log(`✅ Device stop completed for action ${actionState.actionId}`); // 注意:实际的设备停止操作由主进程管理器处理 // 这里只是模拟设备层面的停止确认 }, 1000); } private simulateDeviceDelete(actionState: ActionState) { this.log(`🗑️ Processing device delete operation`); if (actionState.actionParameters) { this.log(`📱 Deleting device with parameters:`); for (const param of actionState.actionParameters) { this.log(` ${param.key}: ${param.value}`); } } // 模拟设备删除过程 setTimeout(async () => { actionState.actionStatus = "FINISHED"; actionState.resultDescription = "Device delete operation completed successfully"; this.log(`✅ Device delete completed for action ${actionState.actionId}`); // 发送最后一次状态更新,包含删除完成的动作状态 this.publishState(); // 等待一小段时间确保状态发送完成 setTimeout(async () => { this.log(`🔄 Device delete action completed, initiating worker shutdown...`); // 主动关闭设备模拟器 worker await this.close(); }, 500); }, 1000); } async handleAction(actionMessage: any) { this.log(`Handling external action: ${actionMessage.actionType}`); this.processDeviceAction(actionMessage); } async reconnect() { this.log("Reconnecting to MQTT broker..."); try { if (this.client) { await this.client.endAsync(); } await this.connectMqtt(); this.log("Reconnected successfully"); this.sendMessage("status", { status: "reconnected" }); } catch (error) { this.log(`Reconnection failed: ${(error as Error).message}`); this.sendMessage("error", { error: (error as Error).message }); } } async close() { this.log("Closing device simulator..."); // 清理定时器 if (this.stateInterval) { clearInterval(this.stateInterval); } if (this.connectionInterval) { clearInterval(this.connectionInterval); } // 清理Modbus定时器和连接 if (this.modbusPollTimer) { clearInterval(this.modbusPollTimer); } if (this.modbus) { try { await this.modbus.close(); this.log("Modbus connection closed"); } catch (error) { this.log(`Error closing Modbus connection: ${(error as Error).message}`); } } // 关闭MQTT连接 if (this.client) { try { await this.client.endAsync(); this.log("MQTT connection closed"); } catch (error) { this.log(`Error closing MQTT connection: ${(error as Error).message}`); } } this.sendMessage("status", { status: "closed" }); } } // Worker消息处理 const simulator = new DeviceSimulator(); // 全局错误处理器 self.addEventListener('error', (event) => { console.error('❌ Uncaught error in device simulator worker:', event.error); // 检查是否为连接相关错误 const errorMessage = event.error?.message || event.message || 'Unknown error'; const isConnError = [ "no connection", "connection lost", "connection closed", "connection refused", "timeout", "timed out", "ECONNRESET", "ENOTCONN", "ECONNREFUSED", "ETIMEDOUT", "EPIPE", "ENETUNREACH", "EHOSTUNREACH" ].some(keyword => errorMessage.toLowerCase().includes(keyword.toLowerCase())); if (isConnError) { console.log('❌ Global error handler detected connection failure, requesting worker reset'); self.postMessage({ type: "reset", data: { reason: "uncaught_connection_error", error: `Uncaught connection error: ${errorMessage}` } } as DeviceMainMessage); } else { self.postMessage({ type: "error", data: { error: errorMessage } } as DeviceMainMessage); } }); // 全局Promise拒绝处理器 self.addEventListener('unhandledrejection', (event) => { console.error('❌ Unhandled promise rejection in device simulator worker:', event.reason); // 检查是否为连接相关错误 const errorMessage = event.reason?.message || event.reason || 'Unknown promise rejection'; const isConnError = [ "no connection", "connection lost", "connection closed", "connection refused", "timeout", "timed out", "ECONNRESET", "ENOTCONN", "ECONNREFUSED", "ETIMEDOUT", "EPIPE", "ENETUNREACH", "EHOSTUNREACH" ].some(keyword => String(errorMessage).toLowerCase().includes(keyword.toLowerCase())); if (isConnError) { console.log('❌ Global promise rejection handler detected connection failure, requesting worker reset'); self.postMessage({ type: "reset", data: { reason: "uncaught_promise_rejection", error: `Uncaught promise rejection: ${errorMessage}` } } as DeviceMainMessage); } else { self.postMessage({ type: "error", data: { error: String(errorMessage) } } as DeviceMainMessage); } // 阻止默认的错误处理 event.preventDefault(); }); self.onmessage = async (event: MessageEvent) => { const { type, data } = event.data; try { switch (type) { case "init": await simulator.initialize(data); break; case "action": await simulator.handleAction(data); break; case "reconnect": await simulator.reconnect(); break; case "close": await simulator.close(); break; default: console.warn(`Unknown message type: ${type}`); } } catch (error) { console.error(`Error handling message ${type}:`, error); // 检查是否为连接相关错误 const errorMessage = (error as Error).message; const isConnError = [ "no connection", "connection lost", "connection closed", "connection refused", "timeout", "timed out", "ECONNRESET", "ENOTCONN", "ECONNREFUSED", "ETIMEDOUT", "EPIPE", "ENETUNREACH", "EHOSTUNREACH" ].some(keyword => errorMessage.toLowerCase().includes(keyword.toLowerCase())); if (isConnError) { console.log(`❌ Message handler detected connection failure, requesting worker reset`); self.postMessage({ type: "reset", data: { reason: "message_handler_connection_error", error: `Message handler connection error: ${errorMessage}` } } as DeviceMainMessage); } else { self.postMessage({ type: "error", data: { error: errorMessage } } as DeviceMainMessage); } } };