// device_protocol_config.ts - 设备协议配置示例 // 展示如何根据设备信息自动配置Modbus参数 import { createModuleLogger } from "./debug_logger.ts"; const logger = createModuleLogger("REGISTER_CONFIG"); // 获取默认轮询间隔(基于设备品牌优化) export function getDefaultPollInterval(brandName?: string): number { // 根据设备品牌返回优化的轮询间隔 const interval = (() => { switch (brandName) { case "西门子": return 500; // 西门子设备响应较快 case "台达": return 1000; // 台达设备标准间隔 case "三菱": return 1500; // 三菱设备较保守间隔 default: return 2000; // 通用设备默认间隔 } })(); logger.debug("⏱️ Poll interval determined", { brandName: brandName || "unknown", interval: `${interval}ms`, reason: brandName ? "brand_specific" : "default" }); return interval; } // 寄存器定义接口 export interface RegisterDefinition { fnCode: string; // 功能码:1=读线圈,2=读离散输入,3=读保持寄存器,4=读输入寄存器,6=写单个寄存器 name: string; // 寄存器名称 bind?: string; // 可选:绑定 ID regCount: string; // 寄存器数量 regAddress: string; // 寄存器地址 } // 功能码映射到Modbus函数 const FUNCTION_CODE_MAP: Record = { "1": "readCoils", // 读线圈 "2": "readDiscreteInputs", // 读离散输入 "3": "readHoldingRegisters", // 读保持寄存器 "4": "readInputRegisters", // 读输入寄存器 "6": "readHoldingRegisters" // 写单个寄存器(读取时用保持寄存器) }; // 从寄存器定义创建轮询配置 export function createModbusPollConfigFromRegisters(registers: RegisterDefinition[]) { logger.info("🔄 Creating Modbus polling configuration", { totalRegisters: registers.length, registers: registers.map(r => ({ name: r.name, fnCode: r.fnCode, address: r.regAddress })) }); const pollConfig = []; const skippedRegisters = []; for (const reg of registers) { const fnCode = reg.fnCode; const modbusFunction = FUNCTION_CODE_MAP[fnCode]; logger.trace("🔍 Processing register", { name: reg.name, fnCode, address: reg.regAddress, count: reg.regCount, modbusFunction }); if (!modbusFunction) { const warning = `Unsupported function code: ${fnCode} for register ${reg.name}`; logger.warn("⚠️ " + warning, { register: reg, supportedCodes: Object.keys(FUNCTION_CODE_MAP) }); console.warn(warning); skippedRegisters.push({ register: reg.name, reason: "unsupported_function_code" }); continue; } // 只为读取功能码创建轮询配置(功能码6是写操作,但我们也可以读取它的当前值) if (["1", "2", "3", "4", "6"].includes(fnCode)) { const pollItem = { fn: modbusFunction, start: parseInt(reg.regAddress), len: parseInt(reg.regCount), mqttKey: `device/register/${reg.name}`, bind: reg.bind || reg.name }; logger.debug("✅ Created poll config for register", { register: reg.name, pollItem }); pollConfig.push(pollItem); } else { logger.warn("⚠️ Function code not supported for polling", { register: reg.name, fnCode, reason: "not_readable" }); skippedRegisters.push({ register: reg.name, reason: "not_readable" }); } } logger.info("🎯 Polling configuration creation completed", { totalInput: registers.length, totalOutput: pollConfig.length, skipped: skippedRegisters.length, skippedDetails: skippedRegisters, successRate: `${((pollConfig.length / registers.length) * 100).toFixed(1)}%` }); return pollConfig; } // 解析寄存器字符串 export function parseRegistersFromString(registersStr: string): RegisterDefinition[] { logger.debug("📝 Parsing registers string", { inputLength: registersStr.length, inputPreview: registersStr.substring(0, 100) + (registersStr.length > 100 ? '...' : '') }); try { const parsed = JSON.parse(registersStr); logger.trace("🔍 JSON parsing successful", { parsedType: typeof parsed, isArray: Array.isArray(parsed), length: Array.isArray(parsed) ? parsed.length : 'not_array' }); if (!Array.isArray(parsed)) { logger.error("❌ Parsed data is not an array", { parsedType: typeof parsed, parsed }); return []; } const registers = parsed as RegisterDefinition[]; const validRegisters = []; const invalidRegisters = []; for (const reg of registers) { const isValid = reg.fnCode && reg.name && reg.regCount && reg.regAddress; logger.trace("🔍 Validating register", { register: reg, isValid, missingFields: { fnCode: !reg.fnCode, name: !reg.name, regCount: !reg.regCount, regAddress: !reg.regAddress } }); if (isValid) { validRegisters.push(reg); } else { invalidRegisters.push({ register: reg, missingFields: Object.keys(reg).filter(key => !reg[key as keyof RegisterDefinition]) }); } } logger.info("✅ Register parsing completed", { totalInput: registers.length, validRegisters: validRegisters.length, invalidRegisters: invalidRegisters.length, invalidDetails: invalidRegisters, successRate: `${((validRegisters.length / registers.length) * 100).toFixed(1)}%` }); return validRegisters; } catch (error) { logger.error("❌ Failed to parse registers string", { error: (error as Error).message, inputString: registersStr }); console.error("Failed to parse registers string:", error); return []; } } export default { getDefaultPollInterval, createModbusPollConfigFromRegisters, parseRegistersFromString };