This commit is contained in:
Toby 2025-04-15 19:42:04 +08:00
commit 051046e891
7 changed files with 2269 additions and 0 deletions

4
.gitignore vendored Normal file
View File

@ -0,0 +1,4 @@
dist
node_modules
/serial.log
/serial-proxy.exe

8
config.json Normal file
View File

@ -0,0 +1,8 @@
{
"hostPort": "COM10",
"devicePort": "COM11",
"baudRate": 19200,
"dataBits": 8,
"stopBits": 1,
"parity": "none"
}

Binary file not shown.

2133
package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

21
package.json Normal file
View File

@ -0,0 +1,21 @@
{
"name": "serial-proxy",
"version": "1.0.0",
"main": "dist/index.js",
"bin": "dist/index.js",
"scripts": {
"build": "tsc",
"dev": "ts-node src/idnex.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"
},
"dependencies": {
"serialport": "^11.0.0"
},
"devDependencies": {
"pkg": "^5.8.1",
"ts-node": "^10.9.2",
"typescript": "^5.0.0"
}
}

93
src/index.ts Normal file
View File

@ -0,0 +1,93 @@
import fs from 'fs';
import path from 'path';
import { SerialPort } from 'serialport';
interface Config {
hostPort: string;
devicePort: string;
baudRate: number;
logFile: string;
dataBits: 8 | 5 | 6 | 7 | undefined;
stopBits: 1 | 2 | 1.5 | undefined;
parity: 'none' | 'even' | 'odd' | 'mark' | 'space';
}
function loadConfig(): Config {
const configPath = path.resolve(__dirname, '../config.json');
if (!fs.existsSync(configPath)) {
console.error('❌ 配置文件 config.json 未找到');
process.exit(1);
}
const configText = fs.readFileSync(configPath, 'utf-8');
try {
const raw = JSON.parse(configText);
const config: Config = {
hostPort: raw.hostPort,
devicePort: raw.devicePort,
baudRate: raw.baudRate,
logFile: raw.logFile || path.resolve(process.cwd(), 'serial.log'),
dataBits: raw.dataBits || 8,
stopBits: raw.stopBits || 1,
parity: raw.parity || 'none'
};
return config;
} catch (err) {
console.error('❌ 配置文件格式错误:', err);
process.exit(1);
}
}
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 {
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}`;
}
function logAndPrint(stream: fs.WriteStream, direction: string, data: Buffer) {
const line = formatLogLine(direction, data);
stream.write(line + '\n');
console.log(line);
}
function startProxy(config: Config) {
const { hostPort, devicePort, baudRate, dataBits, stopBits, parity, logFile } = config;
const logStream = fs.createWriteStream(logFile, { flags: 'a' });
const host = new SerialPort({ path: hostPort, baudRate, dataBits, stopBits, parity });
const device = new SerialPort({ path: devicePort, baudRate, dataBits, stopBits, parity });
host.on('data', data => {
logAndPrint(logStream, '[主机 → 设备]', data);
device.write(data);
});
device.on('data', data => {
logAndPrint(logStream, '[设备 → 主机]', data);
host.write(data);
});
host.on('error', err => console.error('主机端串口错误:', err.message));
device.on('error', err => console.error('设备端串口错误:', err.message));
console.log(`✅ 串口代理已启动:
${hostPort}
${devicePort}
${baudRate}
${logFile}`);
}
startProxy(loadConfig());

10
tsconfig.json Normal file
View File

@ -0,0 +1,10 @@
{
"compilerOptions": {
"target": "ES2020",
"module": "commonjs",
"outDir": "dist",
"strict": true,
"esModuleInterop": true
},
"include": ["src"]
}