njs-modbus 2.1.0 → 3.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +324 -147
- package/README.zh-CN.md +380 -0
- package/dist/index.cjs +2552 -2288
- package/dist/index.d.ts +400 -233
- package/dist/index.mjs +2548 -2287
- package/dist/src/error-code.d.ts +2 -24
- package/dist/src/layers/application/abstract-application-layer.d.ts +13 -8
- package/dist/src/layers/application/ascii-application-layer.d.ts +9 -11
- package/dist/src/layers/application/rtu-application-layer.d.ts +20 -47
- package/dist/src/layers/application/tcp-application-layer.d.ts +9 -8
- package/dist/src/layers/physical/abstract-physical-layer.d.ts +43 -14
- package/dist/src/layers/physical/index.d.ts +9 -3
- package/dist/src/layers/physical/serial-physical-layer.d.ts +43 -15
- package/dist/src/layers/physical/tcp-client-physical-layer.d.ts +13 -12
- package/dist/src/layers/physical/tcp-physical-connection.d.ts +16 -0
- package/dist/src/layers/physical/tcp-server-physical-layer.d.ts +24 -14
- package/dist/src/layers/physical/udp-client-physical-layer.d.ts +33 -0
- package/dist/src/layers/physical/udp-server-physical-layer.d.ts +50 -0
- package/dist/src/layers/physical/utils.d.ts +39 -0
- package/dist/src/layers/physical/vars.d.ts +11 -0
- package/dist/src/master/master-session.d.ts +3 -3
- package/dist/src/master/master.d.ts +55 -19
- package/dist/src/slave/slave.d.ts +58 -33
- package/dist/src/types.d.ts +2 -2
- package/dist/src/utils/callback.d.ts +8 -0
- package/dist/src/utils/crc.d.ts +1 -1
- package/dist/src/utils/index.d.ts +7 -4
- package/dist/src/utils/predictRtuFrameLength.d.ts +13 -16
- package/dist/src/utils/promisify-cb.d.ts +4 -0
- package/dist/src/utils/rtu-timing.d.ts +63 -0
- package/dist/src/utils/whitelist.d.ts +11 -0
- package/dist/src/vars.d.ts +2 -0
- package/package.json +15 -8
- package/dist/src/layers/physical/udp-physical-layer.d.ts +0 -42
- package/dist/src/utils/genConnectionId.d.ts +0 -2
- package/dist/test/adu-buffer.test.d.ts +0 -1
- package/dist/test/ascii-hex-sentry.test.d.ts +0 -1
- package/dist/test/ascii-hex-validation.test.d.ts +0 -1
- package/dist/test/ascii-tcp-fragmentation.test.d.ts +0 -1
- package/dist/test/check-range.test.d.ts +0 -1
- package/dist/test/fallback-atomic.test.d.ts +0 -1
- package/dist/test/fallback-serial.test.d.ts +0 -1
- package/dist/test/fc17-serverid-validation.test.d.ts +0 -1
- package/dist/test/fc43-conformity.test.d.ts +0 -1
- package/dist/test/fc43-utf8-objects.test.d.ts +0 -1
- package/dist/test/gen-connection-id.test.d.ts +0 -1
- package/dist/test/helpers/raw-tcp.d.ts +0 -38
- package/dist/test/master-concurrent.test.d.ts +0 -1
- package/dist/test/modbus-error.test.d.ts +0 -1
- package/dist/test/physical-lifecycle.test.d.ts +0 -1
- package/dist/test/predict-rtu.test.d.ts +0 -1
- package/dist/test/rtu-custom-fc.test.d.ts +0 -1
- package/dist/test/rtu-pool-overflow.test.d.ts +0 -1
- package/dist/test/rtu-t15-timing.test.d.ts +0 -1
- package/dist/test/rtu-t35-default.test.d.ts +0 -1
- package/dist/test/rtu-t35-strict.test.d.ts +0 -1
- package/dist/test/rtu-tcp-fragmentation.test.d.ts +0 -1
- package/dist/test/serial-e2e.test.d.ts +0 -1
- package/dist/test/slave-multi-connection.test.d.ts +0 -1
- package/dist/test/slave.test.d.ts +0 -1
- package/dist/test/tcp-fragmentation.test.d.ts +0 -1
- package/dist/test/udp-multi-client.test.d.ts +0 -1
package/README.zh-CN.md
ADDED
|
@@ -0,0 +1,380 @@
|
|
|
1
|
+
# njs-modbus
|
|
2
|
+
|
|
3
|
+
[English](./README.md) | 简体中文
|
|
4
|
+
|
|
5
|
+
Node.js 的纯 JavaScript Modbus 实现。
|
|
6
|
+
|
|
7
|
+
<!-- prettier-ignore-start -->
|
|
8
|
+
[](http://www.npm-stats.com/~packages/njs-modbus)
|
|
9
|
+
[](https://www.npmjs.com/package/njs-modbus)
|
|
10
|
+
[](https://bundlephobia.com/package/njs-modbus)
|
|
11
|
+
[](https://github.com/xiejay97/njs-modbus/actions)
|
|
12
|
+
<!-- prettier-ignore-end -->
|
|
13
|
+
|
|
14
|
+
## 特性
|
|
15
|
+
|
|
16
|
+
- **协议:** Modbus TCP、RTU、ASCII
|
|
17
|
+
- **传输:** 串口、TCP 客户端/服务端、UDP 客户端/服务端
|
|
18
|
+
- **主站:** FIFO 或流水线并发请求(仅 TCP)
|
|
19
|
+
- **从站:** 每连接 FIFO 或并发处理(仅 TCP)
|
|
20
|
+
- **自定义功能码**,支持可插拔帧解析
|
|
21
|
+
- **广播**(单元号 = 0)
|
|
22
|
+
- **完整 TypeScript**
|
|
23
|
+
- **零运行时依赖**(仅在使用串口时 peer-depends `serialport`)
|
|
24
|
+
|
|
25
|
+
| 码值 | 名称 |
|
|
26
|
+
|------|------|
|
|
27
|
+
| 01 | 读取线圈 |
|
|
28
|
+
| 02 | 读取离散输入 |
|
|
29
|
+
| 03 | 读取保持寄存器 |
|
|
30
|
+
| 04 | 读取输入寄存器 |
|
|
31
|
+
| 05 | 写单个线圈 |
|
|
32
|
+
| 06 | 写单个寄存器 |
|
|
33
|
+
| 15 | 写多个线圈 |
|
|
34
|
+
| 16 | 写多个寄存器 |
|
|
35
|
+
| 17 | 报告服务器 ID |
|
|
36
|
+
| 22 | 掩码写寄存器 |
|
|
37
|
+
| 23 | 读/写多个寄存器 |
|
|
38
|
+
| 43/14 | 读取设备标识 |
|
|
39
|
+
|
|
40
|
+
## 安装
|
|
41
|
+
|
|
42
|
+
```bash
|
|
43
|
+
npm install njs-modbus
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
使用串口传输时,还需安装 peer dependency:
|
|
47
|
+
|
|
48
|
+
```bash
|
|
49
|
+
npm install serialport
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
## 快速开始
|
|
53
|
+
|
|
54
|
+
### TCP 主站
|
|
55
|
+
|
|
56
|
+
```typescript
|
|
57
|
+
import { ModbusMaster } from 'njs-modbus';
|
|
58
|
+
|
|
59
|
+
const master = new ModbusMaster({
|
|
60
|
+
physical: { type: 'TCP_CLIENT' },
|
|
61
|
+
protocol: { type: 'TCP' },
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
await master.open({ port: 502, host: '192.168.1.10' });
|
|
65
|
+
|
|
66
|
+
const res = await master.readHoldingRegisters(1, 0, 10);
|
|
67
|
+
console.log(res.data);
|
|
68
|
+
|
|
69
|
+
await master.close();
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
### TCP 从站
|
|
73
|
+
|
|
74
|
+
```typescript
|
|
75
|
+
import { ModbusSlave } from 'njs-modbus';
|
|
76
|
+
|
|
77
|
+
const slave = new ModbusSlave({
|
|
78
|
+
physical: { type: 'TCP_SERVER' },
|
|
79
|
+
protocol: { type: 'TCP' },
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
slave.add({
|
|
83
|
+
unit: 1,
|
|
84
|
+
readHoldingRegisters: (address, length) => {
|
|
85
|
+
return Array.from({ length }, (_, i) => address + i);
|
|
86
|
+
},
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
await slave.open({ port: 502 });
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
### 串口 RTU 主站
|
|
93
|
+
|
|
94
|
+
```typescript
|
|
95
|
+
const master = new ModbusMaster({
|
|
96
|
+
physical: {
|
|
97
|
+
type: 'SERIAL',
|
|
98
|
+
opts: { path: '/dev/ttyUSB0', baudRate: 9600 },
|
|
99
|
+
},
|
|
100
|
+
protocol: { type: 'RTU' },
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
await master.open();
|
|
104
|
+
const res = await master.readHoldingRegisters(1, 0, 10);
|
|
105
|
+
await master.close();
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
### RTU over TCP
|
|
109
|
+
|
|
110
|
+
```typescript
|
|
111
|
+
const master = new ModbusMaster({
|
|
112
|
+
physical: { type: 'TCP_CLIENT' },
|
|
113
|
+
protocol: { type: 'RTU' },
|
|
114
|
+
});
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
## 物理层
|
|
118
|
+
|
|
119
|
+
所有物理层暴露 `open()` / `close()`、`state` 属性,以及事件:`'open'`、`'connect'`、`'close'`、`'error'`。
|
|
120
|
+
|
|
121
|
+
| 类型 | 类 | `open(...)` 参数 |
|
|
122
|
+
|------|-------|-----------------|
|
|
123
|
+
| `SERIAL` | `SerialPhysicalLayer` | 无 |
|
|
124
|
+
| `TCP_CLIENT` | `TcpClientPhysicalLayer` | `SocketConnectOpts` |
|
|
125
|
+
| `TCP_SERVER` | `TcpServerPhysicalLayer` | `ListenOptions` |
|
|
126
|
+
| `UDP_CLIENT` | `UdpClientPhysicalLayer` | `{ port, address }` |
|
|
127
|
+
| `UDP_SERVER` | `UdpServerPhysicalLayer` | `BindOptions` |
|
|
128
|
+
| `CUSTOM` | *(用户自定义)* | *(自定义)* |
|
|
129
|
+
|
|
130
|
+
### 服务端配置
|
|
131
|
+
|
|
132
|
+
```typescript
|
|
133
|
+
const slave = new ModbusSlave({
|
|
134
|
+
physical: {
|
|
135
|
+
type: 'TCP_SERVER',
|
|
136
|
+
opts: {
|
|
137
|
+
whitelist: ['192.168.1.10', '10.0.0.5'], // IPv4-mapped IPv6 自动规范化
|
|
138
|
+
maxConnections: 10,
|
|
139
|
+
idleTimeout: 30000, // 驱逐不活跃连接,0 = 禁用
|
|
140
|
+
},
|
|
141
|
+
},
|
|
142
|
+
protocol: { type: 'TCP' },
|
|
143
|
+
});
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
### 物理层选项
|
|
147
|
+
|
|
148
|
+
| 选项 | 类型 | 说明 |
|
|
149
|
+
|--------|------|-------------|
|
|
150
|
+
| `whitelist` | `string[]` | 允许的客户端 IP。`::ffff:` 前缀自动剥离。 |
|
|
151
|
+
| `maxConnections` | `number` | 最大并发连接数。超出后新连接静默丢弃。 |
|
|
152
|
+
| `idleTimeout` | `number` | 空闲超时(毫秒),超时后驱逐连接。默认 `30000`,传 `0` 禁用。 |
|
|
153
|
+
| `socketOpts` / `serverOpts` | `object` | 透传给 Node.js `createSocket()` / `createServer()`。 |
|
|
154
|
+
|
|
155
|
+
## 主站 API
|
|
156
|
+
|
|
157
|
+
```typescript
|
|
158
|
+
new ModbusMaster({
|
|
159
|
+
physical: { type: 'TCP_CLIENT' },
|
|
160
|
+
protocol: { type: 'TCP' }, // 'TCP' | 'RTU' | 'ASCII'
|
|
161
|
+
timeout?: 1000, // 单次请求超时(毫秒)
|
|
162
|
+
concurrent?: false, // 流水线模式(仅 TCP)
|
|
163
|
+
})
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
RTU 帧选项:
|
|
167
|
+
|
|
168
|
+
```typescript
|
|
169
|
+
protocol: {
|
|
170
|
+
type: 'RTU',
|
|
171
|
+
opts: {
|
|
172
|
+
// 既可以传裸数字(毫秒),也可以传 `{ unit: 'bit' | 'ms', value: N }`。
|
|
173
|
+
// 传 `0` 显式禁用该定时器(适用于 RTU-over-TCP 等无丢包传输)。
|
|
174
|
+
intervalBetweenFrames: 20, // 20 毫秒
|
|
175
|
+
interCharTimeout: { unit: 'bit', value: 10 }, // 按 bit-time,需 baudRate
|
|
176
|
+
poolSize: 1024,
|
|
177
|
+
},
|
|
178
|
+
}
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
ASCII 选项:
|
|
182
|
+
|
|
183
|
+
```typescript
|
|
184
|
+
protocol: {
|
|
185
|
+
type: 'ASCII',
|
|
186
|
+
opts: {
|
|
187
|
+
lenientHex: true, // 接受小写十六进制 (a-f)。默认:false(按规范仅大写)
|
|
188
|
+
},
|
|
189
|
+
}
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
### 方法
|
|
193
|
+
|
|
194
|
+
| 方法 | 说明 |
|
|
195
|
+
|--------|-------------|
|
|
196
|
+
| `open(...args)` | 打开物理层。只能调用一次;`close()` 后实例永久失效。 |
|
|
197
|
+
| `close()` | 立即关闭。进行中和队列中的请求会被 reject。 |
|
|
198
|
+
| `readCoils(unit, address, length, timeout?)` | FC 01 |
|
|
199
|
+
| `readDiscreteInputs(unit, address, length, timeout?)` | FC 02 |
|
|
200
|
+
| `readHoldingRegisters(unit, address, length, timeout?)` | FC 03 |
|
|
201
|
+
| `readInputRegisters(unit, address, length, timeout?)` | FC 04 |
|
|
202
|
+
| `writeSingleCoil(unit, address, value, timeout?)` | FC 05 |
|
|
203
|
+
| `writeSingleRegister(unit, address, value, timeout?)` | FC 06 |
|
|
204
|
+
| `writeMultipleCoils(unit, address, values, timeout?)` | FC 15 |
|
|
205
|
+
| `writeMultipleRegisters(unit, address, values, timeout?)` | FC 16 |
|
|
206
|
+
| `reportServerId(unit, serverIdLength?, timeout?)` | FC 17 |
|
|
207
|
+
| `maskWriteRegister(unit, address, andMask, orMask, timeout?)` | FC 22 |
|
|
208
|
+
| `readAndWriteMultipleRegisters(unit, read, write, timeout?)` | FC 23 |
|
|
209
|
+
| `readDeviceIdentification(unit, readDeviceIDCode, objectId, timeout?)` | FC 43/14 |
|
|
210
|
+
| `sendCustomFC(unit, fc, data, timeout?)` | 发送用户自定义功能码 |
|
|
211
|
+
| `addCustomFunctionCode(cfc)` | 注册自定义功能码用于帧解析 |
|
|
212
|
+
| `removeCustomFunctionCode(fc)` | 注销自定义功能码 |
|
|
213
|
+
|
|
214
|
+
广播(`unit = 0`)resolve `void` — 从站不响应。
|
|
215
|
+
|
|
216
|
+
## 从站 API
|
|
217
|
+
|
|
218
|
+
```typescript
|
|
219
|
+
new ModbusSlave({
|
|
220
|
+
physical: { type: 'TCP_SERVER' },
|
|
221
|
+
protocol: { type: 'TCP' },
|
|
222
|
+
concurrent?: false, // 每连接并发(仅 TCP)
|
|
223
|
+
})
|
|
224
|
+
```
|
|
225
|
+
|
|
226
|
+
### 注册模型
|
|
227
|
+
|
|
228
|
+
```typescript
|
|
229
|
+
slave.add({
|
|
230
|
+
unit: 1,
|
|
231
|
+
|
|
232
|
+
// 可选:在默认分发前拦截任意功能码
|
|
233
|
+
interceptor?: (fc, data) => Buffer | undefined,
|
|
234
|
+
|
|
235
|
+
readCoils?: (address, length) => boolean[],
|
|
236
|
+
readDiscreteInputs?: (address, length) => boolean[],
|
|
237
|
+
readHoldingRegisters?: (address, length) => number[],
|
|
238
|
+
readInputRegisters?: (address, length) => number[],
|
|
239
|
+
|
|
240
|
+
writeSingleCoil?: (address, value) => void,
|
|
241
|
+
writeMultipleCoils?: (address, values) => void,
|
|
242
|
+
|
|
243
|
+
writeSingleRegister?: (address, value) => void,
|
|
244
|
+
writeMultipleRegisters?: (address, values) => void,
|
|
245
|
+
|
|
246
|
+
maskWriteRegister?: (address, andMask, orMask) => void,
|
|
247
|
+
|
|
248
|
+
reportServerId?: () => ServerId,
|
|
249
|
+
readDeviceIdentification?: () => { [index: number]: string },
|
|
250
|
+
|
|
251
|
+
// 可选地址范围校验
|
|
252
|
+
getAddressRange?: () => ({
|
|
253
|
+
discreteInputs?: [number, number] | [number, number][];
|
|
254
|
+
coils?: [number, number] | [number, number][];
|
|
255
|
+
inputRegisters?: [number, number] | [number, number][];
|
|
256
|
+
holdingRegisters?: [number, number] | [number, number][];
|
|
257
|
+
}),
|
|
258
|
+
});
|
|
259
|
+
```
|
|
260
|
+
|
|
261
|
+
缺失的处理器返回 `ILLEGAL_FUNCTION`。越界地址返回 `ILLEGAL_DATA_ADDRESS`。处理器抛出的异常变为 `SERVER_DEVICE_FAILURE`,除非错误是 `ModbusError`。
|
|
262
|
+
|
|
263
|
+
| 方法 | 说明 |
|
|
264
|
+
|--------|-------------|
|
|
265
|
+
| `add(model)` | 注册从站模型 |
|
|
266
|
+
| `remove(unit)` | 移除从站模型 |
|
|
267
|
+
| `open(...args)` | 打开物理层。一次性:close() 后不可重新打开。 |
|
|
268
|
+
| `close()` | 立即关闭 |
|
|
269
|
+
| `addCustomFunctionCode(cfc)` | 注册自定义功能码 |
|
|
270
|
+
| `removeCustomFunctionCode(fc)` | 注销自定义功能码 |
|
|
271
|
+
|
|
272
|
+
## 自定义功能码
|
|
273
|
+
|
|
274
|
+
```typescript
|
|
275
|
+
import type { CustomFunctionCode } from 'njs-modbus';
|
|
276
|
+
|
|
277
|
+
const cfc: CustomFunctionCode = {
|
|
278
|
+
fc: 0x50,
|
|
279
|
+
predictRequestLength: (buffer) => {
|
|
280
|
+
if (buffer.length < 2) return null;
|
|
281
|
+
return 4 + buffer[1];
|
|
282
|
+
},
|
|
283
|
+
predictResponseLength: (buffer) => {
|
|
284
|
+
if (buffer.length < 2) return null;
|
|
285
|
+
return 4 + buffer[1];
|
|
286
|
+
},
|
|
287
|
+
handle: async (data, unit) => {
|
|
288
|
+
return Buffer.from([data[1], data[0]]);
|
|
289
|
+
},
|
|
290
|
+
};
|
|
291
|
+
|
|
292
|
+
// 从站:帧解析 + 分发
|
|
293
|
+
slave.addCustomFunctionCode(cfc);
|
|
294
|
+
|
|
295
|
+
// 主站:仅帧解析
|
|
296
|
+
master.addCustomFunctionCode(cfc);
|
|
297
|
+
const response = await master.sendCustomFC(1, 0x50, [0xab, 0xcd]);
|
|
298
|
+
```
|
|
299
|
+
|
|
300
|
+
## 错误处理
|
|
301
|
+
|
|
302
|
+
```typescript
|
|
303
|
+
import { ErrorCode, ModbusError, getErrorByCode } from 'njs-modbus';
|
|
304
|
+
|
|
305
|
+
// ErrorCode.ILLEGAL_FUNCTION = 0x01
|
|
306
|
+
// ErrorCode.ILLEGAL_DATA_ADDRESS = 0x02
|
|
307
|
+
// ErrorCode.ILLEGAL_DATA_VALUE = 0x03
|
|
308
|
+
// ErrorCode.SERVER_DEVICE_FAILURE = 0x04
|
|
309
|
+
// ErrorCode.ACKNOWLEDGE = 0x05
|
|
310
|
+
// ErrorCode.SERVER_DEVICE_BUSY = 0x06
|
|
311
|
+
// ErrorCode.MEMORY_PARITY_ERROR = 0x08
|
|
312
|
+
// ErrorCode.GATEWAY_PATH_UNAVAILABLE = 0x0a
|
|
313
|
+
// ErrorCode.GATEWAY_TARGET_DEVICE_FAILED_TO_RESPOND = 0x0b
|
|
314
|
+
|
|
315
|
+
slave.add({
|
|
316
|
+
unit: 1,
|
|
317
|
+
readHoldingRegisters: (address) => {
|
|
318
|
+
if (address > 100) {
|
|
319
|
+
throw getErrorByCode(ErrorCode.ILLEGAL_DATA_ADDRESS);
|
|
320
|
+
}
|
|
321
|
+
return [42];
|
|
322
|
+
},
|
|
323
|
+
});
|
|
324
|
+
```
|
|
325
|
+
|
|
326
|
+
## 性能
|
|
327
|
+
|
|
328
|
+
与 [jsmodbus](https://github.com/Cloud-Automation/node-modbus) 和 [modbus-serial](https://github.com/yaacov/node-modbus-serial) 的对比。
|
|
329
|
+
|
|
330
|
+
| 指标 | njs-modbus | jsmodbus | modbus-serial |
|
|
331
|
+
|------|-----------|----------|---------------|
|
|
332
|
+
| TCP 吞吐量 | **5,527 ops/sec** | 3,239 (0.59x) | 371 (0.07x) |
|
|
333
|
+
| TCP P99 延迟 | **2.71 ms** | 4.73 ms (1.75x) | 12.48 ms (4.61x) |
|
|
334
|
+
| TCP CPU 效率 | **1,116 µs/op** | 1,950 (1.75x) | 16,715 (14.98x) |
|
|
335
|
+
| 并发 (8 连接) | **5,274 ops/sec** | 3,416 (0.65x) | 1,815 (0.34x) |
|
|
336
|
+
| RTU CPU 效率 | **1,762 µs/op** | 1,760 (1.00x) | 2,144 (1.22x) |
|
|
337
|
+
| TCP 响应编码 | **2.04M ops/sec** | 373K (0.18x) | 411K (0.20x) |
|
|
338
|
+
| TCP 响应解码 | **1.91M ops/sec** | 538K (0.28x) | 231K (0.12x) |
|
|
339
|
+
|
|
340
|
+
<details>
|
|
341
|
+
<summary>完整基准测试结果</summary>
|
|
342
|
+
|
|
343
|
+
Node.js v24.15.0 · linux x64 · 3 次运行 × 300 秒 · [完整报告](./benchmark/RESULTS.md)
|
|
344
|
+
|
|
345
|
+
### TCP 吞吐量(单连接)
|
|
346
|
+
|
|
347
|
+
```
|
|
348
|
+
njs-modbus │ 5,527 ops/sec 🏆 CPU: 1,116 µs/op
|
|
349
|
+
jsmodbus │ 3,239 ops/sec (0.59x) CPU: 1,950 µs/op
|
|
350
|
+
modbus-serial │ 371 ops/sec (0.07x) CPU: 16,715 µs/op
|
|
351
|
+
```
|
|
352
|
+
|
|
353
|
+
### 并发(8 连接)
|
|
354
|
+
|
|
355
|
+
```
|
|
356
|
+
njs-modbus │ 5,274 ops/sec 🏆 CPU: 1,392 µs/op
|
|
357
|
+
jsmodbus │ 3,416 ops/sec (0.65x) CPU: 2,131 µs/op
|
|
358
|
+
modbus-serial │ 1,815 ops/sec (0.34x) CPU: 3,962 µs/op
|
|
359
|
+
```
|
|
360
|
+
|
|
361
|
+
### RTU 串口(模拟 115200 波特率)
|
|
362
|
+
|
|
363
|
+
```
|
|
364
|
+
njs-modbus │ 44 ops/sec CPU: 1,762 µs/op
|
|
365
|
+
jsmodbus │ 44 ops/sec CPU: 1,760 µs/op
|
|
366
|
+
modbus-serial │ 44 ops/sec CPU: 2,144 µs/op
|
|
367
|
+
```
|
|
368
|
+
|
|
369
|
+
### 编码 / 解码(CPU 微基准测试)
|
|
370
|
+
|
|
371
|
+
```
|
|
372
|
+
tcpResEncode: njs-modbus 2.04M ops/sec (jsmodbus 0.18x, modbus-serial 0.20x)
|
|
373
|
+
tcpResDecode: njs-modbus 1.91M ops/sec (jsmodbus 0.28x, modbus-serial 0.12x)
|
|
374
|
+
```
|
|
375
|
+
|
|
376
|
+
</details>
|
|
377
|
+
|
|
378
|
+
## 许可证
|
|
379
|
+
|
|
380
|
+
[](/LICENSE)
|