Compare commits
3 Commits
3cc201d62f
...
78b7da10c2
| Author | SHA1 | Date |
|---|---|---|
|
|
78b7da10c2 | 1 week ago |
|
|
279341ea68 | 1 week ago |
|
|
8f8e0f94b4 | 1 week ago |
5 changed files with 814 additions and 0 deletions
@ -0,0 +1,298 @@ |
|||||||
|
# USB Serial JS 桥接文档 |
||||||
|
|
||||||
|
## 概述 |
||||||
|
|
||||||
|
`usbSerial` 对象由 Android 端注入 WebView,提供 USB 串口设备的发现、探测、连接及数据读写能力。 |
||||||
|
|
||||||
|
## 注入对象 |
||||||
|
|
||||||
|
Android 端在初始化 WebView 时会注入全局对象 `usbSerial`,web 端可直接通过 `window.usbSerial` 或 `usbSerial` 访问。 |
||||||
|
|
||||||
|
## 方法 |
||||||
|
|
||||||
|
### 设备发现 |
||||||
|
|
||||||
|
| 方法 | 描述 | 参数 | 返回值 | |
||||||
|
| -------------------- | -------------------------- | ---- | ------ | |
||||||
|
| startDiscovery() | 开始 USB 设备发现 | 无 | 无 | |
||||||
|
| stopDiscovery() | 停止 USB 设备发现 | 无 | 无 | |
||||||
|
| getDiscoveredDevices() | 获取已发现的设备列表 | 无 | 空数组 `"[]"`(预留接口) | |
||||||
|
|
||||||
|
### 设备探测 |
||||||
|
|
||||||
|
| 方法 | 描述 | 参数 | 返回值 | |
||||||
|
| ------------------------------------------------------------ | -------------------------- | ----------------------------------------------------------------------- | ------ | |
||||||
|
| deviceProbe(deviceName, portNumber, baudRate, hexCmd, timeoutMs) | 手动探测指定设备的串口响应 | deviceName: 设备名称 (String)<br>portNumber: 端口号 (Int)<br>baudRate: 波特率 (Int)<br>hexCmd: 探测命令十六进制字符串,不发送时传空串 `""` (String)<br>timeoutMs: 超时毫秒数 (Int) | 无 | |
||||||
|
|
||||||
|
### 设备连接 |
||||||
|
|
||||||
|
| 方法 | 描述 | 参数 | 返回值 | |
||||||
|
| -------------------------------------------- | ------------------ | ---------------------------------------------------------------------------- | ------ | |
||||||
|
| deviceOpen(deviceName, portNumber, baudRate) | 打开设备串口连接 | deviceName: 设备名称 (String)<br>portNumber: 端口号 (Int)<br>baudRate: 波特率 (Int) | 无 | |
||||||
|
| deviceClose(deviceName, portNumber) | 关闭设备串口连接 | deviceName: 设备名称 (String)<br>portNumber: 端口号 (Int) | 无 | |
||||||
|
|
||||||
|
### 数据写入 |
||||||
|
|
||||||
|
| 方法 | 描述 | 参数 | 返回值 | |
||||||
|
| ---------------------------------------------- | ------------------ | ------------------------------------------------------------------------------------- | ------ | |
||||||
|
| deviceWrite(deviceName, portNumber, hexData) | 向已连接的设备发送数据 | deviceName: 设备名称 (String)<br>portNumber: 端口号 (Int)<br>hexData: 十六进制字符串 (String) | 无 | |
||||||
|
|
||||||
|
> **注意**:`deviceWrite` 仅在连接状态为 `CONNECTED` 时才会实际发送数据,且需要 hexData 非空。 |
||||||
|
|
||||||
|
--- |
||||||
|
|
||||||
|
## 回调函数 |
||||||
|
|
||||||
|
web 端需要在 `usbSerial` 对象上实现以下回调函数,以接收 Android 端的异步通知。所有回调函数均无返回值。 |
||||||
|
|
||||||
|
> 回调函数注册方式: |
||||||
|
> ```js |
||||||
|
> usbSerial.onDeviceAttached = function(jsonStr) { |
||||||
|
> const data = JSON.parse(jsonStr); |
||||||
|
> console.log("设备插入:", data); |
||||||
|
> }; |
||||||
|
> ``` |
||||||
|
|
||||||
|
### 设备插拔回调 |
||||||
|
|
||||||
|
| 方法 | 描述 | 参数 | |
||||||
|
| ------------------------------- | ------------------ | -------------------------------- | |
||||||
|
| onDeviceAttached(jsonStr) | USB 设备插入时回调 | JSON 字符串,结构见下文 | |
||||||
|
| onDeviceDetached(jsonStr) | USB 设备拔出时回调 | JSON 字符串,结构见下文 | |
||||||
|
|
||||||
|
### 设备发现回调 |
||||||
|
|
||||||
|
| 方法 | 描述 | 参数 | |
||||||
|
| ----------------------------------- | ---------------------------------- | -------------------------------- | |
||||||
|
| onDeviceDiscovered(jsonStr) | 设备匹配到探测协议时回调 | JSON 字符串,结构见下文 | |
||||||
|
| onDeviceProbeResult(jsonStr) | `deviceProbe()` 执行结果回调 | JSON 字符串,结构见下文 | |
||||||
|
|
||||||
|
### 设备连接回调 |
||||||
|
|
||||||
|
| 方法 | 描述 | 参数 | |
||||||
|
| ------------------------------- | ------------------------ | -------------------------------- | |
||||||
|
| onDeviceOpened(jsonStr) | `deviceOpen()` 执行结果 | JSON 字符串,结构见下文 | |
||||||
|
| onDeviceState(jsonStr) | 连接状态变化时回调 | JSON 字符串,结构见下文 | |
||||||
|
|
||||||
|
### 数据接收回调 |
||||||
|
|
||||||
|
| 方法 | 描述 | 参数 | |
||||||
|
| --------------------------- | -------------------------- | -------------------------------- | |
||||||
|
| onDeviceData(jsonStr) | 收到设备上报数据时回调 | JSON 字符串,结构见下文 | |
||||||
|
|
||||||
|
--- |
||||||
|
|
||||||
|
## 数据结构 |
||||||
|
|
||||||
|
### onDeviceAttached 回调 |
||||||
|
|
||||||
|
```json |
||||||
|
{ |
||||||
|
"deviceName": "/dev/bus/usb/001/002", |
||||||
|
"deviceId": 1002, |
||||||
|
"ports": [1, 2] |
||||||
|
} |
||||||
|
``` |
||||||
|
|
||||||
|
| 字段 | 类型 | 描述 | |
||||||
|
| ------------ | -------- | ------------------------ | |
||||||
|
| deviceName | String | 设备路径名称 | |
||||||
|
| deviceId | Number | USB 设备 ID | |
||||||
|
| ports | Array | 可用串口端口号列表 | |
||||||
|
|
||||||
|
### onDeviceDetached 回调 |
||||||
|
|
||||||
|
```json |
||||||
|
{ |
||||||
|
"deviceName": "/dev/bus/usb/001/002", |
||||||
|
"deviceId": 1002 |
||||||
|
} |
||||||
|
``` |
||||||
|
|
||||||
|
| 字段 | 类型 | 描述 | |
||||||
|
| ------------ | -------- | ------------------------ | |
||||||
|
| deviceName | String | 设备路径名称 | |
||||||
|
| deviceId | Number | USB 设备 ID | |
||||||
|
|
||||||
|
### onDeviceDiscovered 回调 |
||||||
|
|
||||||
|
```json |
||||||
|
{ |
||||||
|
"deviceName": "/dev/bus/usb/001/002", |
||||||
|
"port": 1, |
||||||
|
"probeId": "my-probe", |
||||||
|
"baudRate": 9600, |
||||||
|
"responseHex": "01020304" |
||||||
|
} |
||||||
|
``` |
||||||
|
|
||||||
|
| 字段 | 类型 | 描述 | |
||||||
|
| ------------ | -------- | -------------------------------- | |
||||||
|
| deviceName | String | 设备路径名称 | |
||||||
|
| port | Number | 端口号 | |
||||||
|
| probeId | String | 匹配的探测协议 ID | |
||||||
|
| baudRate | Number | 探测到的波特率 | |
||||||
|
| responseHex | String | 设备响应数据的十六进制字符串 | |
||||||
|
|
||||||
|
### onDeviceProbeResult 回调 |
||||||
|
|
||||||
|
```json |
||||||
|
{ |
||||||
|
"deviceName": "/dev/bus/usb/001/002", |
||||||
|
"port": 1, |
||||||
|
"baudRate": 115200, |
||||||
|
"success": true, |
||||||
|
"responseHex": "AABBCC", |
||||||
|
"error": "" |
||||||
|
} |
||||||
|
``` |
||||||
|
|
||||||
|
| 字段 | 类型 | 描述 | |
||||||
|
| ------------ | -------- | ------------------------------------------ | |
||||||
|
| deviceName | String | 设备路径名称 | |
||||||
|
| port | Number | 端口号 | |
||||||
|
| baudRate | Number | 探测使用的波特率 | |
||||||
|
| success | Boolean | 探测是否成功 | |
||||||
|
| responseHex | String | 设备响应数据的十六进制字符串(失败时为空串) | |
||||||
|
| error | String | 错误信息(成功时为空串) | |
||||||
|
|
||||||
|
### onDeviceOpened 回调 |
||||||
|
|
||||||
|
```json |
||||||
|
{ |
||||||
|
"deviceName": "/dev/bus/usb/001/002", |
||||||
|
"port": 1, |
||||||
|
"success": true, |
||||||
|
"error": "" |
||||||
|
} |
||||||
|
``` |
||||||
|
|
||||||
|
| 字段 | 类型 | 描述 | |
||||||
|
| ------------ | -------- | ------------------------------------------ | |
||||||
|
| deviceName | String | 设备路径名称 | |
||||||
|
| port | Number | 端口号 | |
||||||
|
| success | Boolean | 打开是否成功 | |
||||||
|
| error | String | 错误信息(成功时为空串);可能值:`"Device not found"`、`"Port not found"` | |
||||||
|
|
||||||
|
### onDeviceState 回调 |
||||||
|
|
||||||
|
```json |
||||||
|
{ |
||||||
|
"deviceName": "/dev/bus/usb/001/002", |
||||||
|
"port": 1, |
||||||
|
"state": "CONNECTED" |
||||||
|
} |
||||||
|
``` |
||||||
|
|
||||||
|
| 字段 | 类型 | 描述 | |
||||||
|
| ------------ | -------- | ------------------------------------------------------ | |
||||||
|
| deviceName | String | 设备路径名称 | |
||||||
|
| port | Number | 端口号 | |
||||||
|
| state | String | 连接状态:`"CONNECTED"` \| `"CONNECTING"` \| `"DISCONNECTED"` | |
||||||
|
|
||||||
|
### onDeviceData 回调 |
||||||
|
|
||||||
|
```json |
||||||
|
{ |
||||||
|
"deviceName": "/dev/bus/usb/001/002", |
||||||
|
"port": 1, |
||||||
|
"hexData": "01020304AABB" |
||||||
|
} |
||||||
|
``` |
||||||
|
|
||||||
|
| 字段 | 类型 | 描述 | |
||||||
|
| ------------ | -------- | ------------------------------ | |
||||||
|
| deviceName | String | 设备路径名称 | |
||||||
|
| port | Number | 端口号 | |
||||||
|
| hexData | String | 收到的数据,十六进制大写字符串 | |
||||||
|
|
||||||
|
--- |
||||||
|
|
||||||
|
## 连接状态枚举 |
||||||
|
|
||||||
|
| 状态 | 触发时机 | |
||||||
|
| ----------------- | ------------------------------------- | |
||||||
|
| `CONNECTING` | `deviceOpen` 后正在尝试打开串口 | |
||||||
|
| `CONNECTED` | 串口打开成功,可以发送和接收数据 | |
||||||
|
| `DISCONNECTED` | 串口未连接、连接失败或被拔出 | |
||||||
|
|
||||||
|
--- |
||||||
|
|
||||||
|
## 典型使用流程 |
||||||
|
|
||||||
|
### 1. 启动发现 |
||||||
|
|
||||||
|
```js |
||||||
|
// 注册回调 |
||||||
|
usbSerial.onDeviceAttached = function(json) { |
||||||
|
const dev = JSON.parse(json); |
||||||
|
console.log("发现设备:", dev.deviceName, "ports:", dev.ports); |
||||||
|
}; |
||||||
|
|
||||||
|
usbSerial.onDeviceDetached = function(json) { |
||||||
|
const dev = JSON.parse(json); |
||||||
|
console.log("设备拔除:", dev.deviceName); |
||||||
|
}; |
||||||
|
|
||||||
|
usbSerial.onDeviceState = function(json) { |
||||||
|
const s = JSON.parse(json); |
||||||
|
console.log("连接状态:", s.deviceName, s.port, s.state); |
||||||
|
}; |
||||||
|
|
||||||
|
usbSerial.onDeviceData = function(json) { |
||||||
|
const d = JSON.parse(json); |
||||||
|
console.log("收到数据:", d.hexData); |
||||||
|
}; |
||||||
|
|
||||||
|
// 开始发现 |
||||||
|
usbSerial.startDiscovery(); |
||||||
|
``` |
||||||
|
|
||||||
|
### 2. 手动探测设备 |
||||||
|
|
||||||
|
```js |
||||||
|
usbSerial.onDeviceProbeResult = function(json) { |
||||||
|
const result = JSON.parse(json); |
||||||
|
if (result.success) { |
||||||
|
console.log("探测成功, 响应:", result.responseHex); |
||||||
|
} else { |
||||||
|
console.log("探测失败:", result.error); |
||||||
|
} |
||||||
|
}; |
||||||
|
|
||||||
|
// 对 /dev/bus/usb/001/002 端口1 以 9600 波特率发 "010300000001" 超时 500ms |
||||||
|
usbSerial.deviceProbe("/dev/bus/usb/001/002", 1, 9600, "010300000001", 500); |
||||||
|
``` |
||||||
|
|
||||||
|
### 3. 打开设备进行通信 |
||||||
|
|
||||||
|
```js |
||||||
|
usbSerial.onDeviceOpened = function(json) { |
||||||
|
const result = JSON.parse(json); |
||||||
|
if (result.success) { |
||||||
|
console.log("设备打开成功"); |
||||||
|
// 发送数据(十六进制字符串) |
||||||
|
usbSerial.deviceWrite(result.deviceName, result.port, "010300000001"); |
||||||
|
} else { |
||||||
|
console.log("打开失败:", result.error); |
||||||
|
} |
||||||
|
}; |
||||||
|
|
||||||
|
usbSerial.deviceOpen("/dev/bus/usb/001/002", 1, 9600); |
||||||
|
``` |
||||||
|
|
||||||
|
### 4. 关闭设备 |
||||||
|
|
||||||
|
```js |
||||||
|
usbSerial.deviceClose("/dev/bus/usb/001/002", 1); |
||||||
|
``` |
||||||
|
|
||||||
|
--- |
||||||
|
|
||||||
|
## 注意事项 |
||||||
|
|
||||||
|
1. **串口参数固定**:数据位 8、停止位 1、无校验位,不可配置。 |
||||||
|
2. **设备键值**:内部以 `"deviceName:portNumber"` 唯一标识一个连接,同一设备不同端口可同时打开。 |
||||||
|
3. **拔除自动清理**:设备拔出时对应连接会自动销毁,无需手动调用 `deviceClose`。 |
||||||
|
4. **线程安全**:所有方法可以在 JS 主线程直接调用,Android 侧会处理线程调度。 |
||||||
|
5. **`getDiscoveredDevices`** 为预留接口,当前仅返回 `"[]"`,后续版本会实现。 |
||||||
@ -0,0 +1,239 @@ |
|||||||
|
/** |
||||||
|
* USB Serial JS 桥接模块 |
||||||
|
* 实现 USB 串口设备的发现、探测、连接及数据读写 |
||||||
|
* 接口规范: docs/webview-usb-serial.md |
||||||
|
*/ |
||||||
|
const { SerialPort } = require('serialport') |
||||||
|
const { ipcMain } = require('electron') |
||||||
|
|
||||||
|
class UsbSerialBridge { |
||||||
|
constructor(mainWindow) { |
||||||
|
this.mainWindow = mainWindow |
||||||
|
this.activeConnections = new Map() |
||||||
|
this.discoveredDevices = new Set() |
||||||
|
this.activeProbes = new Map() |
||||||
|
this._setupIPC() |
||||||
|
} |
||||||
|
|
||||||
|
_emit(event, data) { |
||||||
|
if (this.mainWindow && !this.mainWindow.isDestroyed()) { |
||||||
|
this.mainWindow.webContents.send(event, JSON.stringify(data)) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
_setupIPC() { |
||||||
|
ipcMain.on('usb-start-discovery', () => this._startDiscovery()) |
||||||
|
ipcMain.on('usb-stop-discovery', () => this._stopDiscovery()) |
||||||
|
ipcMain.handle('usb-get-discovered', () => this._getDiscoveredDevices()) |
||||||
|
ipcMain.on('usb-device-probe', (_e, p) => this._deviceProbe(p)) |
||||||
|
ipcMain.on('usb-device-open', (_e, p) => this._deviceOpen(p)) |
||||||
|
ipcMain.on('usb-device-close', (_e, p) => this._deviceClose(p)) |
||||||
|
ipcMain.on('usb-device-write', (_e, p) => this._deviceWrite(p)) |
||||||
|
} |
||||||
|
|
||||||
|
async _startDiscovery() { |
||||||
|
console.log('[usb] 开始发现串口设备...') |
||||||
|
try { |
||||||
|
const ports = await SerialPort.list() |
||||||
|
for (const p of ports) { |
||||||
|
if (!this.discoveredDevices.has(p.path)) { |
||||||
|
this.discoveredDevices.add(p.path) |
||||||
|
console.log('[usb] 发现设备:', p.path) |
||||||
|
this._emit('usb-callback-device-attached', { |
||||||
|
deviceName: p.path, |
||||||
|
deviceId: parseInt(p.vendorId || '0', 16) || 0, |
||||||
|
ports: [1] |
||||||
|
}) |
||||||
|
} |
||||||
|
} |
||||||
|
console.log('[usb] 发现完成, 共', ports.length, '个设备') |
||||||
|
} catch (err) { |
||||||
|
console.error('[usb] 发现失败:', err.message) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
_stopDiscovery() { |
||||||
|
console.log('[usb] 停止发现') |
||||||
|
} |
||||||
|
|
||||||
|
_getDiscoveredDevices() { |
||||||
|
return JSON.stringify([]) |
||||||
|
} |
||||||
|
|
||||||
|
async _deviceProbe({ deviceName, portNumber, baudRate, hexCmd, timeoutMs }) { |
||||||
|
const probeId = deviceName + ':' + portNumber |
||||||
|
console.log('[usb] 探测:', deviceName, 'port=', portNumber, 'baud=', baudRate) |
||||||
|
|
||||||
|
if (this.activeProbes.has(probeId)) { |
||||||
|
const old = this.activeProbes.get(probeId) |
||||||
|
clearTimeout(old.timeout) |
||||||
|
try { old.port.close() } catch {} |
||||||
|
this.activeProbes.delete(probeId) |
||||||
|
} |
||||||
|
|
||||||
|
try { |
||||||
|
const port = new SerialPort({ |
||||||
|
path: deviceName, |
||||||
|
baudRate: baudRate || 9600, |
||||||
|
dataBits: 8, |
||||||
|
parity: 'none', |
||||||
|
stopBits: 1, |
||||||
|
autoOpen: false, |
||||||
|
dtr: false, |
||||||
|
rts: false |
||||||
|
}) |
||||||
|
|
||||||
|
await new Promise((resolve, reject) => { |
||||||
|
port.open(err => err ? reject(new Error(err.message)) : resolve()) |
||||||
|
}) |
||||||
|
|
||||||
|
let responseHex = '' |
||||||
|
const timeout = setTimeout(() => { |
||||||
|
this.activeProbes.delete(probeId) |
||||||
|
try { port.close() } catch {} |
||||||
|
const success = responseHex.length > 0 |
||||||
|
console.log('[usb] 探测' + (success ? '成功' : '超时') + ':', deviceName, '响应=' + (responseHex || '无')) |
||||||
|
this._emit('usb-callback-device-probe-result', { |
||||||
|
deviceName, port: portNumber, baudRate, |
||||||
|
success, responseHex, error: success ? '' : 'No response' |
||||||
|
}) |
||||||
|
}, timeoutMs || 1000) |
||||||
|
|
||||||
|
port.on('data', (data) => { |
||||||
|
responseHex += data.toString('hex').toUpperCase() |
||||||
|
}) |
||||||
|
|
||||||
|
port.on('error', (err) => { |
||||||
|
console.error('[usb] 探测错误:', err.message) |
||||||
|
}) |
||||||
|
|
||||||
|
this.activeProbes.set(probeId, { timeout, port }) |
||||||
|
|
||||||
|
if (hexCmd) { |
||||||
|
const cmdBuf = Buffer.from(hexCmd, 'hex') |
||||||
|
port.write(cmdBuf, (err) => { |
||||||
|
if (err) { |
||||||
|
clearTimeout(timeout) |
||||||
|
this.activeProbes.delete(probeId) |
||||||
|
try { port.close() } catch {} |
||||||
|
this._emit('usb-callback-device-probe-result', { |
||||||
|
deviceName, port: portNumber, baudRate, |
||||||
|
success: false, responseHex: '', error: err.message |
||||||
|
}) |
||||||
|
} else { |
||||||
|
console.log('[usb] 已发送探测命令:', hexCmd) |
||||||
|
} |
||||||
|
}) |
||||||
|
} else { |
||||||
|
console.log('[usb] 不发送探测命令, 等待自动上报...') |
||||||
|
} |
||||||
|
} catch (err) { |
||||||
|
console.error('[usb] 探测失败:', err.message) |
||||||
|
this._emit('usb-callback-device-probe-result', { |
||||||
|
deviceName, port: portNumber, baudRate, |
||||||
|
success: false, responseHex: '', error: err.message |
||||||
|
}) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
async _deviceOpen({ deviceName, portNumber, baudRate }) { |
||||||
|
const connKey = deviceName + ':' + portNumber |
||||||
|
console.log('[usb] 打开:', connKey, 'baud=', baudRate) |
||||||
|
|
||||||
|
if (this.activeConnections.has(connKey)) { |
||||||
|
try { this.activeConnections.get(connKey).port.close() } catch {} |
||||||
|
this.activeConnections.delete(connKey) |
||||||
|
} |
||||||
|
|
||||||
|
try { |
||||||
|
const port = new SerialPort({ |
||||||
|
path: deviceName, |
||||||
|
baudRate: baudRate || 9600, |
||||||
|
dataBits: 8, |
||||||
|
parity: 'none', |
||||||
|
stopBits: 1, |
||||||
|
autoOpen: false, |
||||||
|
dtr: false, |
||||||
|
rts: false |
||||||
|
}) |
||||||
|
|
||||||
|
await new Promise((resolve, reject) => { |
||||||
|
port.open(err => err ? reject(new Error(err.message)) : resolve()) |
||||||
|
}) |
||||||
|
|
||||||
|
const entry = { port, deviceName, portNumber } |
||||||
|
this.activeConnections.set(connKey, entry) |
||||||
|
|
||||||
|
console.log('[usb] 打开成功:', connKey) |
||||||
|
this._emit('usb-callback-device-opened', { |
||||||
|
deviceName, port: portNumber, success: true, error: '' |
||||||
|
}) |
||||||
|
this._emit('usb-callback-device-state', { |
||||||
|
deviceName, port: portNumber, state: 'CONNECTED' |
||||||
|
}) |
||||||
|
|
||||||
|
port.on('data', (data) => { |
||||||
|
this._emit('usb-callback-device-data', { |
||||||
|
deviceName, |
||||||
|
port: portNumber, |
||||||
|
hexData: data.toString('hex').toUpperCase() |
||||||
|
}) |
||||||
|
}) |
||||||
|
|
||||||
|
port.on('error', (err) => { |
||||||
|
console.error('[usb] 连接错误:', err.message) |
||||||
|
}) |
||||||
|
|
||||||
|
port.on('close', () => { |
||||||
|
console.log('[usb] 连接关闭:', connKey) |
||||||
|
this.activeConnections.delete(connKey) |
||||||
|
this._emit('usb-callback-device-state', { |
||||||
|
deviceName, port: portNumber, state: 'DISCONNECTED' |
||||||
|
}) |
||||||
|
}) |
||||||
|
} catch (err) { |
||||||
|
console.error('[usb] 打开失败:', err.message) |
||||||
|
this._emit('usb-callback-device-opened', { |
||||||
|
deviceName, port: portNumber, success: false, error: err.message |
||||||
|
}) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
_deviceClose({ deviceName, portNumber }) { |
||||||
|
const connKey = deviceName + ':' + portNumber |
||||||
|
console.log('[usb] 关闭:', connKey) |
||||||
|
if (this.activeConnections.has(connKey)) { |
||||||
|
const entry = this.activeConnections.get(connKey) |
||||||
|
try { entry.port.close() } catch {} |
||||||
|
this.activeConnections.delete(connKey) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
_deviceWrite({ deviceName, portNumber, hexData }) { |
||||||
|
const connKey = deviceName + ':' + portNumber |
||||||
|
console.log('[usb] 写入:', connKey, 'hex=' + hexData) |
||||||
|
|
||||||
|
if (!this.activeConnections.has(connKey)) { |
||||||
|
console.error('[usb] 写入失败:', connKey, '未连接') |
||||||
|
return |
||||||
|
} |
||||||
|
|
||||||
|
const entry = this.activeConnections.get(connKey) |
||||||
|
const buf = Buffer.from(hexData, 'hex') |
||||||
|
entry.port.write(buf, (err) => { |
||||||
|
if (err) { |
||||||
|
console.error('[usb] 写入错误:', err.message) |
||||||
|
} |
||||||
|
}) |
||||||
|
} |
||||||
|
|
||||||
|
disconnectAll() { |
||||||
|
for (const [key, entry] of this.activeConnections) { |
||||||
|
console.log('[usb] 断开:', key) |
||||||
|
try { entry.port.close() } catch {} |
||||||
|
} |
||||||
|
this.activeConnections.clear() |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
module.exports = { UsbSerialBridge } |
||||||
Loading…
Reference in new issue