njs-modbus 3.1.1 → 3.3.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 +84 -35
- package/README.zh-CN.md +84 -35
- package/dist/index.cjs +1291 -497
- package/dist/index.d.ts +99 -31
- package/dist/index.mjs +1291 -498
- package/dist/utils.cjs +536 -0
- package/dist/utils.d.ts +163 -0
- package/dist/utils.mjs +522 -0
- package/package.json +22 -2
- package/dist/src/error-code.d.ts +0 -17
- package/dist/src/index.d.ts +0 -7
- package/dist/src/layers/application/abstract-application-layer.d.ts +0 -26
- package/dist/src/layers/application/ascii-application-layer.d.ts +0 -23
- package/dist/src/layers/application/index.d.ts +0 -6
- package/dist/src/layers/application/rtu-application-layer.d.ts +0 -34
- package/dist/src/layers/application/tcp-application-layer.d.ts +0 -16
- package/dist/src/layers/physical/abstract-physical-layer.d.ts +0 -50
- package/dist/src/layers/physical/index.d.ts +0 -12
- package/dist/src/layers/physical/serial-physical-layer.d.ts +0 -70
- package/dist/src/layers/physical/tcp-client-physical-layer.d.ts +0 -20
- package/dist/src/layers/physical/tcp-physical-connection.d.ts +0 -16
- package/dist/src/layers/physical/tcp-server-physical-layer.d.ts +0 -29
- package/dist/src/layers/physical/udp-client-physical-layer.d.ts +0 -34
- package/dist/src/layers/physical/udp-server-physical-layer.d.ts +0 -51
- package/dist/src/layers/physical/utils.d.ts +0 -39
- package/dist/src/layers/physical/vars.d.ts +0 -11
- package/dist/src/master/index.d.ts +0 -3
- package/dist/src/master/master-session.d.ts +0 -18
- package/dist/src/master/master.d.ts +0 -140
- package/dist/src/slave/index.d.ts +0 -2
- package/dist/src/slave/slave.d.ts +0 -119
- package/dist/src/types.d.ts +0 -54
- package/dist/src/utils/bitsToMs.d.ts +0 -13
- package/dist/src/utils/callback.d.ts +0 -8
- package/dist/src/utils/checkRange.d.ts +0 -1
- package/dist/src/utils/crc.d.ts +0 -1
- package/dist/src/utils/index.d.ts +0 -11
- package/dist/src/utils/isUint8.d.ts +0 -8
- package/dist/src/utils/lrc.d.ts +0 -1
- package/dist/src/utils/predictRtuFrameLength.d.ts +0 -17
- package/dist/src/utils/promisify-cb.d.ts +0 -4
- package/dist/src/utils/rtu-timing.d.ts +0 -63
- package/dist/src/utils/whitelist.d.ts +0 -11
- package/dist/src/vars.d.ts +0 -49
package/README.md
CHANGED
|
@@ -15,8 +15,8 @@ A pure JavaScript implementation of Modbus for Node.js.
|
|
|
15
15
|
|
|
16
16
|
- **Protocols:** Modbus TCP, RTU, ASCII
|
|
17
17
|
- **Transports:** Serial, TCP client/server, UDP client/server
|
|
18
|
-
- **Master:** FIFO or pipelined concurrent requests (TCP
|
|
19
|
-
- **Slave:** Per-connection FIFO or concurrent processing (TCP
|
|
18
|
+
- **Master:** FIFO or pipelined concurrent requests (protocol must be TCP)
|
|
19
|
+
- **Slave:** Per-connection FIFO or concurrent processing (protocol must be TCP)
|
|
20
20
|
- **Custom function codes** with pluggable framing
|
|
21
21
|
- **Broadcasting** (unit = 0)
|
|
22
22
|
- **Full TypeScript**
|
|
@@ -39,6 +39,8 @@ A pure JavaScript implementation of Modbus for Node.js.
|
|
|
39
39
|
|
|
40
40
|
## Installation
|
|
41
41
|
|
|
42
|
+
Requires **Node.js ≥ 18.19**.
|
|
43
|
+
|
|
42
44
|
```bash
|
|
43
45
|
npm install njs-modbus
|
|
44
46
|
```
|
|
@@ -152,6 +154,26 @@ const slave = new ModbusSlave({
|
|
|
152
154
|
| `idleTimeout` | `number` | Idle timeout in ms before evicting a connection. Default `30000`. Pass `0` to disable. |
|
|
153
155
|
| `socketOpts` / `serverOpts` | `object` | Forwarded to Node.js `createSocket()` / `createServer()`. |
|
|
154
156
|
|
|
157
|
+
## Custom Physical Layer
|
|
158
|
+
|
|
159
|
+
Any byte-oriented transport can be used by implementing `AbstractPhysicalLayer` and `AbstractPhysicalConnection`, then passing `{ type: 'CUSTOM', layer: yourLayer }`.
|
|
160
|
+
|
|
161
|
+
```typescript
|
|
162
|
+
import { AbstractPhysicalLayer, AbstractPhysicalConnection } from 'njs-modbus';
|
|
163
|
+
|
|
164
|
+
class MyPhysicalLayer extends AbstractPhysicalLayer { /* open / close */ }
|
|
165
|
+
class MyConnection extends AbstractPhysicalConnection { /* write / destroy, emit 'data' */ }
|
|
166
|
+
|
|
167
|
+
const master = new ModbusMaster({
|
|
168
|
+
physical: { type: 'CUSTOM', layer: new MyPhysicalLayer() },
|
|
169
|
+
protocol: { type: 'TCP' },
|
|
170
|
+
});
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
See [`examples/websocket`](./examples/websocket) for a complete **Modbus TCP over WebSocket** implementation — client/server physical layers with a working demo.
|
|
174
|
+
|
|
175
|
+
See [`examples/bluetooth`](./examples/bluetooth) for a **Modbus TCP over BLE** implementation using the Nordic UART Service (NUS) — includes a loopback test that runs without real BLE hardware.
|
|
176
|
+
|
|
155
177
|
## Master API
|
|
156
178
|
|
|
157
179
|
```typescript
|
|
@@ -159,7 +181,7 @@ new ModbusMaster({
|
|
|
159
181
|
physical: { type: 'TCP_CLIENT' },
|
|
160
182
|
protocol: { type: 'TCP' }, // 'TCP' | 'RTU' | 'ASCII'
|
|
161
183
|
timeout?: 1000, // per-request timeout (ms)
|
|
162
|
-
concurrent?: false, // pipelined mode (TCP only)
|
|
184
|
+
concurrent?: false, // pipelined mode (protocol: TCP only)
|
|
163
185
|
})
|
|
164
186
|
```
|
|
165
187
|
|
|
@@ -220,7 +242,7 @@ Broadcast (`unit = 0`) resolves `void` — slaves never respond.
|
|
|
220
242
|
new ModbusSlave({
|
|
221
243
|
physical: { type: 'TCP_SERVER' },
|
|
222
244
|
protocol: { type: 'TCP' },
|
|
223
|
-
concurrent?: false, // per-connection concurrent (TCP only)
|
|
245
|
+
concurrent?: false, // per-connection concurrent (protocol: TCP only)
|
|
224
246
|
})
|
|
225
247
|
```
|
|
226
248
|
|
|
@@ -233,10 +255,10 @@ slave.add({
|
|
|
233
255
|
// Optional: intercept any FC before default dispatch
|
|
234
256
|
interceptor?: (fc, data) => Buffer | undefined,
|
|
235
257
|
|
|
236
|
-
readCoils?: (address, length) => boolean[],
|
|
237
|
-
readDiscreteInputs?: (address, length) => boolean[],
|
|
238
|
-
readHoldingRegisters?: (address, length) => number[],
|
|
239
|
-
readInputRegisters?: (address, length) => number[],
|
|
258
|
+
readCoils?: (address, length) => boolean[] | Uint8Array,
|
|
259
|
+
readDiscreteInputs?: (address, length) => boolean[] | Uint8Array,
|
|
260
|
+
readHoldingRegisters?: (address, length) => number[] | Uint16Array,
|
|
261
|
+
readInputRegisters?: (address, length) => number[] | Uint16Array,
|
|
240
262
|
|
|
241
263
|
writeSingleCoil?: (address, value) => void,
|
|
242
264
|
writeMultipleCoils?: (address, values) => void,
|
|
@@ -272,18 +294,20 @@ Missing handlers return `ILLEGAL_FUNCTION`. Out-of-range addresses return `ILLEG
|
|
|
272
294
|
|
|
273
295
|
## Custom Function Codes
|
|
274
296
|
|
|
297
|
+
`predictRequestLength` / `predictResponseLength` receive a shared pool buffer and byte offsets (`start`, `end`). Use `end - start` for bounds checks rather than `buffer.length`.
|
|
298
|
+
|
|
275
299
|
```typescript
|
|
276
300
|
import type { CustomFunctionCode } from 'njs-modbus';
|
|
277
301
|
|
|
278
302
|
const cfc: CustomFunctionCode = {
|
|
279
303
|
fc: 0x50,
|
|
280
|
-
predictRequestLength: (buffer) => {
|
|
281
|
-
if (
|
|
282
|
-
return 4 + buffer[1];
|
|
304
|
+
predictRequestLength: (buffer, start, end) => {
|
|
305
|
+
if (end - start < 2) return null;
|
|
306
|
+
return 4 + buffer[start + 1];
|
|
283
307
|
},
|
|
284
|
-
predictResponseLength: (buffer) => {
|
|
285
|
-
if (
|
|
286
|
-
return 4 + buffer[1];
|
|
308
|
+
predictResponseLength: (buffer, start, end) => {
|
|
309
|
+
if (end - start < 2) return null;
|
|
310
|
+
return 4 + buffer[start + 1];
|
|
287
311
|
},
|
|
288
312
|
handle: async (data, unit) => {
|
|
289
313
|
return Buffer.from([data[1], data[0]]);
|
|
@@ -330,50 +354,75 @@ Benchmarked against [jsmodbus](https://github.com/Cloud-Automation/node-modbus)
|
|
|
330
354
|
|
|
331
355
|
| Metric | njs-modbus | jsmodbus | modbus-serial |
|
|
332
356
|
|--------|-----------|----------|---------------|
|
|
333
|
-
| TCP Throughput | **
|
|
334
|
-
| TCP P99 Latency | **
|
|
335
|
-
|
|
|
336
|
-
|
|
|
337
|
-
|
|
|
338
|
-
| TCP
|
|
339
|
-
|
|
|
357
|
+
| TCP Throughput | **70,544 ops/sec** | 39,827 (0.56x) | 786 (0.01x) |
|
|
358
|
+
| TCP P99 Latency | **53 µs** | 98 µs (1.83x) | 2,331 µs (43.7x) |
|
|
359
|
+
| Concurrent (8 conn) | **75,810 ops/sec** | 41,754 (0.55x) | 5,620 (0.07x) |
|
|
360
|
+
| RTU Serial (115200 baud) | **44 ops/sec** | 44 ops/sec | 45 ops/sec |
|
|
361
|
+
| TCP Response Encode | **6.57M ops/sec** | 1.22M (0.19x) | 1.18M (0.18x) |
|
|
362
|
+
| TCP Response Decode | **6.78M ops/sec** | 1.94M (0.29x) | 0.67M (0.10x) |
|
|
363
|
+
| FC01 Read Coils | **85,170 ops/sec** | 617 (0.01x) | 861 (0.01x) |
|
|
340
364
|
|
|
341
365
|
<details>
|
|
342
366
|
<summary>Full benchmark results</summary>
|
|
343
367
|
|
|
344
|
-
Node.js v24.
|
|
368
|
+
Node.js v24.16.0 · AMD Ryzen 7 9800X3D · linux x64 · 3 runs × 120 s · [full report](./benchmark/RESULTS.md)
|
|
345
369
|
|
|
346
370
|
### TCP Throughput (Single Connection)
|
|
347
371
|
|
|
348
372
|
```
|
|
349
|
-
njs-modbus │
|
|
350
|
-
jsmodbus │
|
|
351
|
-
modbus-serial │
|
|
373
|
+
njs-modbus │ ██████████████████████████████ 70,544 ops/sec 🏆 p99: 53 µs CPU: 91 µs/op
|
|
374
|
+
jsmodbus │ █████████████████░░░░░░░░░░░░░ 39,827 ops/sec (0.56x) p99: 98 µs CPU: 161 µs/op
|
|
375
|
+
modbus-serial │ ░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ 786 ops/sec (0.01x) p99: 2,331 µs CPU: 8,176 µs/op
|
|
352
376
|
```
|
|
353
377
|
|
|
354
378
|
### Concurrent (8 Connections)
|
|
355
379
|
|
|
356
380
|
```
|
|
357
|
-
njs-modbus │
|
|
358
|
-
jsmodbus │
|
|
359
|
-
modbus-serial │
|
|
381
|
+
njs-modbus │ ██████████████████████████████ 75,810 ops/sec 🏆 p99: 258 µs CPU: 91 µs/op
|
|
382
|
+
jsmodbus │ █████████████████░░░░░░░░░░░░░ 41,754 ops/sec (0.55x) p99: 543 µs CPU: 167 µs/op
|
|
383
|
+
modbus-serial │ ██░░░░░░░░░░░░░░░░░░░░░░░░░░░░ 5,620 ops/sec (0.07x) p99: 3,180 µs CPU: 1,237 µs/op
|
|
360
384
|
```
|
|
361
385
|
|
|
362
|
-
### RTU Serial (115200 baud
|
|
386
|
+
### RTU Serial (115200 baud via socat PTY)
|
|
363
387
|
|
|
364
388
|
```
|
|
365
|
-
njs-modbus │ 44 ops/sec CPU:
|
|
366
|
-
jsmodbus │ 44 ops/sec
|
|
367
|
-
modbus-serial │
|
|
389
|
+
njs-modbus │ 44 ops/sec p99: 993 µs CPU: 729 µs/op GC: 2,613 ns/op
|
|
390
|
+
jsmodbus │ 44 ops/sec p99: 1,019 µs CPU: 694 µs/op GC: 2,609 ns/op
|
|
391
|
+
modbus-serial │ 45 ops/sec p99: 3,228 µs CPU: 765 µs/op GC: 3,864 ns/op
|
|
368
392
|
```
|
|
369
393
|
|
|
370
|
-
### Encode / Decode (CPU Micro-benchmark)
|
|
394
|
+
### Frame Encode / Decode (CPU Micro-benchmark)
|
|
371
395
|
|
|
372
396
|
```
|
|
373
|
-
|
|
374
|
-
|
|
397
|
+
tcpReqEncode: njs-modbus 6.90M ops/sec 🏆 jsmodbus 0.78x modbus-serial 0.19x
|
|
398
|
+
tcpResEncode: njs-modbus 6.57M ops/sec 🏆 jsmodbus 0.19x modbus-serial 0.18x
|
|
399
|
+
tcpReqDecode: njs-modbus 6.80M ops/sec 🏆 jsmodbus 0.81x modbus-serial 0.17x
|
|
400
|
+
tcpResDecode: njs-modbus 6.78M ops/sec 🏆 jsmodbus 0.29x modbus-serial 0.10x
|
|
401
|
+
rtuReqEncode: njs-modbus 7.26M ops/sec 🏆 jsmodbus 0.35x modbus-serial 0.76x
|
|
402
|
+
rtuReqDecode: njs-modbus 3.97M ops/sec jsmodbus 1.21x modbus-serial 1.78x 🏆
|
|
403
|
+
asciiReqEncode: njs-modbus 5.64M ops/sec 🏆 modbus-serial 0.57x
|
|
404
|
+
asciiReqDecode: njs-modbus 3.37M ops/sec 🏆 modbus-serial 0.34x
|
|
375
405
|
```
|
|
376
406
|
|
|
407
|
+
### Per-Function-Code TCP Throughput (Normal Payload)
|
|
408
|
+
|
|
409
|
+
| Function Code | njs-modbus | jsmodbus | modbus-serial |
|
|
410
|
+
|---------------|-----------|----------|---------------|
|
|
411
|
+
| FC01 Read Coils | **85,170** 🏆 | 617 (0.01x) | 861 (0.01x) |
|
|
412
|
+
| FC02 Read Discrete Inputs | **85,570** 🏆 | 525 (0.01x) | 861 (0.01x) |
|
|
413
|
+
| FC03 Read Holding Registers | **75,344** 🏆 | 40,081 (0.53x) | 852 (0.01x) |
|
|
414
|
+
| FC04 Read Input Registers | **75,757** 🏆 | 49,524 (0.65x) | 866 (0.01x) |
|
|
415
|
+
| FC05 Write Single Coil | **92,852** 🏆 | 53,521 (0.58x) | 871 (0.01x) |
|
|
416
|
+
| FC06 Write Single Register | **92,864** 🏆 | 53,684 (0.58x) | 872 (0.01x) |
|
|
417
|
+
| FC15 Write Multiple Coils | **86,434** 🏆 | 319 (0.00x) | 869 (0.01x) |
|
|
418
|
+
| FC16 Write Multiple Registers | **76,053** 🏆 | 39,356 (0.52x) | 852 (0.01x) |
|
|
419
|
+
| FC17 Report Server ID | **74,697** 🏆 | — | — |
|
|
420
|
+
| FC22 Mask Write Register | **90,717** 🏆 | — | — |
|
|
421
|
+
| FC23 Read/Write Multiple Registers | **85,652** 🏆 | — | — |
|
|
422
|
+
| FC43 Read Device Identification | **78,030** 🏆 | — | 865 (0.01x) |
|
|
423
|
+
|
|
424
|
+
> FC17 / FC22 / FC23 are not supported by jsmodbus or modbus-serial.
|
|
425
|
+
|
|
377
426
|
</details>
|
|
378
427
|
|
|
379
428
|
## License
|
package/README.zh-CN.md
CHANGED
|
@@ -15,8 +15,8 @@ Node.js 的纯 JavaScript Modbus 实现。
|
|
|
15
15
|
|
|
16
16
|
- **协议:** Modbus TCP、RTU、ASCII
|
|
17
17
|
- **传输:** 串口、TCP 客户端/服务端、UDP 客户端/服务端
|
|
18
|
-
- **主站:** FIFO
|
|
19
|
-
- **从站:** 每连接 FIFO
|
|
18
|
+
- **主站:** FIFO 或流水线并发请求(协议必须为 TCP)
|
|
19
|
+
- **从站:** 每连接 FIFO 或并发处理(协议必须为 TCP)
|
|
20
20
|
- **自定义功能码**,支持可插拔帧解析
|
|
21
21
|
- **广播**(单元号 = 0)
|
|
22
22
|
- **完整 TypeScript**
|
|
@@ -39,6 +39,8 @@ Node.js 的纯 JavaScript Modbus 实现。
|
|
|
39
39
|
|
|
40
40
|
## 安装
|
|
41
41
|
|
|
42
|
+
需要 **Node.js ≥ 18.19**。
|
|
43
|
+
|
|
42
44
|
```bash
|
|
43
45
|
npm install njs-modbus
|
|
44
46
|
```
|
|
@@ -152,6 +154,26 @@ const slave = new ModbusSlave({
|
|
|
152
154
|
| `idleTimeout` | `number` | 空闲超时(毫秒),超时后驱逐连接。默认 `30000`,传 `0` 禁用。 |
|
|
153
155
|
| `socketOpts` / `serverOpts` | `object` | 透传给 Node.js `createSocket()` / `createServer()`。 |
|
|
154
156
|
|
|
157
|
+
## 自定义物理层
|
|
158
|
+
|
|
159
|
+
任何面向字节的传输层都可以通过实现 `AbstractPhysicalLayer` 和 `AbstractPhysicalConnection` 来使用,然后传入 `{ type: 'CUSTOM', layer: yourLayer }`。
|
|
160
|
+
|
|
161
|
+
```typescript
|
|
162
|
+
import { AbstractPhysicalLayer, AbstractPhysicalConnection } from 'njs-modbus';
|
|
163
|
+
|
|
164
|
+
class MyPhysicalLayer extends AbstractPhysicalLayer { /* open / close */ }
|
|
165
|
+
class MyConnection extends AbstractPhysicalConnection { /* write / destroy, emit 'data' */ }
|
|
166
|
+
|
|
167
|
+
const master = new ModbusMaster({
|
|
168
|
+
physical: { type: 'CUSTOM', layer: new MyPhysicalLayer() },
|
|
169
|
+
protocol: { type: 'TCP' },
|
|
170
|
+
});
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
查看 [`examples/websocket`](./examples/websocket) 获取完整的 **WebSocket 承载 Modbus TCP** 实现——包含客户端/服务端物理层及可运行的演示。
|
|
174
|
+
|
|
175
|
+
查看 [`examples/bluetooth`](./examples/bluetooth) 获取 **BLE 承载 Modbus TCP** 实现(基于 Nordic UART Service),包含无需真实蓝牙硬件即可运行的回环测试。
|
|
176
|
+
|
|
155
177
|
## 主站 API
|
|
156
178
|
|
|
157
179
|
```typescript
|
|
@@ -159,7 +181,7 @@ new ModbusMaster({
|
|
|
159
181
|
physical: { type: 'TCP_CLIENT' },
|
|
160
182
|
protocol: { type: 'TCP' }, // 'TCP' | 'RTU' | 'ASCII'
|
|
161
183
|
timeout?: 1000, // 单次请求超时(毫秒)
|
|
162
|
-
concurrent?: false, //
|
|
184
|
+
concurrent?: false, // 流水线模式(协议:仅 TCP)
|
|
163
185
|
})
|
|
164
186
|
```
|
|
165
187
|
|
|
@@ -219,7 +241,7 @@ protocol: {
|
|
|
219
241
|
new ModbusSlave({
|
|
220
242
|
physical: { type: 'TCP_SERVER' },
|
|
221
243
|
protocol: { type: 'TCP' },
|
|
222
|
-
concurrent?: false, //
|
|
244
|
+
concurrent?: false, // 每连接并发(协议:仅 TCP)
|
|
223
245
|
})
|
|
224
246
|
```
|
|
225
247
|
|
|
@@ -232,10 +254,10 @@ slave.add({
|
|
|
232
254
|
// 可选:在默认分发前拦截任意功能码
|
|
233
255
|
interceptor?: (fc, data) => Buffer | undefined,
|
|
234
256
|
|
|
235
|
-
readCoils?: (address, length) => boolean[],
|
|
236
|
-
readDiscreteInputs?: (address, length) => boolean[],
|
|
237
|
-
readHoldingRegisters?: (address, length) => number[],
|
|
238
|
-
readInputRegisters?: (address, length) => number[],
|
|
257
|
+
readCoils?: (address, length) => boolean[] | Uint8Array,
|
|
258
|
+
readDiscreteInputs?: (address, length) => boolean[] | Uint8Array,
|
|
259
|
+
readHoldingRegisters?: (address, length) => number[] | Uint16Array,
|
|
260
|
+
readInputRegisters?: (address, length) => number[] | Uint16Array,
|
|
239
261
|
|
|
240
262
|
writeSingleCoil?: (address, value) => void,
|
|
241
263
|
writeMultipleCoils?: (address, values) => void,
|
|
@@ -271,18 +293,20 @@ slave.add({
|
|
|
271
293
|
|
|
272
294
|
## 自定义功能码
|
|
273
295
|
|
|
296
|
+
`predictRequestLength` / `predictResponseLength` 接收共享池缓冲区及字节偏移量(`start`、`end`)。边界检查应使用 `end - start` 而非 `buffer.length`。
|
|
297
|
+
|
|
274
298
|
```typescript
|
|
275
299
|
import type { CustomFunctionCode } from 'njs-modbus';
|
|
276
300
|
|
|
277
301
|
const cfc: CustomFunctionCode = {
|
|
278
302
|
fc: 0x50,
|
|
279
|
-
predictRequestLength: (buffer) => {
|
|
280
|
-
if (
|
|
281
|
-
return 4 + buffer[1];
|
|
303
|
+
predictRequestLength: (buffer, start, end) => {
|
|
304
|
+
if (end - start < 2) return null;
|
|
305
|
+
return 4 + buffer[start + 1];
|
|
282
306
|
},
|
|
283
|
-
predictResponseLength: (buffer) => {
|
|
284
|
-
if (
|
|
285
|
-
return 4 + buffer[1];
|
|
307
|
+
predictResponseLength: (buffer, start, end) => {
|
|
308
|
+
if (end - start < 2) return null;
|
|
309
|
+
return 4 + buffer[start + 1];
|
|
286
310
|
},
|
|
287
311
|
handle: async (data, unit) => {
|
|
288
312
|
return Buffer.from([data[1], data[0]]);
|
|
@@ -329,50 +353,75 @@ slave.add({
|
|
|
329
353
|
|
|
330
354
|
| 指标 | njs-modbus | jsmodbus | modbus-serial |
|
|
331
355
|
|------|-----------|----------|---------------|
|
|
332
|
-
| TCP 吞吐量 | **
|
|
333
|
-
| TCP P99 延迟 | **
|
|
334
|
-
|
|
|
335
|
-
|
|
|
336
|
-
|
|
|
337
|
-
| TCP
|
|
338
|
-
|
|
|
356
|
+
| TCP 吞吐量 | **70,544 ops/sec** | 39,827 (0.56x) | 786 (0.01x) |
|
|
357
|
+
| TCP P99 延迟 | **53 µs** | 98 µs (1.83x) | 2,331 µs (43.7x) |
|
|
358
|
+
| 并发 (8 连接) | **75,810 ops/sec** | 41,754 (0.55x) | 5,620 (0.07x) |
|
|
359
|
+
| RTU 串口 (115200 波特率) | **44 ops/sec** | 44 ops/sec | 45 ops/sec |
|
|
360
|
+
| TCP 响应编码 | **6.57M ops/sec** | 1.22M (0.19x) | 1.18M (0.18x) |
|
|
361
|
+
| TCP 响应解码 | **6.78M ops/sec** | 1.94M (0.29x) | 0.67M (0.10x) |
|
|
362
|
+
| FC01 读取线圈 | **85,170 ops/sec** | 617 (0.01x) | 861 (0.01x) |
|
|
339
363
|
|
|
340
364
|
<details>
|
|
341
365
|
<summary>完整基准测试结果</summary>
|
|
342
366
|
|
|
343
|
-
Node.js v24.
|
|
367
|
+
Node.js v24.16.0 · AMD Ryzen 7 9800X3D · linux x64 · 3 次运行 × 120 秒 · [完整报告](./benchmark/RESULTS.md)
|
|
344
368
|
|
|
345
369
|
### TCP 吞吐量(单连接)
|
|
346
370
|
|
|
347
371
|
```
|
|
348
|
-
njs-modbus │
|
|
349
|
-
jsmodbus │
|
|
350
|
-
modbus-serial │
|
|
372
|
+
njs-modbus │ ██████████████████████████████ 70,544 ops/sec 🏆 p99: 53 µs CPU: 91 µs/op
|
|
373
|
+
jsmodbus │ █████████████████░░░░░░░░░░░░░ 39,827 ops/sec (0.56x) p99: 98 µs CPU: 161 µs/op
|
|
374
|
+
modbus-serial │ ░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ 786 ops/sec (0.01x) p99: 2,331 µs CPU: 8,176 µs/op
|
|
351
375
|
```
|
|
352
376
|
|
|
353
377
|
### 并发(8 连接)
|
|
354
378
|
|
|
355
379
|
```
|
|
356
|
-
njs-modbus │
|
|
357
|
-
jsmodbus │
|
|
358
|
-
modbus-serial │
|
|
380
|
+
njs-modbus │ ██████████████████████████████ 75,810 ops/sec 🏆 p99: 258 µs CPU: 91 µs/op
|
|
381
|
+
jsmodbus │ █████████████████░░░░░░░░░░░░░ 41,754 ops/sec (0.55x) p99: 543 µs CPU: 167 µs/op
|
|
382
|
+
modbus-serial │ ██░░░░░░░░░░░░░░░░░░░░░░░░░░░░ 5,620 ops/sec (0.07x) p99: 3,180 µs CPU: 1,237 µs/op
|
|
359
383
|
```
|
|
360
384
|
|
|
361
|
-
### RTU
|
|
385
|
+
### RTU 串口(socat PTY 模拟 115200 波特率)
|
|
362
386
|
|
|
363
387
|
```
|
|
364
|
-
njs-modbus │ 44 ops/sec CPU:
|
|
365
|
-
jsmodbus │ 44 ops/sec
|
|
366
|
-
modbus-serial │
|
|
388
|
+
njs-modbus │ 44 ops/sec p99: 993 µs CPU: 729 µs/op GC: 2,613 ns/op
|
|
389
|
+
jsmodbus │ 44 ops/sec p99: 1,019 µs CPU: 694 µs/op GC: 2,609 ns/op
|
|
390
|
+
modbus-serial │ 45 ops/sec p99: 3,228 µs CPU: 765 µs/op GC: 3,864 ns/op
|
|
367
391
|
```
|
|
368
392
|
|
|
369
|
-
###
|
|
393
|
+
### 帧编码 / 解码(CPU 微基准测试)
|
|
370
394
|
|
|
371
395
|
```
|
|
372
|
-
|
|
373
|
-
|
|
396
|
+
tcpReqEncode: njs-modbus 6.90M ops/sec 🏆 jsmodbus 0.78x modbus-serial 0.19x
|
|
397
|
+
tcpResEncode: njs-modbus 6.57M ops/sec 🏆 jsmodbus 0.19x modbus-serial 0.18x
|
|
398
|
+
tcpReqDecode: njs-modbus 6.80M ops/sec 🏆 jsmodbus 0.81x modbus-serial 0.17x
|
|
399
|
+
tcpResDecode: njs-modbus 6.78M ops/sec 🏆 jsmodbus 0.29x modbus-serial 0.10x
|
|
400
|
+
rtuReqEncode: njs-modbus 7.26M ops/sec 🏆 jsmodbus 0.35x modbus-serial 0.76x
|
|
401
|
+
rtuReqDecode: njs-modbus 3.97M ops/sec jsmodbus 1.21x modbus-serial 1.78x 🏆
|
|
402
|
+
asciiReqEncode: njs-modbus 5.64M ops/sec 🏆 modbus-serial 0.57x
|
|
403
|
+
asciiReqDecode: njs-modbus 3.37M ops/sec 🏆 modbus-serial 0.34x
|
|
374
404
|
```
|
|
375
405
|
|
|
406
|
+
### 全功能码 TCP 吞吐量(常规负载)
|
|
407
|
+
|
|
408
|
+
| 功能码 | njs-modbus | jsmodbus | modbus-serial |
|
|
409
|
+
|--------|-----------|----------|---------------|
|
|
410
|
+
| FC01 读取线圈 | **85,170** 🏆 | 617 (0.01x) | 861 (0.01x) |
|
|
411
|
+
| FC02 读取离散输入 | **85,570** 🏆 | 525 (0.01x) | 861 (0.01x) |
|
|
412
|
+
| FC03 读取保持寄存器 | **75,344** 🏆 | 40,081 (0.53x) | 852 (0.01x) |
|
|
413
|
+
| FC04 读取输入寄存器 | **75,757** 🏆 | 49,524 (0.65x) | 866 (0.01x) |
|
|
414
|
+
| FC05 写单个线圈 | **92,852** 🏆 | 53,521 (0.58x) | 871 (0.01x) |
|
|
415
|
+
| FC06 写单个寄存器 | **92,864** 🏆 | 53,684 (0.58x) | 872 (0.01x) |
|
|
416
|
+
| FC15 写多个线圈 | **86,434** 🏆 | 319 (0.00x) | 869 (0.01x) |
|
|
417
|
+
| FC16 写多个寄存器 | **76,053** 🏆 | 39,356 (0.52x) | 852 (0.01x) |
|
|
418
|
+
| FC17 报告服务器 ID | **74,697** 🏆 | — | — |
|
|
419
|
+
| FC22 掩码写寄存器 | **90,717** 🏆 | — | — |
|
|
420
|
+
| FC23 读/写多个寄存器 | **85,652** 🏆 | — | — |
|
|
421
|
+
| FC43 读取设备标识 | **78,030** 🏆 | — | 865 (0.01x) |
|
|
422
|
+
|
|
423
|
+
> FC17 / FC22 / FC23 不受 jsmodbus 和 modbus-serial 支持。
|
|
424
|
+
|
|
376
425
|
</details>
|
|
377
426
|
|
|
378
427
|
## 许可证
|