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/dist/index.d.ts CHANGED
@@ -12,10 +12,11 @@ interface ApplicationDataUnit {
12
12
  data: Buffer;
13
13
  }
14
14
  interface ServerId {
15
- /** Server ID; may be 1 byte (number) or multi-byte (number[]) per device spec. */
15
+ /** Server ID as a byte array (e.g. single-byte IDs use `[id]`). */
16
16
  serverId?: number[];
17
17
  runIndicatorStatus?: boolean;
18
- additionalData?: number[];
18
+ /** Additional data bytes. Using Buffer avoids intermediate array conversions. */
19
+ additionalData?: Buffer;
19
20
  }
20
21
  interface DeviceIdentification {
21
22
  readDeviceIDCode: number;
@@ -39,7 +40,8 @@ interface DeviceIdentification {
39
40
  * (PDU + 2-byte CRC) from leading bytes; they are required so the framing FSM
40
41
  * can advance without the deleted sliding-window CRC fallback.
41
42
  *
42
- * Return `null` from a predictor to signal "need more bytes before I can decide".
43
+ * Return `0` (`PREDICT_NEED_MORE`) to signal "need more bytes before I can decide".
44
+ * Return `-1` (`PREDICT_UNKNOWN`) to signal "cannot determine the length".
43
45
  * Return a positive integer (>= 4, <= 256) for the total frame length.
44
46
  */
45
47
  interface CustomFunctionCode {
@@ -47,27 +49,25 @@ interface CustomFunctionCode {
47
49
  /**
48
50
  * Predict total RTU frame length for an incoming request (slave-side framing).
49
51
  *
50
- * @param bufferThe raw frame buffer (a shared pool or incoming chunk). Do NOT
51
- * modify it; the framing layer owns the memory.
52
- * @param startByte 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.
52
+ * @param getByteZero-based accessor; `getByte(i)` returns the byte at offset
53
+ * `i` within the candidate frame (0 = unit ID, 1 = function code, …).
54
+ * @param lengthNumber of bytes available so far. Use this for bounds checks
55
+ * rather than any buffer property.
56
+ * @returns Total frame length (>= 4, <= 256), `0` if more bytes are needed,
57
+ * or `-1` if the length cannot be determined.
56
58
  */
57
- predictRequestLength: (buffer: Buffer, start: number, end: number) => number | null;
59
+ predictRequestLength: (getByte: (idx: number) => number, length: number) => number;
58
60
  /**
59
61
  * Predict total RTU frame length for an incoming response (master-side framing).
60
62
  *
61
- * @param bufferThe raw frame buffer (a shared pool or incoming chunk). Do NOT
62
- * modify it; the framing layer owns the memory.
63
- * @param startByte 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.
63
+ * @param getByteZero-based accessor; `getByte(i)` returns the byte at offset
64
+ * `i` within the candidate frame (0 = unit ID, 1 = function code, …).
65
+ * @param lengthNumber of bytes available so far. Use this for bounds checks
66
+ * rather than any buffer property.
67
+ * @returns Total frame length (>= 4, <= 256), `0` if more bytes are needed,
68
+ * or `-1` if the length cannot be determined.
69
69
  */
70
- predictResponseLength: (buffer: Buffer, start: number, end: number) => number | null;
70
+ predictResponseLength: (getByte: (idx: number) => number, length: number) => number;
71
71
  /**
72
72
  * Slave-side handler. Receives PDU payload (bytes after `fc`, before CRC) and
73
73
  * the unit ID being addressed; must return the PDU payload of the response.
@@ -316,6 +316,18 @@ declare class UdpServerPhysicalLayer extends AbstractPhysicalLayer {
316
316
  interface AbstractPhysicalConnectionEvents {
317
317
  data: [data: Buffer];
318
318
  close: [];
319
+ /**
320
+ * Emitted when raw data has been successfully written to the wire.
321
+ * The `buffer` is a live reference — mutating it will corrupt the frame
322
+ * that the connection is still processing. Treat it as read-only.
323
+ */
324
+ tx: [buffer: Buffer];
325
+ /**
326
+ * Emitted when raw data is received from the wire.
327
+ * The `buffer` is a live reference — mutating it will corrupt the frame
328
+ * that the application layer is about to parse. Treat it as read-only.
329
+ */
330
+ rx: [buffer: Buffer];
319
331
  }
320
332
  /**
321
333
  * A one-way data transmission channel.
@@ -335,6 +347,28 @@ interface AbstractPhysicalLayerEvents {
335
347
  close: [];
336
348
  error: [error: Error];
337
349
  }
350
+ /** Debug events emitted by individual connections and proxied through Master/Slave. */
351
+ interface ConnectionDebugEvents {
352
+ /**
353
+ * Emitted when raw data has been successfully written to the wire.
354
+ * The `buffer` is a live reference — mutating it will corrupt the frame
355
+ * that the connection is still processing. Treat it as read-only.
356
+ */
357
+ tx: [buffer: Buffer, connection: AbstractPhysicalConnection];
358
+ /**
359
+ * Emitted when raw data is received from the wire.
360
+ * The `buffer` is a live reference — mutating it will corrupt the frame
361
+ * that the application layer is about to parse. Treat it as read-only.
362
+ */
363
+ rx: [buffer: Buffer, connection: AbstractPhysicalConnection];
364
+ }
365
+ /** Application-layer framing events emitted by Master/Slave per connection. */
366
+ interface ConnectionApplicationEvents {
367
+ framing: [frame: ApplicationDataUnit & {
368
+ buffer: Buffer;
369
+ }, connection: AbstractPhysicalConnection];
370
+ framingError: [error: Error, connection: AbstractPhysicalConnection];
371
+ }
338
372
  /**
339
373
  * An abstraction over local hardware or network resources.
340
374
  *
@@ -440,7 +474,6 @@ interface AsciiApplicationLayerOptions {
440
474
  declare class AsciiApplicationLayer extends AbstractApplicationLayer {
441
475
  readonly PROTOCOL: "ASCII";
442
476
  readonly ROLE: 'MASTER' | 'SLAVE';
443
- readonly lenientHex: boolean;
444
477
  private _connection;
445
478
  private _state;
446
479
  private _cleanupCbs;
@@ -457,34 +490,29 @@ interface RtuApplicationLayerOptions {
457
490
  /** Inter-character timeout in milliseconds (Modbus RTU t1.5). Opt-in. */
458
491
  interCharTimeout?: number;
459
492
  /**
460
- * Buffer pool size per connection (bytes). Defaults to `MAX_FRAME_LENGTH * 2`
461
- * (512 bytes). Increase this if you expect frames larger than 256 bytes or
462
- * heavy pipelining on a single connection.
493
+ * Enforces strict Modbus RTU timing. When true, any frame containing a t1.5
494
+ * inter-character timeout event will be discarded immediately, even if the CRC16 is valid.
495
+ * @default false
463
496
  */
464
- poolSize?: number;
497
+ strictTiming?: boolean;
465
498
  }
466
499
  declare class RtuApplicationLayer extends AbstractApplicationLayer {
467
500
  readonly PROTOCOL: "RTU";
468
501
  readonly ROLE: 'MASTER' | 'SLAVE';
469
502
  private _connection;
470
- private _state;
471
- private _poolSize;
472
- private _threePointFiveT;
473
- private _onePointFiveT;
503
+ private _residual;
504
+ private _residualLen;
505
+ private _expectedLen;
506
+ private _t15Time;
507
+ private _t35Time;
508
+ private _t15Strict;
509
+ private _t15Timer?;
510
+ private _t35Timer?;
511
+ private _t15Marker;
474
512
  private _customFunctionCodes;
475
513
  private _cleanupCbs;
476
514
  get connection(): AbstractPhysicalConnection;
477
515
  constructor(role: 'MASTER' | 'SLAVE', connection: AbstractPhysicalConnection, options?: RtuApplicationLayerOptions);
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;
487
- private flushBuffer;
488
516
  flush(): void;
489
517
  addCustomFunctionCode(cfc: CustomFunctionCode): void;
490
518
  removeCustomFunctionCode(fc: number): void;
@@ -496,12 +524,12 @@ declare class TcpApplicationLayer extends AbstractApplicationLayer {
496
524
  readonly ROLE: 'MASTER' | 'SLAVE';
497
525
  private _connection;
498
526
  private _transactionId;
499
- private _buffer;
527
+ private _residual;
528
+ private _residualLen;
529
+ private _expectedLen;
500
530
  private _cleanupCbs;
501
531
  get connection(): AbstractPhysicalConnection;
502
532
  constructor(role: 'MASTER' | 'SLAVE', connection: AbstractPhysicalConnection);
503
- private tryExtract;
504
- private processFrame;
505
533
  flush(): void;
506
534
  encode(unit: number, fc: number, data: Buffer, transaction?: number): Buffer;
507
535
  }
@@ -547,11 +575,12 @@ interface RtuProtocolOptions {
547
575
  */
548
576
  interCharTimeout?: RtuTimingValue;
549
577
  /**
550
- * Buffer pool size per connection (bytes). Defaults to `MAX_FRAME_LENGTH * 2`
551
- * (512 bytes). Increase this if you expect frames larger than 256 bytes or
552
- * heavy pipelining on a single connection.
578
+ * Enforces strict Modbus RTU timing. When true, any frame containing a t1.5
579
+ * inter-character timeout event will be discarded immediately, even if the
580
+ * CRC16 is valid.
581
+ * @default false
553
582
  */
554
- poolSize?: number;
583
+ strictTiming?: boolean;
555
584
  }
556
585
 
557
586
  interface ReturnValue<T> {
@@ -581,7 +610,7 @@ interface ModbusMasterOptions {
581
610
  opts?: AsciiApplicationLayerOptions;
582
611
  };
583
612
  }
584
- declare class ModbusMaster<T extends ModbusMasterOptions = ModbusMasterOptions> extends EventEmitter<AbstractPhysicalLayerEvents> {
613
+ declare class ModbusMaster<T extends ModbusMasterOptions = ModbusMasterOptions> extends EventEmitter<AbstractPhysicalLayerEvents & ConnectionDebugEvents & ConnectionApplicationEvents> {
585
614
  readonly timeout: number;
586
615
  readonly concurrent: boolean;
587
616
  private _masterSession;
@@ -615,10 +644,10 @@ declare class ModbusMaster<T extends ModbusMasterOptions = ModbusMasterOptions>
615
644
  private writeFC1Or2;
616
645
  writeFC1: this['readCoils'];
617
646
  readCoils(unit: 0, address: number, length: number, timeout?: number): Promise<void>;
618
- readCoils(unit: number, address: number, length: number, timeout?: number): Promise<ReturnValue<boolean[]>>;
647
+ readCoils(unit: number, address: number, length: number, timeout?: number): Promise<ReturnValue<Uint8Array>>;
619
648
  writeFC2: this['readDiscreteInputs'];
620
649
  readDiscreteInputs(unit: 0, address: number, length: number, timeout?: number): Promise<void>;
621
- readDiscreteInputs(unit: number, address: number, length: number, timeout?: number): Promise<ReturnValue<boolean[]>>;
650
+ readDiscreteInputs(unit: number, address: number, length: number, timeout?: number): Promise<ReturnValue<Uint8Array>>;
622
651
  private writeFC3Or4;
623
652
  writeFC3: this['readHoldingRegisters'];
624
653
  readHoldingRegisters(unit: 0, address: number, length: number, timeout?: number): Promise<void>;
@@ -627,14 +656,14 @@ declare class ModbusMaster<T extends ModbusMasterOptions = ModbusMasterOptions>
627
656
  readInputRegisters(unit: 0, address: number, length: number, timeout?: number): Promise<void>;
628
657
  readInputRegisters(unit: number, address: number, length: number, timeout?: number): Promise<ReturnValue<number[]>>;
629
658
  writeFC5: this['writeSingleCoil'];
630
- writeSingleCoil(unit: 0, address: number, value: boolean, timeout?: number): Promise<void>;
631
- writeSingleCoil(unit: number, address: number, value: boolean, timeout?: number): Promise<ReturnValue<boolean>>;
659
+ writeSingleCoil(unit: 0, address: number, value: number, timeout?: number): Promise<void>;
660
+ writeSingleCoil(unit: number, address: number, value: number, timeout?: number): Promise<ReturnValue<number>>;
632
661
  writeFC6: this['writeSingleRegister'];
633
662
  writeSingleRegister(unit: 0, address: number, value: number, timeout?: number): Promise<void>;
634
663
  writeSingleRegister(unit: number, address: number, value: number, timeout?: number): Promise<ReturnValue<number>>;
635
664
  writeFC15: this['writeMultipleCoils'];
636
- writeMultipleCoils(unit: 0, address: number, value: boolean[], timeout?: number): Promise<void>;
637
- writeMultipleCoils(unit: number, address: number, value: boolean[], timeout?: number): Promise<ReturnValue<boolean[]>>;
665
+ writeMultipleCoils(unit: 0, address: number, value: Uint8Array, timeout?: number): Promise<void>;
666
+ writeMultipleCoils(unit: number, address: number, value: Uint8Array, timeout?: number): Promise<ReturnValue<Uint8Array>>;
638
667
  writeFC16: this['writeMultipleRegisters'];
639
668
  writeMultipleRegisters(unit: 0, address: number, value: number[], timeout?: number): Promise<void>;
640
669
  writeMultipleRegisters(unit: number, address: number, value: number[], timeout?: number): Promise<ReturnValue<number[]>>;
@@ -670,7 +699,7 @@ declare class ModbusMaster<T extends ModbusMasterOptions = ModbusMasterOptions>
670
699
  sendCustomFC(unit: 0, fc: number, data: Buffer | number[], timeout?: number): Promise<void>;
671
700
  sendCustomFC(unit: number, fc: number, data: Buffer | number[], timeout?: number): Promise<Buffer>;
672
701
  /**
673
- * Open the underlying physical layer and begin accepting connections.
702
+ * Open the underlying physical layer and establish a connection.
674
703
  *
675
704
  * A `ModbusMaster` instance can only be opened once. Once {@link close}
676
705
  * is called — explicitly or because the physical layer disconnected —
@@ -715,15 +744,17 @@ interface ModbusSlaveModel {
715
744
  * Otherwise keep the default read and write behavior.
716
745
  */
717
746
  interceptor?: MaybeAsyncFunction<(fc: number, data: Buffer) => Buffer | undefined>;
718
- readDiscreteInputs?: MaybeAsyncFunction<(address: number, length: number) => boolean[]>;
719
- readCoils?: MaybeAsyncFunction<(address: number, length: number) => boolean[]>;
720
- writeSingleCoil?: MaybeAsyncFunction<(address: number, value: boolean) => void>;
747
+ readDiscreteInputs?: MaybeAsyncFunction<(address: number, length: number) => Uint8Array>;
748
+ readCoils?: MaybeAsyncFunction<(address: number, length: number) => Uint8Array>;
749
+ writeSingleCoil?: MaybeAsyncFunction<(address: number, value: number) => void>;
721
750
  /**
722
751
  * If omitted, defaults to loop and call `writeSingleCoil`.
723
752
  */
724
- writeMultipleCoils?: MaybeAsyncFunction<(address: number, value: boolean[]) => void>;
725
- readInputRegisters?: MaybeAsyncFunction<(address: number, length: number) => number[]>;
726
- readHoldingRegisters?: MaybeAsyncFunction<(address: number, length: number) => number[]>;
753
+ writeMultipleCoils?: MaybeAsyncFunction<(address: number, value: Uint8Array) => void>;
754
+ /** Return `Uint16Array` to avoid the `Array.from` allocation overhead. */
755
+ readInputRegisters?: MaybeAsyncFunction<(address: number, length: number) => number[] | Uint16Array>;
756
+ /** Return `Uint16Array` to avoid the `Array.from` allocation overhead. */
757
+ readHoldingRegisters?: MaybeAsyncFunction<(address: number, length: number) => number[] | Uint16Array>;
727
758
  writeSingleRegister?: MaybeAsyncFunction<(address: number, value: number) => void>;
728
759
  /**
729
760
  * If omitted, defaults to loop and call `writeSingleRegister`.
@@ -762,7 +793,7 @@ interface ModbusSlaveOptions {
762
793
  opts?: AsciiApplicationLayerOptions;
763
794
  };
764
795
  }
765
- declare class ModbusSlave<T extends ModbusSlaveOptions = ModbusSlaveOptions> extends EventEmitter<AbstractPhysicalLayerEvents> {
796
+ declare class ModbusSlave<T extends ModbusSlaveOptions = ModbusSlaveOptions> extends EventEmitter<AbstractPhysicalLayerEvents & ConnectionDebugEvents & ConnectionApplicationEvents> {
766
797
  readonly models: Map<number, ModbusSlaveModel>;
767
798
  readonly concurrent: boolean;
768
799
  private _physicalLayer;
@@ -833,4 +864,4 @@ declare class ModbusSlave<T extends ModbusSlaveOptions = ModbusSlaveOptions> ext
833
864
  }
834
865
 
835
866
  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 };
836
- export type { AbstractPhysicalLayerEvents, ApplicationDataUnit, AsciiApplicationLayerOptions, Callback$1 as Callback, CustomFunctionCode, DeviceIdentification, MaybeAsyncFunction, ModbusMasterOptions, ModbusSlaveModel, ModbusSlaveOptions, OpenArgs, PhysicalConfig, RtuApplicationLayerOptions, ServerId, TcpServerPhysicalLayerOptions, UdpServerPhysicalLayerOptions };
867
+ export type { AbstractPhysicalLayerEvents, ApplicationDataUnit, AsciiApplicationLayerOptions, Callback$1 as Callback, ConnectionApplicationEvents, ConnectionDebugEvents, CustomFunctionCode, DeviceIdentification, MaybeAsyncFunction, ModbusMasterOptions, ModbusSlaveModel, ModbusSlaveOptions, OpenArgs, PhysicalConfig, RtuApplicationLayerOptions, ServerId, TcpServerPhysicalLayerOptions, UdpServerPhysicalLayerOptions };