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.zh-CN.md CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  [English](./README.md) | 简体中文
4
4
 
5
- Node.js 的纯 JavaScript Modbus 实现。
5
+ Node.js 的纯 JavaScript Modbus 实现,针对高吞吐量和低 GC 压力优化。
6
6
 
7
7
  <!-- prettier-ignore-start -->
8
8
  [![npm download](http://img.shields.io/npm/dw/njs-modbus.svg?style=flat-square)](http://www.npm-stats.com/~packages/njs-modbus)
@@ -11,17 +11,20 @@ Node.js 的纯 JavaScript Modbus 实现。
11
11
  [![CI](https://img.shields.io/github/actions/workflow/status/xiejay97/njs-modbus/ci.yml?branch=main&style=flat-square)](https://github.com/xiejay97/njs-modbus/actions)
12
12
  <!-- prettier-ignore-end -->
13
13
 
14
- ## 特性
14
+ ## 核心亮点
15
15
 
16
- - **协议:** Modbus TCP、RTU、ASCII
17
- - **传输:** 串口、TCP 客户端/服务端、UDP 客户端/服务端
18
- - **主站:** FIFO 或流水线并发请求(协议必须为 TCP)
19
- - **从站:** 每连接 FIFO 或并发处理(协议必须为 TCP)
16
+ - **协议** Modbus TCP、RTU、ASCII
17
+ - **传输** 串口、TCP 客户端/服务端、UDP 客户端/服务端,支持可插拔自定义传输层
18
+ - **性能优先** — 并行 FIFO 队列、惰性删除定时器堆、内联大端读写、零分配热路径
19
+ - **主站** FIFO 或流水线并发请求(仅 TCP)
20
+ - **从站** — 每连接 FIFO 或并发处理(仅 TCP)
20
21
  - **自定义功能码**,支持可插拔帧解析
21
- - **广播**(单元号 = 0
22
+ - **广播**(`unit = 0`)
22
23
  - **完整 TypeScript**
23
24
  - **零运行时依赖**(仅在使用串口时 peer-depends `serialport`)
24
25
 
26
+ ## 支持的功能码
27
+
25
28
  | 码值 | 名称 |
26
29
  |------|------|
27
30
  | 01 | 读取线圈 |
@@ -116,20 +119,45 @@ const master = new ModbusMaster({
116
119
  });
117
120
  ```
118
121
 
122
+ ## 架构
123
+
124
+ 本库遵循严格的两层设计:
125
+
126
+ ```
127
+ ┌─────────────────────────────────────────┐
128
+ │ ModbusMaster / ModbusSlave │ ← 用户 API,会话/队列管理
129
+ ├─────────────────────────────────────────┤
130
+ │ TcpApplicationLayer │ ← 协议帧解析(MBAP / CRC16 / LRC)
131
+ │ RtuApplicationLayer │
132
+ │ AsciiApplicationLayer │
133
+ ├─────────────────────────────────────────┤
134
+ │ AbstractPhysicalConnection │ ← 每连接 I/O
135
+ ├─────────────────────────────────────────┤
136
+ │ AbstractPhysicalLayer │ ← 资源管理(端口/套接字/服务端)
137
+ │ ├── TcpClientPhysicalLayer │
138
+ │ ├── TcpServerPhysicalLayer │
139
+ │ ├── UdpClientPhysicalLayer │
140
+ │ ├── UdpServerPhysicalLayer │
141
+ │ └── SerialPhysicalLayer │
142
+ └─────────────────────────────────────────┘
143
+ ```
144
+
145
+ 所有传输层都统一为面向连接的模型:物理层在就绪时触发 `'connect'` 事件并传入一个 `AbstractPhysicalConnection`,每个连接会独立创建一个应用层实例。
146
+
119
147
  ## 物理层
120
148
 
121
- 所有物理层暴露 `open()` / `close()`、`state` 属性,以及事件:`'open'`、`'connect'`、`'close'`、`'error'`。
149
+ 所有物理层暴露 `open()` / `close()`、`state` 属性,以及 [事件](#事件) 章节中列出的事件。
122
150
 
123
- | 类型 | 类 | `open(...)` 参数 |
124
- |------|-------|-----------------|
125
- | `SERIAL` | `SerialPhysicalLayer` | |
126
- | `TCP_CLIENT` | `TcpClientPhysicalLayer` | `SocketConnectOpts` |
127
- | `TCP_SERVER` | `TcpServerPhysicalLayer` | `ListenOptions` |
128
- | `UDP_CLIENT` | `UdpClientPhysicalLayer` | `{ port, address }` |
129
- | `UDP_SERVER` | `UdpServerPhysicalLayer` | `BindOptions` |
130
- | `CUSTOM` | *(用户自定义)* | *(自定义)* |
151
+ | 类型 | `open(...)` 参数 | 说明 |
152
+ |------|-----------------|------|
153
+ | `SERIAL` | `()` | 通过 `serialport` 包访问串口 |
154
+ | `TCP_CLIENT` | `SocketConnectOpts` | TCP 客户端套接字 |
155
+ | `TCP_SERVER` | `ListenOptions` | TCP 服务端 |
156
+ | `UDP_CLIENT` | `{ port?, address? }` | UDP 客户端 |
157
+ | `UDP_SERVER` | `BindOptions` | UDP 服务端 |
158
+ | `CUSTOM` | *(用户自定义)* | 用户提供的 `AbstractPhysicalLayer` 实例 |
131
159
 
132
- ### 服务端配置
160
+ ### TCP 服务端配置
133
161
 
134
162
  ```typescript
135
163
  const slave = new ModbusSlave({
@@ -145,62 +173,44 @@ const slave = new ModbusSlave({
145
173
  });
146
174
  ```
147
175
 
148
- ### 物理层选项
176
+ ### 物理层选项参考
149
177
 
150
178
  | 选项 | 类型 | 说明 |
151
- |--------|------|-------------|
179
+ |------|------|------|
152
180
  | `whitelist` | `string[]` | 允许的客户端 IP。`::ffff:` 前缀自动剥离。 |
153
181
  | `maxConnections` | `number` | 最大并发连接数。超出后新连接静默丢弃。 |
154
182
  | `idleTimeout` | `number` | 空闲超时(毫秒),超时后驱逐连接。默认 `30000`,传 `0` 禁用。 |
155
183
  | `socketOpts` / `serverOpts` | `object` | 透传给 Node.js `createSocket()` / `createServer()`。 |
156
184
 
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** 实现——包含客户端/服务端物理层及可运行的演示。
185
+ ## 协议层
174
186
 
175
- 查看 [`examples/bluetooth`](./examples/bluetooth) 获取 **BLE 承载 Modbus TCP** 实现(基于 Nordic UART Service),包含无需真实蓝牙硬件即可运行的回环测试。
187
+ | 协议 | 选项 | 说明 |
188
+ |------|------|------|
189
+ | `TCP` | 无 | Modbus TCP,带 MBAP 头 |
190
+ | `RTU` | `RtuProtocolOptions` | Modbus RTU,带 CRC16 |
191
+ | `ASCII` | `AsciiApplicationLayerOptions` | Modbus ASCII,带 LRC |
176
192
 
177
- ## 主站 API
178
-
179
- ```typescript
180
- new ModbusMaster({
181
- physical: { type: 'TCP_CLIENT' },
182
- protocol: { type: 'TCP' }, // 'TCP' | 'RTU' | 'ASCII'
183
- timeout?: 1000, // 单次请求超时(毫秒)
184
- concurrent?: false, // 流水线模式(协议:仅 TCP)
185
- })
186
- ```
187
-
188
- RTU 帧选项:
193
+ ### RTU 选项
189
194
 
190
195
  ```typescript
191
196
  protocol: {
192
197
  type: 'RTU',
193
198
  opts: {
194
- // 既可以传裸数字(毫秒),也可以传 `{ unit: 'bit' | 'ms', value: N }`。
195
- // `0` 显式禁用该定时器(适用于 RTU-over-TCP 等无丢包传输)。
196
- intervalBetweenFrames: 20, // 20 毫秒
197
- interCharTimeout: { unit: 'bit', value: 10 }, // 按 bit-time,需 baudRate
198
- poolSize: 1024,
199
+ // t3.5 帧间静默时间。
200
+ // 既可以传裸数字(毫秒),也可以传 { unit: 'bit' | 'ms', value: N }。
201
+ // 0 显式禁用(适用于 RTU-over-TCP 等无丢包传输)。
202
+ intervalBetweenFrames: 20,
203
+
204
+ // t1.5 字符间超时(可选)。
205
+ interCharTimeout: { unit: 'bit', value: 10 },
206
+
207
+ // 丢弃存在 t1.5 时序违规的帧。
208
+ strictTiming: true,
199
209
  },
200
210
  }
201
211
  ```
202
212
 
203
- ASCII 选项:
213
+ ### ASCII 选项
204
214
 
205
215
  ```typescript
206
216
  protocol: {
@@ -211,11 +221,24 @@ protocol: {
211
221
  }
212
222
  ```
213
223
 
224
+ ## 主站 API
225
+
226
+ ### 构造函数
227
+
228
+ ```typescript
229
+ new ModbusMaster({
230
+ physical: { type: 'TCP_CLIENT' },
231
+ protocol: { type: 'TCP' }, // 'TCP' | 'RTU' | 'ASCII'
232
+ timeout?: 1000, // 单次请求超时(毫秒)
233
+ concurrent?: false, // 流水线模式(仅 TCP)
234
+ })
235
+ ```
236
+
214
237
  ### 方法
215
238
 
216
239
  | 方法 | 说明 |
217
- |--------|-------------|
218
- | `open(...args)` | 打开物理层。只能调用一次;`close()` 后实例永久失效。 |
240
+ |------|------|
241
+ | `open(...args)` | 打开物理层。一次性:close() 后不可重新打开。 |
219
242
  | `close()` | 立即关闭。进行中和队列中的请求会被 reject。 |
220
243
  | `readCoils(unit, address, length, timeout?)` | FC 01 |
221
244
  | `readDiscreteInputs(unit, address, length, timeout?)` | FC 02 |
@@ -237,11 +260,13 @@ protocol: {
237
260
 
238
261
  ## 从站 API
239
262
 
263
+ ### 构造函数
264
+
240
265
  ```typescript
241
266
  new ModbusSlave({
242
267
  physical: { type: 'TCP_SERVER' },
243
268
  protocol: { type: 'TCP' },
244
- concurrent?: false, // 每连接并发(协议:仅 TCP)
269
+ concurrent?: false, // 每连接并发(仅 TCP)
245
270
  })
246
271
  ```
247
272
 
@@ -254,8 +279,8 @@ slave.add({
254
279
  // 可选:在默认分发前拦截任意功能码
255
280
  interceptor?: (fc, data) => Buffer | undefined,
256
281
 
257
- readCoils?: (address, length) => boolean[] | Uint8Array,
258
- readDiscreteInputs?: (address, length) => boolean[] | Uint8Array,
282
+ readCoils?: (address, length) => Uint8Array,
283
+ readDiscreteInputs?: (address, length) => Uint8Array,
259
284
  readHoldingRegisters?: (address, length) => number[] | Uint16Array,
260
285
  readInputRegisters?: (address, length) => number[] | Uint16Array,
261
286
 
@@ -282,31 +307,70 @@ slave.add({
282
307
 
283
308
  缺失的处理器返回 `ILLEGAL_FUNCTION`。越界地址返回 `ILLEGAL_DATA_ADDRESS`。处理器抛出的异常变为 `SERVER_DEVICE_FAILURE`,除非错误是 `ModbusError`。
284
309
 
310
+ ### 方法
311
+
285
312
  | 方法 | 说明 |
286
- |--------|-------------|
313
+ |------|------|
287
314
  | `add(model)` | 注册从站模型 |
288
- | `remove(unit)` | 移除从站模型 |
289
- | `open(...args)` | 打开物理层。一次性:close() 后不可重新打开。 |
315
+ | `remove(unit)` | 移除模型 |
316
+ | `open(...args)` | 打开并开始接受连接。一次性。 |
290
317
  | `close()` | 立即关闭 |
291
318
  | `addCustomFunctionCode(cfc)` | 注册自定义功能码 |
292
319
  | `removeCustomFunctionCode(fc)` | 注销自定义功能码 |
293
320
 
321
+ ## 事件
322
+
323
+ `ModbusMaster` 和 `ModbusSlave` 都是 `EventEmitter`,所有事件均带类型定义。
324
+
325
+ ### 物理层事件
326
+
327
+ | 事件 | 参数 | 说明 |
328
+ |------|------|------|
329
+ | `open` | `()` | 物理层就绪,可接受连接 |
330
+ | `connect` | `(connection)` | 新连接已建立 |
331
+ | `close` | `()` | 物理层已关闭 |
332
+ | `error` | `(error)` | 物理层错误 |
333
+
334
+ ### 连接调试事件
335
+
336
+ | 事件 | 参数 | 说明 |
337
+ |------|------|------|
338
+ | `tx` | `(buffer, connection)` | 原始数据已写入链路 |
339
+ | `rx` | `(buffer, connection)` | 从链路接收到原始数据 |
340
+
341
+ ### 应用层事件
342
+
343
+ | 事件 | 参数 | 说明 |
344
+ |------|------|------|
345
+ | `framing` | `(frame, connection)` | 完整帧已解码 |
346
+ | `framingError` | `(error, connection)` | 帧解析错误(CRC/LRC 失败、MBAP 格式错误、时序违规等) |
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
+ ```
357
+
294
358
  ## 自定义功能码
295
359
 
296
- `predictRequestLength` / `predictResponseLength` 接收共享池缓冲区及字节偏移量(`start`、`end`)。边界检查应使用 `end - start` 而非 `buffer.length`。
360
+ `predictRequestLength` / `predictResponseLength` 接收 `getByte` 访问器和当前可用字节数 `length`。请使用 `length` 做边界检查,不要直接访问缓冲区。
297
361
 
298
362
  ```typescript
299
363
  import type { CustomFunctionCode } from 'njs-modbus';
300
364
 
301
365
  const cfc: CustomFunctionCode = {
302
366
  fc: 0x50,
303
- predictRequestLength: (buffer, start, end) => {
304
- if (end - start < 2) return null;
305
- return 4 + buffer[start + 1];
367
+ predictRequestLength: (getByte, length) => {
368
+ if (length < 2) return 0; // 需要更多字节
369
+ return 4 + getByte(1);
306
370
  },
307
- predictResponseLength: (buffer, start, end) => {
308
- if (end - start < 2) return null;
309
- return 4 + buffer[start + 1];
371
+ predictResponseLength: (getByte, length) => {
372
+ if (length < 2) return 0;
373
+ return 4 + getByte(1);
310
374
  },
311
375
  handle: async (data, unit) => {
312
376
  return Buffer.from([data[1], data[0]]);
@@ -347,82 +411,65 @@ slave.add({
347
411
  });
348
412
  ```
349
413
 
350
- ## 性能
414
+ ## 自定义物理层
415
+
416
+ 任何面向字节的传输层都可以通过实现 `AbstractPhysicalLayer` 和 `AbstractPhysicalConnection` 来使用,然后传入 `{ type: 'CUSTOM', layer: yourLayer }`。
351
417
 
352
- 与 [jsmodbus](https://github.com/Cloud-Automation/node-modbus) 和 [modbus-serial](https://github.com/yaacov/node-modbus-serial) 的对比。
418
+ ```typescript
419
+ import { AbstractPhysicalLayer, AbstractPhysicalConnection } from 'njs-modbus';
353
420
 
354
- | 指标 | njs-modbus | jsmodbus | modbus-serial |
355
- |------|-----------|----------|---------------|
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) |
421
+ class MyPhysicalLayer extends AbstractPhysicalLayer { /* open / close */ }
422
+ class MyConnection extends AbstractPhysicalConnection { /* write / destroy, emit 'data' */ }
363
423
 
364
- <details>
365
- <summary>完整基准测试结果</summary>
424
+ const master = new ModbusMaster({
425
+ physical: { type: 'CUSTOM', layer: new MyPhysicalLayer() },
426
+ protocol: { type: 'TCP' },
427
+ });
428
+ ```
366
429
 
367
- Node.js v24.16.0 · AMD Ryzen 7 9800X3D · linux x64 · 3 次运行 × 120 秒 · [完整报告](./benchmark/RESULTS.md)
430
+ 查看 [`examples/websocket`](./examples/websocket) 获取完整的 **WebSocket 承载 Modbus TCP** 实现。
368
431
 
369
- ### TCP 吞吐量(单连接)
432
+ 查看 [`examples/bluetooth`](./examples/bluetooth) 获取基于 Nordic UART Service (NUS) 的 **BLE 承载 Modbus TCP** 实现。
370
433
 
371
- ```
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
375
- ```
434
+ ## 性能
376
435
 
377
- ### 并发(8 连接)
436
+ [jsmodbus](https://github.com/Cloud-Automation/node-modbus) v4.0.10 和 [modbus-serial](https://github.com/yaacov/node-modbus-serial) v8.0.25 的对比。
378
437
 
379
- ```
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
383
- ```
438
+ 测试环境:AMD Ryzen 7 9800X3D 8-Core Processor · Node.js v24.16.0 · [完整报告](./benchmark/report_presentation.md)
384
439
 
385
- ### RTU 串口(socat PTY 模拟 115200 波特率)
440
+ ### TCP 端到端
386
441
 
387
- ```
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
391
- ```
442
+ | 指标 | njs-modbus | jsmodbus | modbus-serial |
443
+ |------|-----------|----------|---------------|
444
+ | 单连接 | **74,964 ops/sec** 🏆 | 55,996 (0.75x) | 853 (0.01x) |
445
+ | 8 并发客户端 | **10,188 ops/sec** 🏆 | 5,918 (0.58x) | 728 (0.07x) |
446
+ | P99 延迟(单连接) | **17 µs** 🏆 | 15 µs (0.88x) | 1,206 µs (70.9x) |
392
447
 
393
- ### 帧编码 / 解码(CPU 微基准测试)
448
+ ### 帧编码 / 解码(CPU 微基准)
394
449
 
395
- ```
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
404
- ```
450
+ | 基准测试 | njs-modbus | jsmodbus | modbus-serial |
451
+ |----------|-----------|----------|---------------|
452
+ | TCP 请求编码 | **9.61M ops/sec** 🏆 | 6.32M (0.66x) | 0.68M (0.07x) |
453
+ | TCP 响应编码 | **7.51M ops/sec** 🏆 | 1.18M (0.16x) | 0.44M (0.06x) |
454
+ | TCP 响应解码 | **8.55M ops/sec** 🏆 | 2.04M (0.24x) | 0.70M (0.08x) |
455
+ | RTU 请求编码 | **9.42M ops/sec** 🏆 | 3.03M (0.32x) | 6.57M (0.70x) |
456
+ | ASCII 请求编码 | **7.09M ops/sec** 🏆 | — | 4.11M (0.58x) |
457
+ | ASCII 请求解码 | **4.91M ops/sec** 🏆 | — | 1.19M (0.24x) |
405
458
 
406
- ### 全功能码 TCP 吞吐量(常规负载)
459
+ ### 全功能码 TCP 吞吐(100 线圈 / 50 寄存器负载)
407
460
 
408
461
  | 功能码 | njs-modbus | jsmodbus | modbus-serial |
409
462
  |--------|-----------|----------|---------------|
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
-
425
- </details>
463
+ | FC01 读取线圈 | **75,430** 🏆 | 488 (0.01x) | 859 (0.01x) |
464
+ | FC03 读取保持寄存器 | **82,028** 🏆 | 46,596 (0.57x) | 864 (0.01x) |
465
+ | FC06 写单个寄存器 | **86,110** 🏆 | 51,084 (0.59x) | 871 (0.01x) |
466
+ | FC15 写多个线圈 | **78,766** 🏆 | | 867 (0.01x) |
467
+ | FC17 报告服务器 ID | **79,185** 🏆 | | |
468
+ | FC22 掩码写寄存器 | **73,060** 🏆 | | |
469
+ | FC23 读/写多个寄存器 | **68,429** 🏆 | | |
470
+ | FC43 读取设备标识 | **62,176** 🏆 | | 868 (0.01x) |
471
+
472
+ > FC17 / FC22 / FC23 / FC43 不受 jsmodbus 支持。
426
473
 
427
474
  ## 许可证
428
475