feat: modbus enableCRC
This commit is contained in:
parent
051046e891
commit
d67cda3462
10
config.json
10
config.json
@ -1,8 +1,10 @@
|
|||||||
{
|
{
|
||||||
"hostPort": "COM10",
|
"hostPort": "/dev/cu.usbserial-350",
|
||||||
"devicePort": "COM11",
|
"devicePort": "/dev/cu.usbserial-230",
|
||||||
"baudRate": 19200,
|
"baudRate": 9600,
|
||||||
"dataBits": 8,
|
"dataBits": 8,
|
||||||
"stopBits": 1,
|
"stopBits": 1,
|
||||||
"parity": "none"
|
"parity": "none",
|
||||||
|
"protocol": "custom",
|
||||||
|
"enableCRC": false
|
||||||
}
|
}
|
@ -5,10 +5,11 @@
|
|||||||
"bin": "dist/index.js",
|
"bin": "dist/index.js",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"build": "tsc",
|
"build": "tsc",
|
||||||
"dev": "ts-node src/idnex.ts",
|
"dev": "ts-node src/index.ts",
|
||||||
"start": "node dist/index.js",
|
"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",
|
"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": {
|
"dependencies": {
|
||||||
"serialport": "^11.0.0"
|
"serialport": "^11.0.0"
|
||||||
|
57
src/index.ts
57
src/index.ts
@ -10,6 +10,8 @@ interface Config {
|
|||||||
dataBits: 8 | 5 | 6 | 7 | undefined;
|
dataBits: 8 | 5 | 6 | 7 | undefined;
|
||||||
stopBits: 1 | 2 | 1.5 | undefined;
|
stopBits: 1 | 2 | 1.5 | undefined;
|
||||||
parity: 'none' | 'even' | 'odd' | 'mark' | 'space';
|
parity: 'none' | 'even' | 'odd' | 'mark' | 'space';
|
||||||
|
protocol: 'custom' | 'modbus';
|
||||||
|
enableCRC?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
function loadConfig(): Config {
|
function loadConfig(): Config {
|
||||||
@ -30,7 +32,9 @@ function loadConfig(): Config {
|
|||||||
logFile: raw.logFile || path.resolve(process.cwd(), 'serial.log'),
|
logFile: raw.logFile || path.resolve(process.cwd(), 'serial.log'),
|
||||||
dataBits: raw.dataBits || 8,
|
dataBits: raw.dataBits || 8,
|
||||||
stopBits: raw.stopBits || 1,
|
stopBits: raw.stopBits || 1,
|
||||||
parity: raw.parity || 'none'
|
parity: raw.parity || 'none',
|
||||||
|
protocol: raw.protocol === 'modbus' ? 'modbus' : 'custom',
|
||||||
|
enableCRC: raw.enableCRC === true
|
||||||
};
|
};
|
||||||
|
|
||||||
return config;
|
return config;
|
||||||
@ -40,30 +44,52 @@ function loadConfig(): Config {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
function formatTime(date: Date): string {
|
function formatTime(date: Date): string {
|
||||||
const pad = (n: number) => n.toString().padStart(2, '0');
|
const pad = (n: number) => n.toString().padStart(2, '0');
|
||||||
return `${date.getFullYear()}-${pad(date.getMonth() + 1)}-${pad(date.getDate())} `
|
return `${date.getFullYear()}-${pad(date.getMonth() + 1)}-${pad(date.getDate())} `
|
||||||
+ `${pad(date.getHours())}:${pad(date.getMinutes())}:${pad(date.getSeconds())}`;
|
+ `${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 timestamp = formatTime(new Date());
|
||||||
const hex = data.toString('hex').toUpperCase().replace(/(.{2})/g, '$1 ').trim();
|
const hex = data.toString('hex').toUpperCase().replace(/(.{2})/g, '$1 ').trim();
|
||||||
|
|
||||||
|
if (protocol === 'modbus') {
|
||||||
|
return `${timestamp} ${direction} HEX: ${hex}`;
|
||||||
|
} else {
|
||||||
const ascii = data.toString().replace(/[^\x20-\x7E]/g, '.');
|
const ascii = data.toString().replace(/[^\x20-\x7E]/g, '.');
|
||||||
return `${timestamp} ${direction} HEX: ${hex} ASCII: ${ascii}`;
|
return `${timestamp} ${direction} HEX: ${hex} ASCII: ${ascii}`;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function logAndPrint(stream: fs.WriteStream, direction: string, data: Buffer) {
|
function logAndPrint(stream: fs.WriteStream, direction: string, data: Buffer, protocol: 'custom' | 'modbus') {
|
||||||
const line = formatLogLine(direction, data);
|
const line = formatLogLine(direction, data, protocol);
|
||||||
stream.write(line + '\n');
|
stream.write(line + '\n');
|
||||||
console.log(line);
|
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) {
|
function startProxy(config: Config) {
|
||||||
|
const { hostPort, devicePort, baudRate, dataBits, stopBits, parity, logFile, protocol, enableCRC } = config;
|
||||||
|
|
||||||
const { hostPort, devicePort, baudRate, dataBits, stopBits, parity, logFile } = config;
|
|
||||||
|
|
||||||
const logStream = fs.createWriteStream(logFile, { flags: 'a' });
|
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 });
|
const device = new SerialPort({ path: devicePort, baudRate, dataBits, stopBits, parity });
|
||||||
|
|
||||||
host.on('data', data => {
|
host.on('data', data => {
|
||||||
logAndPrint(logStream, '[主机 → 设备]', data);
|
let outData = data;
|
||||||
device.write(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 => {
|
device.on('data', data => {
|
||||||
logAndPrint(logStream, '[设备 → 主机]', data);
|
logAndPrint(logStream, '[设备 → 主机]', data, protocol);
|
||||||
host.write(data);
|
host.write(data);
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -87,6 +120,8 @@ function startProxy(config: Config) {
|
|||||||
主机端:${hostPort}
|
主机端:${hostPort}
|
||||||
设备端:${devicePort}
|
设备端:${devicePort}
|
||||||
波特率:${baudRate}
|
波特率:${baudRate}
|
||||||
|
协议类型:${protocol}
|
||||||
|
CRC 校验:${enableCRC ? '启用' : '关闭'}
|
||||||
日志文件:${logFile}`);
|
日志文件:${logFile}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user