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.
Files changed (44) hide show
  1. package/README.md +84 -35
  2. package/README.zh-CN.md +84 -35
  3. package/dist/index.cjs +1291 -497
  4. package/dist/index.d.ts +99 -31
  5. package/dist/index.mjs +1291 -498
  6. package/dist/utils.cjs +536 -0
  7. package/dist/utils.d.ts +163 -0
  8. package/dist/utils.mjs +522 -0
  9. package/package.json +22 -2
  10. package/dist/src/error-code.d.ts +0 -17
  11. package/dist/src/index.d.ts +0 -7
  12. package/dist/src/layers/application/abstract-application-layer.d.ts +0 -26
  13. package/dist/src/layers/application/ascii-application-layer.d.ts +0 -23
  14. package/dist/src/layers/application/index.d.ts +0 -6
  15. package/dist/src/layers/application/rtu-application-layer.d.ts +0 -34
  16. package/dist/src/layers/application/tcp-application-layer.d.ts +0 -16
  17. package/dist/src/layers/physical/abstract-physical-layer.d.ts +0 -50
  18. package/dist/src/layers/physical/index.d.ts +0 -12
  19. package/dist/src/layers/physical/serial-physical-layer.d.ts +0 -70
  20. package/dist/src/layers/physical/tcp-client-physical-layer.d.ts +0 -20
  21. package/dist/src/layers/physical/tcp-physical-connection.d.ts +0 -16
  22. package/dist/src/layers/physical/tcp-server-physical-layer.d.ts +0 -29
  23. package/dist/src/layers/physical/udp-client-physical-layer.d.ts +0 -34
  24. package/dist/src/layers/physical/udp-server-physical-layer.d.ts +0 -51
  25. package/dist/src/layers/physical/utils.d.ts +0 -39
  26. package/dist/src/layers/physical/vars.d.ts +0 -11
  27. package/dist/src/master/index.d.ts +0 -3
  28. package/dist/src/master/master-session.d.ts +0 -18
  29. package/dist/src/master/master.d.ts +0 -140
  30. package/dist/src/slave/index.d.ts +0 -2
  31. package/dist/src/slave/slave.d.ts +0 -119
  32. package/dist/src/types.d.ts +0 -54
  33. package/dist/src/utils/bitsToMs.d.ts +0 -13
  34. package/dist/src/utils/callback.d.ts +0 -8
  35. package/dist/src/utils/checkRange.d.ts +0 -1
  36. package/dist/src/utils/crc.d.ts +0 -1
  37. package/dist/src/utils/index.d.ts +0 -11
  38. package/dist/src/utils/isUint8.d.ts +0 -8
  39. package/dist/src/utils/lrc.d.ts +0 -1
  40. package/dist/src/utils/predictRtuFrameLength.d.ts +0 -17
  41. package/dist/src/utils/promisify-cb.d.ts +0 -4
  42. package/dist/src/utils/rtu-timing.d.ts +0 -63
  43. package/dist/src/utils/whitelist.d.ts +0 -11
  44. 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 only)
19
- - **Slave:** Per-connection FIFO or concurrent processing (TCP only)
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 (buffer.length < 2) return null;
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 (buffer.length < 2) return null;
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 | **5,527 ops/sec** | 3,239 (0.59x) | 371 (0.07x) |
334
- | TCP P99 Latency | **2.71 ms** | 4.73 ms (1.75x) | 12.48 ms (4.61x) |
335
- | TCP CPU Efficiency | **1,116 µs/op** | 1,950 (1.75x) | 16,715 (14.98x) |
336
- | Concurrent (8 conn) | **5,274 ops/sec** | 3,416 (0.65x) | 1,815 (0.34x) |
337
- | RTU CPU Efficiency | **1,762 µs/op** | 1,760 (1.00x) | 2,144 (1.22x) |
338
- | TCP Res Encode | **2.04M ops/sec** | 373K (0.18x) | 411K (0.20x) |
339
- | TCP Res Decode | **1.91M ops/sec** | 538K (0.28x) | 231K (0.12x) |
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.15.0 · linux x64 · 3 runs × 300 s · [full report](./benchmark/RESULTS.md)
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 │ 5,527 ops/sec 🏆 CPU: 1,116 µs/op
350
- jsmodbus │ 3,239 ops/sec (0.59x) CPU: 1,950 µs/op
351
- modbus-serial │ 371 ops/sec (0.07x) CPU: 16,715 µs/op
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 │ 5,274 ops/sec 🏆 CPU: 1,392 µs/op
358
- jsmodbus │ 3,416 ops/sec (0.65x) CPU: 2,131 µs/op
359
- modbus-serial │ 1,815 ops/sec (0.34x) CPU: 3,962 µs/op
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 simulated)
386
+ ### RTU Serial (115200 baud via socat PTY)
363
387
 
364
388
  ```
365
- njs-modbus │ 44 ops/sec CPU: 1,762 µs/op
366
- jsmodbus │ 44 ops/sec CPU: 1,760 µs/op
367
- modbus-serial │ 44 ops/sec CPU: 2,144 µs/op
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
- tcpResEncode: njs-modbus 2.04M ops/sec (jsmodbus 0.18x, modbus-serial 0.20x)
374
- tcpResDecode: njs-modbus 1.91M ops/sec (jsmodbus 0.28x, modbus-serial 0.12x)
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 或流水线并发请求(仅 TCP)
19
- - **从站:** 每连接 FIFO 或并发处理(仅 TCP)
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, // 流水线模式(仅 TCP)
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, // 每连接并发(仅 TCP)
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 (buffer.length < 2) return null;
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 (buffer.length < 2) return null;
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 吞吐量 | **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) |
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.15.0 · linux x64 · 3 次运行 × 300 秒 · [完整报告](./benchmark/RESULTS.md)
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 │ 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
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 │ 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
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 串口(模拟 115200 波特率)
385
+ ### RTU 串口(socat PTY 模拟 115200 波特率)
362
386
 
363
387
  ```
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
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
- ### 编码 / 解码(CPU 微基准测试)
393
+ ### 帧编码 / 解码(CPU 微基准测试)
370
394
 
371
395
  ```
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)
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
  ## 许可证