njs-modbus 3.2.0 → 3.4.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 +185 -110
- package/README.zh-CN.md +185 -109
- package/dist/index.cjs +1084 -588
- package/dist/index.d.ts +92 -61
- package/dist/index.mjs +1084 -588
- package/dist/utils.cjs +293 -168
- package/dist/utils.d.ts +43 -40
- package/dist/utils.mjs +289 -167
- package/package.json +4 -3
package/README.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
English | [简体中文](./README.zh-CN.md)
|
|
4
4
|
|
|
5
|
-
A pure JavaScript implementation of Modbus for Node.js.
|
|
5
|
+
A pure JavaScript implementation of Modbus for Node.js, optimized for throughput and low GC pressure.
|
|
6
6
|
|
|
7
7
|
<!-- prettier-ignore-start -->
|
|
8
8
|
[](http://www.npm-stats.com/~packages/njs-modbus)
|
|
@@ -11,17 +11,20 @@ A pure JavaScript implementation of Modbus for Node.js.
|
|
|
11
11
|
[](https://github.com/xiejay97/njs-modbus/actions)
|
|
12
12
|
<!-- prettier-ignore-end -->
|
|
13
13
|
|
|
14
|
-
##
|
|
14
|
+
## Highlights
|
|
15
15
|
|
|
16
|
-
- **Protocols
|
|
17
|
-
- **Transports
|
|
18
|
-
- **
|
|
19
|
-
- **
|
|
16
|
+
- **Protocols** — Modbus TCP, RTU, ASCII
|
|
17
|
+
- **Transports** — Serial, TCP client/server, UDP client/server, plus pluggable custom layers
|
|
18
|
+
- **Performance-first** — Parallel FIFO queues, lazy-deletion timer heap, inline BE reads/writes, zero-allocation hot paths
|
|
19
|
+
- **Master** — FIFO or pipelined concurrent requests (TCP only)
|
|
20
|
+
- **Slave** — Per-connection FIFO or concurrent processing (TCP only)
|
|
20
21
|
- **Custom function codes** with pluggable framing
|
|
21
|
-
- **Broadcasting** (unit = 0)
|
|
22
|
+
- **Broadcasting** (`unit = 0`)
|
|
22
23
|
- **Full TypeScript**
|
|
23
24
|
- **Zero runtime dependencies** (peer-depends on `serialport` for serial)
|
|
24
25
|
|
|
26
|
+
## Supported Function Codes
|
|
27
|
+
|
|
25
28
|
| Code | Name |
|
|
26
29
|
|------|------|
|
|
27
30
|
| 01 | Read Coils |
|
|
@@ -39,6 +42,8 @@ A pure JavaScript implementation of Modbus for Node.js.
|
|
|
39
42
|
|
|
40
43
|
## Installation
|
|
41
44
|
|
|
45
|
+
Requires **Node.js ≥ 18.19**.
|
|
46
|
+
|
|
42
47
|
```bash
|
|
43
48
|
npm install njs-modbus
|
|
44
49
|
```
|
|
@@ -114,20 +119,45 @@ const master = new ModbusMaster({
|
|
|
114
119
|
});
|
|
115
120
|
```
|
|
116
121
|
|
|
117
|
-
##
|
|
122
|
+
## Architecture
|
|
123
|
+
|
|
124
|
+
The library follows a strict two-layer design:
|
|
125
|
+
|
|
126
|
+
```
|
|
127
|
+
┌─────────────────────────────────────────┐
|
|
128
|
+
│ ModbusMaster / ModbusSlave │ ← User API, session/queue management
|
|
129
|
+
├─────────────────────────────────────────┤
|
|
130
|
+
│ TcpApplicationLayer │ ← Protocol framing (MBAP / CRC16 / LRC)
|
|
131
|
+
│ RtuApplicationLayer │
|
|
132
|
+
│ AsciiApplicationLayer │
|
|
133
|
+
├─────────────────────────────────────────┤
|
|
134
|
+
│ AbstractPhysicalConnection │ ← Per-connection I/O
|
|
135
|
+
├─────────────────────────────────────────┤
|
|
136
|
+
│ AbstractPhysicalLayer │ ← Resource ownership (port/socket/server)
|
|
137
|
+
│ ├── TcpClientPhysicalLayer │
|
|
138
|
+
│ ├── TcpServerPhysicalLayer │
|
|
139
|
+
│ ├── UdpClientPhysicalLayer │
|
|
140
|
+
│ ├── UdpServerPhysicalLayer │
|
|
141
|
+
│ └── SerialPhysicalLayer │
|
|
142
|
+
└─────────────────────────────────────────┘
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
Every transport is modeled as connection-oriented: physical layers emit `'connect'` with an `AbstractPhysicalConnection`, and a dedicated application layer instance is created per connection.
|
|
146
|
+
|
|
147
|
+
## Physical Layer
|
|
118
148
|
|
|
119
|
-
All physical layers expose `open()` / `close()`, a `state` property, and events
|
|
149
|
+
All physical layers expose `open()` / `close()`, a `state` property, and the events documented in [Events](#events).
|
|
120
150
|
|
|
121
|
-
| Type |
|
|
122
|
-
|
|
123
|
-
| `SERIAL` | `
|
|
124
|
-
| `TCP_CLIENT` | `
|
|
125
|
-
| `TCP_SERVER` | `
|
|
126
|
-
| `UDP_CLIENT` | `
|
|
127
|
-
| `UDP_SERVER` | `
|
|
128
|
-
| `CUSTOM` | *(user-
|
|
151
|
+
| Type | `open(...)` Args | Description |
|
|
152
|
+
|------|-----------------|-------------|
|
|
153
|
+
| `SERIAL` | `()` | Serial port via `serialport` package |
|
|
154
|
+
| `TCP_CLIENT` | `SocketConnectOpts` | TCP client socket |
|
|
155
|
+
| `TCP_SERVER` | `ListenOptions` | TCP server |
|
|
156
|
+
| `UDP_CLIENT` | `{ port?, address? }` | UDP client |
|
|
157
|
+
| `UDP_SERVER` | `BindOptions` | UDP server |
|
|
158
|
+
| `CUSTOM` | *(user-defined)* | User-provided `AbstractPhysicalLayer` instance |
|
|
129
159
|
|
|
130
|
-
### Server options
|
|
160
|
+
### TCP Server options
|
|
131
161
|
|
|
132
162
|
```typescript
|
|
133
163
|
const slave = new ModbusSlave({
|
|
@@ -143,7 +173,7 @@ const slave = new ModbusSlave({
|
|
|
143
173
|
});
|
|
144
174
|
```
|
|
145
175
|
|
|
146
|
-
### Physical layer
|
|
176
|
+
### Physical layer option reference
|
|
147
177
|
|
|
148
178
|
| Option | Type | Description |
|
|
149
179
|
|--------|------|-------------|
|
|
@@ -152,54 +182,35 @@ const slave = new ModbusSlave({
|
|
|
152
182
|
| `idleTimeout` | `number` | Idle timeout in ms before evicting a connection. Default `30000`. Pass `0` to disable. |
|
|
153
183
|
| `socketOpts` / `serverOpts` | `object` | Forwarded to Node.js `createSocket()` / `createServer()`. |
|
|
154
184
|
|
|
155
|
-
##
|
|
156
|
-
|
|
157
|
-
Any byte-oriented transport can be used by implementing `AbstractPhysicalLayer` and `AbstractPhysicalConnection`, then passing `{ type: 'CUSTOM', layer: yourLayer }`.
|
|
158
|
-
|
|
159
|
-
```typescript
|
|
160
|
-
import { AbstractPhysicalLayer, AbstractPhysicalConnection } from 'njs-modbus';
|
|
161
|
-
|
|
162
|
-
class MyPhysicalLayer extends AbstractPhysicalLayer { /* open / close */ }
|
|
163
|
-
class MyConnection extends AbstractPhysicalConnection { /* write / destroy, emit 'data' */ }
|
|
164
|
-
|
|
165
|
-
const master = new ModbusMaster({
|
|
166
|
-
physical: { type: 'CUSTOM', layer: new MyPhysicalLayer() },
|
|
167
|
-
protocol: { type: 'TCP' },
|
|
168
|
-
});
|
|
169
|
-
```
|
|
170
|
-
|
|
171
|
-
See [`examples/websocket`](./examples/websocket) for a complete **Modbus TCP over WebSocket** implementation — client/server physical layers with a working demo.
|
|
185
|
+
## Protocol Layer
|
|
172
186
|
|
|
173
|
-
|
|
187
|
+
| Protocol | Options | Description |
|
|
188
|
+
|----------|---------|-------------|
|
|
189
|
+
| `TCP` | none | Modbus TCP with MBAP header |
|
|
190
|
+
| `RTU` | `RtuProtocolOptions` | Modbus RTU with CRC16 |
|
|
191
|
+
| `ASCII` | `AsciiApplicationLayerOptions` | Modbus ASCII with LRC |
|
|
174
192
|
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
```typescript
|
|
178
|
-
new ModbusMaster({
|
|
179
|
-
physical: { type: 'TCP_CLIENT' },
|
|
180
|
-
protocol: { type: 'TCP' }, // 'TCP' | 'RTU' | 'ASCII'
|
|
181
|
-
timeout?: 1000, // per-request timeout (ms)
|
|
182
|
-
concurrent?: false, // pipelined mode (TCP only)
|
|
183
|
-
})
|
|
184
|
-
```
|
|
185
|
-
|
|
186
|
-
RTU framing options:
|
|
193
|
+
### RTU options
|
|
187
194
|
|
|
188
195
|
```typescript
|
|
189
196
|
protocol: {
|
|
190
197
|
type: 'RTU',
|
|
191
198
|
opts: {
|
|
192
|
-
//
|
|
193
|
-
//
|
|
194
|
-
//
|
|
195
|
-
intervalBetweenFrames: 20,
|
|
196
|
-
|
|
197
|
-
|
|
199
|
+
// t3.5 inter-frame silence.
|
|
200
|
+
// Either a bare number (ms) or { unit: 'bit' | 'ms', value: N }.
|
|
201
|
+
// Use 0 to disable (useful for lossless transports like RTU-over-TCP).
|
|
202
|
+
intervalBetweenFrames: 20,
|
|
203
|
+
|
|
204
|
+
// t1.5 inter-character timeout (opt-in).
|
|
205
|
+
interCharTimeout: { unit: 'bit', value: 10 },
|
|
206
|
+
|
|
207
|
+
// Discard frames with t1.5 timing violations.
|
|
208
|
+
strictTiming: true,
|
|
198
209
|
},
|
|
199
210
|
}
|
|
200
211
|
```
|
|
201
212
|
|
|
202
|
-
ASCII options
|
|
213
|
+
### ASCII options
|
|
203
214
|
|
|
204
215
|
```typescript
|
|
205
216
|
protocol: {
|
|
@@ -210,11 +221,24 @@ protocol: {
|
|
|
210
221
|
}
|
|
211
222
|
```
|
|
212
223
|
|
|
224
|
+
## Master API
|
|
225
|
+
|
|
226
|
+
### Constructor
|
|
227
|
+
|
|
228
|
+
```typescript
|
|
229
|
+
new ModbusMaster({
|
|
230
|
+
physical: { type: 'TCP_CLIENT' },
|
|
231
|
+
protocol: { type: 'TCP' }, // 'TCP' | 'RTU' | 'ASCII'
|
|
232
|
+
timeout?: 1000, // per-request timeout (ms)
|
|
233
|
+
concurrent?: false, // pipelined mode (TCP only)
|
|
234
|
+
})
|
|
235
|
+
```
|
|
236
|
+
|
|
213
237
|
### Methods
|
|
214
238
|
|
|
215
239
|
| Method | Description |
|
|
216
240
|
|--------|-------------|
|
|
217
|
-
| `open(...args)` | Open the physical layer.
|
|
241
|
+
| `open(...args)` | Open the physical layer. One-shot: cannot reopen after `close()`. |
|
|
218
242
|
| `close()` | Close immediately. In-flight and queued requests are rejected. |
|
|
219
243
|
| `readCoils(unit, address, length, timeout?)` | FC 01 |
|
|
220
244
|
| `readDiscreteInputs(unit, address, length, timeout?)` | FC 02 |
|
|
@@ -236,6 +260,8 @@ Broadcast (`unit = 0`) resolves `void` — slaves never respond.
|
|
|
236
260
|
|
|
237
261
|
## Slave API
|
|
238
262
|
|
|
263
|
+
### Constructor
|
|
264
|
+
|
|
239
265
|
```typescript
|
|
240
266
|
new ModbusSlave({
|
|
241
267
|
physical: { type: 'TCP_SERVER' },
|
|
@@ -253,10 +279,10 @@ slave.add({
|
|
|
253
279
|
// Optional: intercept any FC before default dispatch
|
|
254
280
|
interceptor?: (fc, data) => Buffer | undefined,
|
|
255
281
|
|
|
256
|
-
readCoils?: (address, length) =>
|
|
257
|
-
readDiscreteInputs?: (address, length) =>
|
|
258
|
-
readHoldingRegisters?: (address, length) => number[],
|
|
259
|
-
readInputRegisters?: (address, length) => number[],
|
|
282
|
+
readCoils?: (address, length) => Uint8Array,
|
|
283
|
+
readDiscreteInputs?: (address, length) => Uint8Array,
|
|
284
|
+
readHoldingRegisters?: (address, length) => number[] | Uint16Array,
|
|
285
|
+
readInputRegisters?: (address, length) => number[] | Uint16Array,
|
|
260
286
|
|
|
261
287
|
writeSingleCoil?: (address, value) => void,
|
|
262
288
|
writeMultipleCoils?: (address, values) => void,
|
|
@@ -281,29 +307,70 @@ slave.add({
|
|
|
281
307
|
|
|
282
308
|
Missing handlers return `ILLEGAL_FUNCTION`. Out-of-range addresses return `ILLEGAL_DATA_ADDRESS`. Handler throws become `SERVER_DEVICE_FAILURE` unless the error is a `ModbusError`.
|
|
283
309
|
|
|
310
|
+
### Methods
|
|
311
|
+
|
|
284
312
|
| Method | Description |
|
|
285
313
|
|--------|-------------|
|
|
286
314
|
| `add(model)` | Register a slave model |
|
|
287
|
-
| `remove(unit)` | Remove a
|
|
288
|
-
| `open(...args)` | Open
|
|
315
|
+
| `remove(unit)` | Remove a model |
|
|
316
|
+
| `open(...args)` | Open and begin accepting connections. One-shot. |
|
|
289
317
|
| `close()` | Close immediately |
|
|
290
|
-
| `addCustomFunctionCode(cfc)` | Register
|
|
291
|
-
| `removeCustomFunctionCode(fc)` | Unregister
|
|
318
|
+
| `addCustomFunctionCode(cfc)` | Register custom FC |
|
|
319
|
+
| `removeCustomFunctionCode(fc)` | Unregister custom FC |
|
|
320
|
+
|
|
321
|
+
## Events
|
|
322
|
+
|
|
323
|
+
Both `ModbusMaster` and `ModbusSlave` are `EventEmitter`s. All events are typed.
|
|
324
|
+
|
|
325
|
+
### Physical layer events
|
|
326
|
+
|
|
327
|
+
| Event | Arguments | Description |
|
|
328
|
+
|-------|-----------|-------------|
|
|
329
|
+
| `open` | `()` | Physical layer is ready to accept connections |
|
|
330
|
+
| `connect` | `(connection)` | A new connection has been established |
|
|
331
|
+
| `close` | `()` | Physical layer has closed |
|
|
332
|
+
| `error` | `(error)` | Physical layer error |
|
|
333
|
+
|
|
334
|
+
### Connection debug events
|
|
335
|
+
|
|
336
|
+
| Event | Arguments | Description |
|
|
337
|
+
|-------|-----------|-------------|
|
|
338
|
+
| `tx` | `(buffer, connection)` | Raw data written to the wire |
|
|
339
|
+
| `rx` | `(buffer, connection)` | Raw data received from the wire |
|
|
340
|
+
|
|
341
|
+
### Application layer events
|
|
342
|
+
|
|
343
|
+
| Event | Arguments | Description |
|
|
344
|
+
|-------|-----------|-------------|
|
|
345
|
+
| `framing` | `(frame, connection)` | A complete frame has been decoded |
|
|
346
|
+
| `framingError` | `(error, connection)` | Framing error (CRC/LRC failure, malformed MBAP, timing violation, etc.) |
|
|
347
|
+
|
|
348
|
+
```typescript
|
|
349
|
+
master.on('framing', (frame, connection) => {
|
|
350
|
+
console.log('frame:', frame.unit, frame.fc, frame.transaction);
|
|
351
|
+
});
|
|
352
|
+
|
|
353
|
+
master.on('framingError', (error, connection) => {
|
|
354
|
+
console.log('framing error:', error.message);
|
|
355
|
+
});
|
|
356
|
+
```
|
|
292
357
|
|
|
293
358
|
## Custom Function Codes
|
|
294
359
|
|
|
360
|
+
`predictRequestLength` / `predictResponseLength` receive a `getByte` accessor and the number of available `length` bytes. Use `length` for bounds checks — do not access the buffer directly.
|
|
361
|
+
|
|
295
362
|
```typescript
|
|
296
363
|
import type { CustomFunctionCode } from 'njs-modbus';
|
|
297
364
|
|
|
298
365
|
const cfc: CustomFunctionCode = {
|
|
299
366
|
fc: 0x50,
|
|
300
|
-
predictRequestLength: (
|
|
301
|
-
if (
|
|
302
|
-
return 4 +
|
|
367
|
+
predictRequestLength: (getByte, length) => {
|
|
368
|
+
if (length < 2) return 0; // need more bytes
|
|
369
|
+
return 4 + getByte(1);
|
|
303
370
|
},
|
|
304
|
-
predictResponseLength: (
|
|
305
|
-
if (
|
|
306
|
-
return 4 +
|
|
371
|
+
predictResponseLength: (getByte, length) => {
|
|
372
|
+
if (length < 2) return 0;
|
|
373
|
+
return 4 + getByte(1);
|
|
307
374
|
},
|
|
308
375
|
handle: async (data, unit) => {
|
|
309
376
|
return Buffer.from([data[1], data[0]]);
|
|
@@ -344,57 +411,65 @@ slave.add({
|
|
|
344
411
|
});
|
|
345
412
|
```
|
|
346
413
|
|
|
347
|
-
##
|
|
348
|
-
|
|
349
|
-
Benchmarked against [jsmodbus](https://github.com/Cloud-Automation/node-modbus) and [modbus-serial](https://github.com/yaacov/node-modbus-serial).
|
|
350
|
-
|
|
351
|
-
| Metric | njs-modbus | jsmodbus | modbus-serial |
|
|
352
|
-
|--------|-----------|----------|---------------|
|
|
353
|
-
| TCP Throughput | **5,527 ops/sec** | 3,239 (0.59x) | 371 (0.07x) |
|
|
354
|
-
| TCP P99 Latency | **2.71 ms** | 4.73 ms (1.75x) | 12.48 ms (4.61x) |
|
|
355
|
-
| TCP CPU Efficiency | **1,116 µs/op** | 1,950 (1.75x) | 16,715 (14.98x) |
|
|
356
|
-
| Concurrent (8 conn) | **5,274 ops/sec** | 3,416 (0.65x) | 1,815 (0.34x) |
|
|
357
|
-
| RTU CPU Efficiency | **1,762 µs/op** | 1,760 (1.00x) | 2,144 (1.22x) |
|
|
358
|
-
| TCP Res Encode | **2.04M ops/sec** | 373K (0.18x) | 411K (0.20x) |
|
|
359
|
-
| TCP Res Decode | **1.91M ops/sec** | 538K (0.28x) | 231K (0.12x) |
|
|
414
|
+
## Custom Physical Layer
|
|
360
415
|
|
|
361
|
-
|
|
362
|
-
<summary>Full benchmark results</summary>
|
|
416
|
+
Any byte-oriented transport can be used by implementing `AbstractPhysicalLayer` and `AbstractPhysicalConnection`, then passing `{ type: 'CUSTOM', layer: yourLayer }`.
|
|
363
417
|
|
|
364
|
-
|
|
418
|
+
```typescript
|
|
419
|
+
import { AbstractPhysicalLayer, AbstractPhysicalConnection } from 'njs-modbus';
|
|
365
420
|
|
|
366
|
-
|
|
421
|
+
class MyPhysicalLayer extends AbstractPhysicalLayer { /* open / close */ }
|
|
422
|
+
class MyConnection extends AbstractPhysicalConnection { /* write / destroy, emit 'data' */ }
|
|
367
423
|
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
424
|
+
const master = new ModbusMaster({
|
|
425
|
+
physical: { type: 'CUSTOM', layer: new MyPhysicalLayer() },
|
|
426
|
+
protocol: { type: 'TCP' },
|
|
427
|
+
});
|
|
372
428
|
```
|
|
373
429
|
|
|
374
|
-
|
|
430
|
+
See [`examples/websocket`](./examples/websocket) for a complete **Modbus TCP over WebSocket** implementation.
|
|
375
431
|
|
|
376
|
-
|
|
377
|
-
njs-modbus │ 5,274 ops/sec 🏆 CPU: 1,392 µs/op
|
|
378
|
-
jsmodbus │ 3,416 ops/sec (0.65x) CPU: 2,131 µs/op
|
|
379
|
-
modbus-serial │ 1,815 ops/sec (0.34x) CPU: 3,962 µs/op
|
|
380
|
-
```
|
|
432
|
+
See [`examples/bluetooth`](./examples/bluetooth) for a **Modbus TCP over BLE** implementation using the Nordic UART Service (NUS).
|
|
381
433
|
|
|
382
|
-
|
|
434
|
+
## Performance
|
|
383
435
|
|
|
384
|
-
|
|
385
|
-
njs-modbus │ 44 ops/sec CPU: 1,762 µs/op
|
|
386
|
-
jsmodbus │ 44 ops/sec CPU: 1,760 µs/op
|
|
387
|
-
modbus-serial │ 44 ops/sec CPU: 2,144 µs/op
|
|
388
|
-
```
|
|
436
|
+
Benchmarked against [jsmodbus](https://github.com/Cloud-Automation/node-modbus) v4.0.10 and [modbus-serial](https://github.com/yaacov/node-modbus-serial) v8.0.25.
|
|
389
437
|
|
|
390
|
-
|
|
438
|
+
Environment: AMD Ryzen 7 9800X3D 8-Core Processor · Node.js v24.16.0 · [full report](./benchmark/report_presentation.md)
|
|
391
439
|
|
|
392
|
-
|
|
393
|
-
tcpResEncode: njs-modbus 2.04M ops/sec (jsmodbus 0.18x, modbus-serial 0.20x)
|
|
394
|
-
tcpResDecode: njs-modbus 1.91M ops/sec (jsmodbus 0.28x, modbus-serial 0.12x)
|
|
395
|
-
```
|
|
440
|
+
### End-to-end TCP
|
|
396
441
|
|
|
397
|
-
|
|
442
|
+
| Metric | njs-modbus | jsmodbus | modbus-serial |
|
|
443
|
+
|--------|-----------|----------|---------------|
|
|
444
|
+
| Single connection | **74,964 ops/sec** 🏆 | 55,996 (0.75x) | 853 (0.01x) |
|
|
445
|
+
| 8 concurrent clients | **10,188 ops/sec** 🏆 | 5,918 (0.58x) | 728 (0.07x) |
|
|
446
|
+
| P99 latency (single) | **17 µs** 🏆 | 15 µs (0.88x) | 1,206 µs (70.9x) |
|
|
447
|
+
|
|
448
|
+
### Frame encode / decode (CPU micro-benchmark)
|
|
449
|
+
|
|
450
|
+
| Benchmark | njs-modbus | jsmodbus | modbus-serial |
|
|
451
|
+
|-----------|-----------|----------|---------------|
|
|
452
|
+
| TCP request encode | **9.61M ops/sec** 🏆 | 6.32M (0.66x) | 0.68M (0.07x) |
|
|
453
|
+
| TCP response encode | **7.51M ops/sec** 🏆 | 1.18M (0.16x) | 0.44M (0.06x) |
|
|
454
|
+
| TCP response decode | **8.55M ops/sec** 🏆 | 2.04M (0.24x) | 0.70M (0.08x) |
|
|
455
|
+
| RTU request encode | **9.42M ops/sec** 🏆 | 3.03M (0.32x) | 6.57M (0.70x) |
|
|
456
|
+
| ASCII request encode | **7.09M ops/sec** 🏆 | — | 4.11M (0.58x) |
|
|
457
|
+
| ASCII request decode | **4.91M ops/sec** 🏆 | — | 1.19M (0.24x) |
|
|
458
|
+
|
|
459
|
+
### Per-function-code TCP throughput (100 coils / 50 registers)
|
|
460
|
+
|
|
461
|
+
| Function Code | njs-modbus | jsmodbus | modbus-serial |
|
|
462
|
+
|---------------|-----------|----------|---------------|
|
|
463
|
+
| FC01 Read Coils | **75,430** 🏆 | 488 (0.01x) | 859 (0.01x) |
|
|
464
|
+
| FC03 Read Holding Registers | **82,028** 🏆 | 46,596 (0.57x) | 864 (0.01x) |
|
|
465
|
+
| FC06 Write Single Register | **86,110** 🏆 | 51,084 (0.59x) | 871 (0.01x) |
|
|
466
|
+
| FC15 Write Multiple Coils | **78,766** 🏆 | — | 867 (0.01x) |
|
|
467
|
+
| FC17 Report Server ID | **79,185** 🏆 | — | — |
|
|
468
|
+
| FC22 Mask Write Register | **73,060** 🏆 | — | — |
|
|
469
|
+
| FC23 Read/Write Multiple Registers | **68,429** 🏆 | — | — |
|
|
470
|
+
| FC43 Read Device Identification | **62,176** 🏆 | — | 868 (0.01x) |
|
|
471
|
+
|
|
472
|
+
> FC17 / FC22 / FC23 / FC43 are not supported by jsmodbus.
|
|
398
473
|
|
|
399
474
|
## License
|
|
400
475
|
|