api-amr/device_protocol_config.ts
2025-06-04 19:15:02 +08:00

190 lines
6.0 KiB
TypeScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// 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
};