njs-modbus 3.1.1 → 3.2.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 +20 -0
  2. package/README.zh-CN.md +20 -0
  3. package/dist/index.cjs +878 -384
  4. package/dist/index.d.ts +87 -25
  5. package/dist/index.mjs +878 -385
  6. package/dist/utils.cjs +439 -0
  7. package/dist/utils.d.ts +161 -0
  8. package/dist/utils.mjs +425 -0
  9. package/package.json +15 -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/dist/index.d.ts CHANGED
@@ -44,10 +44,30 @@ interface DeviceIdentification {
44
44
  */
45
45
  interface CustomFunctionCode {
46
46
  fc: number;
47
- /** Predict total RTU frame length for an incoming request (slave-side framing). */
48
- predictRequestLength: (buffer: Buffer) => number | null;
49
- /** Predict total RTU frame length for an incoming response (master-side framing). */
50
- predictResponseLength: (buffer: Buffer) => number | null;
47
+ /**
48
+ * Predict total RTU frame length for an incoming request (slave-side framing).
49
+ *
50
+ * @param buffer The raw frame buffer (a shared pool or incoming chunk). Do NOT
51
+ * modify it; the framing layer owns the memory.
52
+ * @param start — Byte offset in `buffer` where the current candidate frame begins
53
+ * (the unit ID byte). Read the function code at `buffer[start + 1]` and derive
54
+ * the total frame length from the trailing bytes.
55
+ * @returns Total frame length (>= 4, <= 256), or `null` if more bytes are needed.
56
+ */
57
+ predictRequestLength: (buffer: Buffer, start: number, end: number) => number | null;
58
+ /**
59
+ * Predict total RTU frame length for an incoming response (master-side framing).
60
+ *
61
+ * @param buffer — The raw frame buffer (a shared pool or incoming chunk). Do NOT
62
+ * modify it; the framing layer owns the memory.
63
+ * @param start — Byte offset in `buffer` where the current candidate frame begins
64
+ * (the unit ID byte). Read the function code at `buffer[start + 1]` and derive
65
+ * the total frame length from the trailing bytes.
66
+ * @param end — Byte offset one past the last valid byte. Bounds checks must use
67
+ * `end - start` as available bytes, NOT `buffer.length` (pool capacity).
68
+ * @returns Total frame length (>= 4, <= 256), or `null` if more bytes are needed.
69
+ */
70
+ predictResponseLength: (buffer: Buffer, start: number, end: number) => number | null;
51
71
  /**
52
72
  * Slave-side handler. Receives PDU payload (bytes after `fc`, before CRC) and
53
73
  * the unit ID being addressed; must return the PDU payload of the response.
@@ -115,6 +135,8 @@ declare enum ConformityLevel {
115
135
  }
116
136
  /** Shared empty Buffer to avoid repeated allocations. */
117
137
  declare const EMPTY_BUFFER: Buffer<ArrayBuffer>;
138
+ /** Shared no-op function to avoid repeated allocations. */
139
+ declare const NOOP: () => void;
118
140
  /** Modbus V1.1b3 PDU quantity limits. */
119
141
  declare const LIMITS: {
120
142
  readonly READ_COILS_MIN: 1;
@@ -353,25 +375,37 @@ type PhysicalConfig = {
353
375
  type: 'CUSTOM';
354
376
  layer: AbstractPhysicalLayer;
355
377
  };
378
+ /**
379
+ * User-facing arguments for `Master.open()` / `Slave.open()`.
380
+ *
381
+ * These match the physical layer's `open()` signatures **without** the trailing
382
+ * callback — `promisifyCb` appends it internally so the user gets a `Promise`.
383
+ *
384
+ * | Config | User-facing args |
385
+ * |---------------|--------------------------------------|
386
+ * | SERIAL | `()` — configured at construction |
387
+ * | TCP_CLIENT | `(opts?)` — `SocketConnectOpts` |
388
+ * | TCP_SERVER | `(opts?)` — `ListenOptions` |
389
+ * | UDP_CLIENT | `(remote?)` — `{ port?, address? }` |
390
+ * | UDP_SERVER | `(opts?)` — `BindOptions` |
391
+ * | CUSTOM | `never` — call `layer.open()` directly |
392
+ */
356
393
  type OpenArgs<T extends PhysicalConfig> = T extends {
357
394
  type: 'SERIAL';
358
- } ? Parameters<SerialPhysicalLayer['open']> : T extends {
395
+ } ? [] : T extends {
359
396
  type: 'TCP_CLIENT';
360
- } ? Parameters<TcpClientPhysicalLayer['open']> : T extends {
397
+ } ? [options?: SocketConnectOpts] : T extends {
361
398
  type: 'TCP_SERVER';
362
- } ? Parameters<TcpServerPhysicalLayer['open']> : T extends {
399
+ } ? [options?: ListenOptions] : T extends {
363
400
  type: 'UDP_CLIENT';
364
- } ? Parameters<UdpClientPhysicalLayer['open']> : T extends {
401
+ } ? [remote?: {
402
+ port?: number;
403
+ address?: string;
404
+ }] : T extends {
365
405
  type: 'UDP_SERVER';
366
- } ? Parameters<UdpServerPhysicalLayer['open']> : never;
406
+ } ? [options?: BindOptions] : never;
367
407
  declare function createPhysicalLayer(config: PhysicalConfig): AbstractPhysicalLayer;
368
408
 
369
- interface AbstractApplicationLayerEvents {
370
- framing: [frame: ApplicationDataUnit & {
371
- buffer: Buffer;
372
- }];
373
- 'framing-error': [error: Error];
374
- }
375
409
  /**
376
410
  * Application-layer protocol handler bound to a single physical connection.
377
411
  *
@@ -379,10 +413,16 @@ interface AbstractApplicationLayerEvents {
379
413
  * established and discarded when the connection closes. Subclasses implement
380
414
  * ASCII, RTU, or TCP framing rules.
381
415
  */
382
- declare abstract class AbstractApplicationLayer extends EventEmitter<AbstractApplicationLayerEvents> {
416
+ declare abstract class AbstractApplicationLayer {
383
417
  abstract readonly PROTOCOL: 'ASCII' | 'RTU' | 'TCP';
384
418
  abstract ROLE: 'MASTER' | 'SLAVE';
385
419
  abstract readonly connection: AbstractPhysicalConnection;
420
+ /** Called when a complete frame is decoded. Defaults to no-op. */
421
+ onFraming: (frame: ApplicationDataUnit & {
422
+ buffer: Buffer;
423
+ }) => void;
424
+ /** Called when a framing error is detected. Defaults to no-op. */
425
+ onFramingError: (error: Error) => void;
386
426
  flush(): void;
387
427
  addCustomFunctionCode(cfc: CustomFunctionCode): void;
388
428
  removeCustomFunctionCode(fc: number): void;
@@ -403,7 +443,7 @@ declare class AsciiApplicationLayer extends AbstractApplicationLayer {
403
443
  readonly lenientHex: boolean;
404
444
  private _connection;
405
445
  private _state;
406
- private _cleanupFns;
446
+ private _cleanupCbs;
407
447
  get connection(): AbstractPhysicalConnection;
408
448
  constructor(role: 'MASTER' | 'SLAVE', connection: AbstractPhysicalConnection, options?: AsciiApplicationLayerOptions);
409
449
  private framing;
@@ -432,10 +472,18 @@ declare class RtuApplicationLayer extends AbstractApplicationLayer {
432
472
  private _threePointFiveT;
433
473
  private _onePointFiveT;
434
474
  private _customFunctionCodes;
435
- private _cleanupFns;
475
+ private _cleanupCbs;
436
476
  get connection(): AbstractPhysicalConnection;
437
477
  constructor(role: 'MASTER' | 'SLAVE', connection: AbstractPhysicalConnection, options?: RtuApplicationLayerOptions);
438
478
  private clearStateTimers;
479
+ /**
480
+ * Shared handler for every "frame is not yet complete" exit in `flushBuffer`.
481
+ * Returns `true` when the caller should `return` (strict reset), `false` to
482
+ * `break` the parse loop. Hot path never reaches here — only error/incomplete
483
+ * edges. Extracted as a method so it is not recreated on every `flushBuffer`
484
+ * call.
485
+ */
486
+ private _handleIncomplete;
439
487
  private flushBuffer;
440
488
  flush(): void;
441
489
  addCustomFunctionCode(cfc: CustomFunctionCode): void;
@@ -449,7 +497,7 @@ declare class TcpApplicationLayer extends AbstractApplicationLayer {
449
497
  private _connection;
450
498
  private _transactionId;
451
499
  private _buffer;
452
- private _cleanupFns;
500
+ private _cleanupCbs;
453
501
  get connection(): AbstractPhysicalConnection;
454
502
  constructor(role: 'MASTER' | 'SLAVE', connection: AbstractPhysicalConnection);
455
503
  private tryExtract;
@@ -546,19 +594,21 @@ declare class ModbusMaster<T extends ModbusMasterOptions = ModbusMasterOptions>
546
594
  private _queueDatas;
547
595
  private _queueTimeouts;
548
596
  private _queueBroadcasts;
549
- private _queueResolves;
550
- private _queueRejects;
597
+ private _queueCallbacks;
551
598
  private _queueHead;
552
599
  private _queueLen;
553
600
  private _draining;
554
601
  private _nextTid;
555
602
  private _cleanupFns;
556
603
  private _closePromise;
604
+ private _nextExchangeId;
605
+ private _pendingExchanges;
606
+ private _timerHeap;
557
607
  get state(): PhysicalState;
558
608
  get physicalLayer(): AbstractPhysicalLayer;
559
609
  constructor(options: T);
560
610
  private _createAppLayer;
561
- private send;
611
+ private _send;
562
612
  private _drain;
563
613
  private _processNext;
564
614
  private _exchange;
@@ -719,7 +769,7 @@ declare class ModbusSlave<T extends ModbusSlaveOptions = ModbusSlaveOptions> ext
719
769
  private _protocol;
720
770
  private _appLayers;
721
771
  private _customFunctionCodes;
722
- private _locks;
772
+ private _intervalLocks;
723
773
  private _cleanupFns;
724
774
  private _closePromise;
725
775
  get state(): PhysicalState;
@@ -742,7 +792,19 @@ declare class ModbusSlave<T extends ModbusSlaveOptions = ModbusSlaveOptions> ext
742
792
  private _drain;
743
793
  private _processFrame;
744
794
  private _intercept;
745
- private _withAddressLock;
795
+ /**
796
+ * Serialize a code block over the half-open address interval `[lo, hi)`.
797
+ * The block runs after all previously-installed locks whose intervals
798
+ * overlap with this one have completed. Two non-overlapping intervals
799
+ * execute in parallel.
800
+ *
801
+ * Locks are tracked in a flat array (`_intervalLocks`); the typical depth
802
+ * is 0-2 entries, so the linear overlap scan is sub-µs. Compare with the
803
+ * old per-address Map design, where FC23 writing 121 registers allocated
804
+ * ~125 objects per request (one Promise.resolve / address + Set + sort +
805
+ * Promise.all); this version allocates 1-3.
806
+ */
807
+ private _withIntervalLock;
746
808
  private _handleFC;
747
809
  private _handleCustomFC;
748
810
  add(model: ModbusSlaveModel): void;
@@ -770,5 +832,5 @@ declare class ModbusSlave<T extends ModbusSlaveOptions = ModbusSlaveOptions> ext
770
832
  close(): Promise<void>;
771
833
  }
772
834
 
773
- export { AbstractApplicationLayer, AbstractPhysicalConnection, AbstractPhysicalLayer, AsciiApplicationLayer, COIL_OFF, COIL_ON, ConformityLevel, EMPTY_BUFFER, EXCEPTION_OFFSET, ErrorCode, FunctionCode, LIMITS, MEI_READ_DEVICE_ID, MasterSession, ModbusError, ModbusMaster, ModbusSlave, PhysicalConnectionState, PhysicalState, ReadDeviceIDCode, RtuApplicationLayer, SerialPhysicalLayer, TcpApplicationLayer, TcpClientPhysicalLayer, TcpServerPhysicalLayer, UdpClientPhysicalLayer, UdpServerPhysicalLayer, createPhysicalLayer, getCodeByError, getErrorByCode };
835
+ export { AbstractApplicationLayer, AbstractPhysicalConnection, AbstractPhysicalLayer, AsciiApplicationLayer, COIL_OFF, COIL_ON, ConformityLevel, EMPTY_BUFFER, EXCEPTION_OFFSET, ErrorCode, FunctionCode, LIMITS, MEI_READ_DEVICE_ID, MasterSession, ModbusError, ModbusMaster, ModbusSlave, NOOP, PhysicalConnectionState, PhysicalState, ReadDeviceIDCode, RtuApplicationLayer, SerialPhysicalLayer, TcpApplicationLayer, TcpClientPhysicalLayer, TcpServerPhysicalLayer, UdpClientPhysicalLayer, UdpServerPhysicalLayer, createPhysicalLayer, getCodeByError, getErrorByCode };
774
836
  export type { AbstractPhysicalLayerEvents, ApplicationDataUnit, AsciiApplicationLayerOptions, Callback$1 as Callback, CustomFunctionCode, DeviceIdentification, MaybeAsyncFunction, ModbusMasterOptions, ModbusSlaveModel, ModbusSlaveOptions, OpenArgs, PhysicalConfig, RtuApplicationLayerOptions, ServerId, TcpServerPhysicalLayerOptions, UdpServerPhysicalLayerOptions };