From d67cda3462650f8bdc126b156c3a01187fc5a2e3 Mon Sep 17 00:00:00 2001 From: Toby <2287769986@qq.com> Date: Wed, 16 Apr 2025 18:42:23 +0800 Subject: [PATCH] feat: modbus enableCRC --- config.json | 10 +++++---- package.json | 5 +++-- src/index.ts | 63 ++++++++++++++++++++++++++++++++++++++++------------ 3 files changed, 58 insertions(+), 20 deletions(-) diff --git a/config.json b/config.json index 34132f4..12a5686 100644 --- a/config.json +++ b/config.json @@ -1,8 +1,10 @@ { - "hostPort": "COM10", - "devicePort": "COM11", - "baudRate": 19200, + "hostPort": "/dev/cu.usbserial-350", + "devicePort": "/dev/cu.usbserial-230", + "baudRate": 9600, "dataBits": 8, "stopBits": 1, - "parity": "none" + "parity": "none", + "protocol": "custom", + "enableCRC": false } \ No newline at end of file diff --git a/package.json b/package.json index e4e6bbd..0f956df 100644 --- a/package.json +++ b/package.json @@ -5,10 +5,11 @@ "bin": "dist/index.js", "scripts": { "build": "tsc", - "dev": "ts-node src/idnex.ts", + "dev": "ts-node src/index.ts", "start": "node dist/index.js", "exe": "nexe dist/index.js --build --resource /Users/daoyouyun/.codes/serial-proxy/nodejs/node-v20.18.0-win-x64.zip --output serial-proxy.exe", - "pkg": "pkg . --targets=node18-win-x64 --output=serial-proxy.exe" + "pkg": "pkg . --targets=node18-win-x64 --output=serial-proxy.exe", + "pkg-arm": "pkg dist/index.js --targets=node18-win-arm64 --output=serial-proxy.exe" }, "dependencies": { "serialport": "^11.0.0" diff --git a/src/index.ts b/src/index.ts index e7b788c..c67fc80 100644 --- a/src/index.ts +++ b/src/index.ts @@ -10,6 +10,8 @@ interface Config { dataBits: 8 | 5 | 6 | 7 | undefined; stopBits: 1 | 2 | 1.5 | undefined; parity: 'none' | 'even' | 'odd' | 'mark' | 'space'; + protocol: 'custom' | 'modbus'; + enableCRC?: boolean; } function loadConfig(): Config { @@ -30,7 +32,9 @@ function loadConfig(): Config { logFile: raw.logFile || path.resolve(process.cwd(), 'serial.log'), dataBits: raw.dataBits || 8, stopBits: raw.stopBits || 1, - parity: raw.parity || 'none' + parity: raw.parity || 'none', + protocol: raw.protocol === 'modbus' ? 'modbus' : 'custom', + enableCRC: raw.enableCRC === true }; return config; @@ -40,30 +44,52 @@ function loadConfig(): Config { } } - function formatTime(date: Date): string { const pad = (n: number) => n.toString().padStart(2, '0'); return `${date.getFullYear()}-${pad(date.getMonth() + 1)}-${pad(date.getDate())} ` + `${pad(date.getHours())}:${pad(date.getMinutes())}:${pad(date.getSeconds())}`; } -function formatLogLine(direction: string, data: Buffer): string { +function formatLogLine(direction: string, data: Buffer, protocol: 'custom' | 'modbus'): string { const timestamp = formatTime(new Date()); const hex = data.toString('hex').toUpperCase().replace(/(.{2})/g, '$1 ').trim(); - const ascii = data.toString().replace(/[^\x20-\x7E]/g, '.'); - return `${timestamp} ${direction} HEX: ${hex} ASCII: ${ascii}`; + + if (protocol === 'modbus') { + return `${timestamp} ${direction} HEX: ${hex}`; + } else { + const ascii = data.toString().replace(/[^\x20-\x7E]/g, '.'); + return `${timestamp} ${direction} HEX: ${hex} ASCII: ${ascii}`; + } } -function logAndPrint(stream: fs.WriteStream, direction: string, data: Buffer) { - const line = formatLogLine(direction, data); +function logAndPrint(stream: fs.WriteStream, direction: string, data: Buffer, protocol: 'custom' | 'modbus') { + const line = formatLogLine(direction, data, protocol); stream.write(line + '\n'); console.log(line); } +function calculateCRC16(buffer: Buffer): Buffer { + let crc = 0xFFFF; + + for (let i = 0; i < buffer.length; i++) { + crc ^= buffer[i]; + for (let j = 0; j < 8; j++) { + if ((crc & 0x0001) !== 0) { + crc >>= 1; + crc ^= 0xA001; + } else { + crc >>= 1; + } + } + } + + const lo = crc & 0xFF; + const hi = (crc >> 8) & 0xFF; + return Buffer.from([lo, hi]); +} + function startProxy(config: Config) { - - - const { hostPort, devicePort, baudRate, dataBits, stopBits, parity, logFile } = config; + const { hostPort, devicePort, baudRate, dataBits, stopBits, parity, logFile, protocol, enableCRC } = config; const logStream = fs.createWriteStream(logFile, { flags: 'a' }); @@ -71,12 +97,19 @@ function startProxy(config: Config) { const device = new SerialPort({ path: devicePort, baudRate, dataBits, stopBits, parity }); host.on('data', data => { - logAndPrint(logStream, '[主机 → 设备]', data); - device.write(data); + let outData = data; + + if (protocol === 'modbus' && enableCRC) { + const crc = calculateCRC16(data); + outData = Buffer.concat([data, crc]); + } + + logAndPrint(logStream, '[主机 → 设备]', outData, protocol); + device.write(outData); }); device.on('data', data => { - logAndPrint(logStream, '[设备 → 主机]', data); + logAndPrint(logStream, '[设备 → 主机]', data, protocol); host.write(data); }); @@ -87,7 +120,9 @@ function startProxy(config: Config) { 主机端:${hostPort} 设备端:${devicePort} 波特率:${baudRate} + 协议类型:${protocol} + CRC 校验:${enableCRC ? '启用' : '关闭'} 日志文件:${logFile}`); } -startProxy(loadConfig()); +startProxy(loadConfig()); \ No newline at end of file