njs-modbus 3.3.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 +177 -131
- package/README.zh-CN.md +177 -130
- package/dist/index.cjs +715 -519
- package/dist/index.d.ts +86 -61
- package/dist/index.mjs +715 -519
- package/dist/utils.cjs +53 -25
- package/dist/utils.d.ts +15 -14
- package/dist/utils.mjs +49 -24
- package/package.json +3 -9
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 |
|
|
@@ -116,20 +119,45 @@ const master = new ModbusMaster({
|
|
|
116
119
|
});
|
|
117
120
|
```
|
|
118
121
|
|
|
119
|
-
##
|
|
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.
|
|
120
146
|
|
|
121
|
-
|
|
147
|
+
## Physical Layer
|
|
122
148
|
|
|
123
|
-
|
|
124
|
-
|------|-------|-----------------|
|
|
125
|
-
| `SERIAL` | `SerialPhysicalLayer` | none |
|
|
126
|
-
| `TCP_CLIENT` | `TcpClientPhysicalLayer` | `SocketConnectOpts` |
|
|
127
|
-
| `TCP_SERVER` | `TcpServerPhysicalLayer` | `ListenOptions` |
|
|
128
|
-
| `UDP_CLIENT` | `UdpClientPhysicalLayer` | `{ port, address }` |
|
|
129
|
-
| `UDP_SERVER` | `UdpServerPhysicalLayer` | `BindOptions` |
|
|
130
|
-
| `CUSTOM` | *(user-provided)* | *(user-defined)* |
|
|
149
|
+
All physical layers expose `open()` / `close()`, a `state` property, and the events documented in [Events](#events).
|
|
131
150
|
|
|
132
|
-
|
|
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 |
|
|
159
|
+
|
|
160
|
+
### TCP Server options
|
|
133
161
|
|
|
134
162
|
```typescript
|
|
135
163
|
const slave = new ModbusSlave({
|
|
@@ -145,7 +173,7 @@ const slave = new ModbusSlave({
|
|
|
145
173
|
});
|
|
146
174
|
```
|
|
147
175
|
|
|
148
|
-
### Physical layer
|
|
176
|
+
### Physical layer option reference
|
|
149
177
|
|
|
150
178
|
| Option | Type | Description |
|
|
151
179
|
|--------|------|-------------|
|
|
@@ -154,54 +182,35 @@ const slave = new ModbusSlave({
|
|
|
154
182
|
| `idleTimeout` | `number` | Idle timeout in ms before evicting a connection. Default `30000`. Pass `0` to disable. |
|
|
155
183
|
| `socketOpts` / `serverOpts` | `object` | Forwarded to Node.js `createSocket()` / `createServer()`. |
|
|
156
184
|
|
|
157
|
-
##
|
|
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.
|
|
185
|
+
## Protocol Layer
|
|
174
186
|
|
|
175
|
-
|
|
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 |
|
|
176
192
|
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
```typescript
|
|
180
|
-
new ModbusMaster({
|
|
181
|
-
physical: { type: 'TCP_CLIENT' },
|
|
182
|
-
protocol: { type: 'TCP' }, // 'TCP' | 'RTU' | 'ASCII'
|
|
183
|
-
timeout?: 1000, // per-request timeout (ms)
|
|
184
|
-
concurrent?: false, // pipelined mode (protocol: TCP only)
|
|
185
|
-
})
|
|
186
|
-
```
|
|
187
|
-
|
|
188
|
-
RTU framing options:
|
|
193
|
+
### RTU options
|
|
189
194
|
|
|
190
195
|
```typescript
|
|
191
196
|
protocol: {
|
|
192
197
|
type: 'RTU',
|
|
193
198
|
opts: {
|
|
194
|
-
//
|
|
195
|
-
//
|
|
196
|
-
//
|
|
197
|
-
intervalBetweenFrames: 20,
|
|
198
|
-
|
|
199
|
-
|
|
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,
|
|
200
209
|
},
|
|
201
210
|
}
|
|
202
211
|
```
|
|
203
212
|
|
|
204
|
-
ASCII options
|
|
213
|
+
### ASCII options
|
|
205
214
|
|
|
206
215
|
```typescript
|
|
207
216
|
protocol: {
|
|
@@ -212,11 +221,24 @@ protocol: {
|
|
|
212
221
|
}
|
|
213
222
|
```
|
|
214
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
|
+
|
|
215
237
|
### Methods
|
|
216
238
|
|
|
217
239
|
| Method | Description |
|
|
218
240
|
|--------|-------------|
|
|
219
|
-
| `open(...args)` | Open the physical layer.
|
|
241
|
+
| `open(...args)` | Open the physical layer. One-shot: cannot reopen after `close()`. |
|
|
220
242
|
| `close()` | Close immediately. In-flight and queued requests are rejected. |
|
|
221
243
|
| `readCoils(unit, address, length, timeout?)` | FC 01 |
|
|
222
244
|
| `readDiscreteInputs(unit, address, length, timeout?)` | FC 02 |
|
|
@@ -238,11 +260,13 @@ Broadcast (`unit = 0`) resolves `void` — slaves never respond.
|
|
|
238
260
|
|
|
239
261
|
## Slave API
|
|
240
262
|
|
|
263
|
+
### Constructor
|
|
264
|
+
|
|
241
265
|
```typescript
|
|
242
266
|
new ModbusSlave({
|
|
243
267
|
physical: { type: 'TCP_SERVER' },
|
|
244
268
|
protocol: { type: 'TCP' },
|
|
245
|
-
concurrent?: false, // per-connection concurrent (
|
|
269
|
+
concurrent?: false, // per-connection concurrent (TCP only)
|
|
246
270
|
})
|
|
247
271
|
```
|
|
248
272
|
|
|
@@ -255,8 +279,8 @@ slave.add({
|
|
|
255
279
|
// Optional: intercept any FC before default dispatch
|
|
256
280
|
interceptor?: (fc, data) => Buffer | undefined,
|
|
257
281
|
|
|
258
|
-
readCoils?: (address, length) =>
|
|
259
|
-
readDiscreteInputs?: (address, length) =>
|
|
282
|
+
readCoils?: (address, length) => Uint8Array,
|
|
283
|
+
readDiscreteInputs?: (address, length) => Uint8Array,
|
|
260
284
|
readHoldingRegisters?: (address, length) => number[] | Uint16Array,
|
|
261
285
|
readInputRegisters?: (address, length) => number[] | Uint16Array,
|
|
262
286
|
|
|
@@ -283,31 +307,70 @@ slave.add({
|
|
|
283
307
|
|
|
284
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`.
|
|
285
309
|
|
|
310
|
+
### Methods
|
|
311
|
+
|
|
286
312
|
| Method | Description |
|
|
287
313
|
|--------|-------------|
|
|
288
314
|
| `add(model)` | Register a slave model |
|
|
289
|
-
| `remove(unit)` | Remove a
|
|
290
|
-
| `open(...args)` | Open
|
|
315
|
+
| `remove(unit)` | Remove a model |
|
|
316
|
+
| `open(...args)` | Open and begin accepting connections. One-shot. |
|
|
291
317
|
| `close()` | Close immediately |
|
|
292
|
-
| `addCustomFunctionCode(cfc)` | Register
|
|
293
|
-
| `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
|
+
```
|
|
294
357
|
|
|
295
358
|
## Custom Function Codes
|
|
296
359
|
|
|
297
|
-
`predictRequestLength` / `predictResponseLength` receive a
|
|
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.
|
|
298
361
|
|
|
299
362
|
```typescript
|
|
300
363
|
import type { CustomFunctionCode } from 'njs-modbus';
|
|
301
364
|
|
|
302
365
|
const cfc: CustomFunctionCode = {
|
|
303
366
|
fc: 0x50,
|
|
304
|
-
predictRequestLength: (
|
|
305
|
-
if (
|
|
306
|
-
return 4 +
|
|
367
|
+
predictRequestLength: (getByte, length) => {
|
|
368
|
+
if (length < 2) return 0; // need more bytes
|
|
369
|
+
return 4 + getByte(1);
|
|
307
370
|
},
|
|
308
|
-
predictResponseLength: (
|
|
309
|
-
if (
|
|
310
|
-
return 4 +
|
|
371
|
+
predictResponseLength: (getByte, length) => {
|
|
372
|
+
if (length < 2) return 0;
|
|
373
|
+
return 4 + getByte(1);
|
|
311
374
|
},
|
|
312
375
|
handle: async (data, unit) => {
|
|
313
376
|
return Buffer.from([data[1], data[0]]);
|
|
@@ -348,82 +411,65 @@ slave.add({
|
|
|
348
411
|
});
|
|
349
412
|
```
|
|
350
413
|
|
|
351
|
-
##
|
|
414
|
+
## Custom Physical Layer
|
|
415
|
+
|
|
416
|
+
Any byte-oriented transport can be used by implementing `AbstractPhysicalLayer` and `AbstractPhysicalConnection`, then passing `{ type: 'CUSTOM', layer: yourLayer }`.
|
|
352
417
|
|
|
353
|
-
|
|
418
|
+
```typescript
|
|
419
|
+
import { AbstractPhysicalLayer, AbstractPhysicalConnection } from 'njs-modbus';
|
|
354
420
|
|
|
355
|
-
|
|
356
|
-
|
|
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) |
|
|
421
|
+
class MyPhysicalLayer extends AbstractPhysicalLayer { /* open / close */ }
|
|
422
|
+
class MyConnection extends AbstractPhysicalConnection { /* write / destroy, emit 'data' */ }
|
|
364
423
|
|
|
365
|
-
|
|
366
|
-
|
|
424
|
+
const master = new ModbusMaster({
|
|
425
|
+
physical: { type: 'CUSTOM', layer: new MyPhysicalLayer() },
|
|
426
|
+
protocol: { type: 'TCP' },
|
|
427
|
+
});
|
|
428
|
+
```
|
|
367
429
|
|
|
368
|
-
|
|
430
|
+
See [`examples/websocket`](./examples/websocket) for a complete **Modbus TCP over WebSocket** implementation.
|
|
369
431
|
|
|
370
|
-
|
|
432
|
+
See [`examples/bluetooth`](./examples/bluetooth) for a **Modbus TCP over BLE** implementation using the Nordic UART Service (NUS).
|
|
371
433
|
|
|
372
|
-
|
|
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
|
|
376
|
-
```
|
|
434
|
+
## Performance
|
|
377
435
|
|
|
378
|
-
|
|
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.
|
|
379
437
|
|
|
380
|
-
|
|
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
|
|
384
|
-
```
|
|
438
|
+
Environment: AMD Ryzen 7 9800X3D 8-Core Processor · Node.js v24.16.0 · [full report](./benchmark/report_presentation.md)
|
|
385
439
|
|
|
386
|
-
###
|
|
440
|
+
### End-to-end TCP
|
|
387
441
|
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
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) |
|
|
393
447
|
|
|
394
|
-
### Frame
|
|
448
|
+
### Frame encode / decode (CPU micro-benchmark)
|
|
395
449
|
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
asciiReqDecode: njs-modbus 3.37M ops/sec 🏆 modbus-serial 0.34x
|
|
405
|
-
```
|
|
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) |
|
|
406
458
|
|
|
407
|
-
### Per-
|
|
459
|
+
### Per-function-code TCP throughput (100 coils / 50 registers)
|
|
408
460
|
|
|
409
461
|
| Function Code | njs-modbus | jsmodbus | modbus-serial |
|
|
410
462
|
|---------------|-----------|----------|---------------|
|
|
411
|
-
| FC01 Read Coils | **
|
|
412
|
-
|
|
|
413
|
-
|
|
|
414
|
-
|
|
|
415
|
-
|
|
|
416
|
-
|
|
|
417
|
-
|
|
|
418
|
-
|
|
|
419
|
-
|
|
420
|
-
|
|
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
|
-
|
|
426
|
-
</details>
|
|
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.
|
|
427
473
|
|
|
428
474
|
## License
|
|
429
475
|
|