api-amr/device_protocol_config.ts

190 lines
6.0 KiB
TypeScript
Raw Normal View History

2025-06-04 19:15:02 +08:00
// 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<string, "readHoldingRegisters" | "readInputRegisters" | "readCoils" | "readDiscreteInputs"> = {
"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
};