init
This commit is contained in:
commit
051046e891
4
.gitignore
vendored
Normal file
4
.gitignore
vendored
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
dist
|
||||||
|
node_modules
|
||||||
|
/serial.log
|
||||||
|
/serial-proxy.exe
|
8
config.json
Normal file
8
config.json
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
{
|
||||||
|
"hostPort": "COM10",
|
||||||
|
"devicePort": "COM11",
|
||||||
|
"baudRate": 19200,
|
||||||
|
"dataBits": 8,
|
||||||
|
"stopBits": 1,
|
||||||
|
"parity": "none"
|
||||||
|
}
|
BIN
nodejs/node-v20.18.0-win-x64.zip
Normal file
BIN
nodejs/node-v20.18.0-win-x64.zip
Normal file
Binary file not shown.
2133
package-lock.json
generated
Normal file
2133
package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
21
package.json
Normal file
21
package.json
Normal 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
93
src/index.ts
Normal 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
10
tsconfig.json
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"target": "ES2020",
|
||||||
|
"module": "commonjs",
|
||||||
|
"outDir": "dist",
|
||||||
|
"strict": true,
|
||||||
|
"esModuleInterop": true
|
||||||
|
},
|
||||||
|
"include": ["src"]
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user