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/dist/index.d.ts CHANGED
@@ -15,7 +15,8 @@ interface ServerId {
15
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,29 +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
- * @param end Byte offset one past the last valid byte. Bounds checks must use
56
- * `end - start` as available bytes, NOT `buffer.length` (pool capacity).
57
- * @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.
58
58
  */
59
- predictRequestLength: (buffer: Buffer, start: number, end: number) => number | null;
59
+ predictRequestLength: (getByte: (idx: number) => number, length: number) => number;
60
60
  /**
61
61
  * Predict total RTU frame length for an incoming response (master-side framing).
62
62
  *
63
- * @param bufferThe raw frame buffer (a shared pool or incoming chunk). Do NOT
64
- * modify it; the framing layer owns the memory.
65
- * @param startByte offset in `buffer` where the current candidate frame begins
66
- * (the unit ID byte). Read the function code at `buffer[start + 1]` and derive
67
- * the total frame length from the trailing bytes.
68
- * @param end Byte offset one past the last valid byte. Bounds checks must use
69
- * `end - start` as available bytes, NOT `buffer.length` (pool capacity).
70
- * @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.
71
69
  */
72
- predictResponseLength: (buffer: Buffer, start: number, end: number) => number | null;
70
+ predictResponseLength: (getByte: (idx: number) => number, length: number) => number;
73
71
  /**
74
72
  * Slave-side handler. Receives PDU payload (bytes after `fc`, before CRC) and
75
73
  * the unit ID being addressed; must return the PDU payload of the response.
@@ -318,6 +316,18 @@ declare class UdpServerPhysicalLayer extends AbstractPhysicalLayer {
318
316
  interface AbstractPhysicalConnectionEvents {
319
317
  data: [data: Buffer];
320
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];
321
331
  }
322
332
  /**
323
333
  * A one-way data transmission channel.
@@ -337,6 +347,28 @@ interface AbstractPhysicalLayerEvents {
337
347
  close: [];
338
348
  error: [error: Error];
339
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
+ }
340
372
  /**
341
373
  * An abstraction over local hardware or network resources.
342
374
  *
@@ -442,7 +474,6 @@ interface AsciiApplicationLayerOptions {
442
474
  declare class AsciiApplicationLayer extends AbstractApplicationLayer {
443
475
  readonly PROTOCOL: "ASCII";
444
476
  readonly ROLE: 'MASTER' | 'SLAVE';
445
- readonly lenientHex: boolean;
446
477
  private _connection;
447
478
  private _state;
448
479
  private _cleanupCbs;
@@ -459,34 +490,29 @@ interface RtuApplicationLayerOptions {
459
490
  /** Inter-character timeout in milliseconds (Modbus RTU t1.5). Opt-in. */
460
491
  interCharTimeout?: number;
461
492
  /**
462
- * Buffer pool size per connection (bytes). Defaults to `MAX_FRAME_LENGTH * 2`
463
- * (512 bytes). Increase this if you expect frames larger than 256 bytes or
464
- * 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
465
496
  */
466
- poolSize?: number;
497
+ strictTiming?: boolean;
467
498
  }
468
499
  declare class RtuApplicationLayer extends AbstractApplicationLayer {
469
500
  readonly PROTOCOL: "RTU";
470
501
  readonly ROLE: 'MASTER' | 'SLAVE';
471
502
  private _connection;
472
- private _state;
473
- private _poolSize;
474
- private _threePointFiveT;
475
- 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;
476
512
  private _customFunctionCodes;
477
513
  private _cleanupCbs;
478
514
  get connection(): AbstractPhysicalConnection;
479
515
  constructor(role: 'MASTER' | 'SLAVE', connection: AbstractPhysicalConnection, options?: RtuApplicationLayerOptions);
480
- private clearStateTimers;
481
- /**
482
- * Shared handler for every "frame is not yet complete" exit in `flushBuffer`.
483
- * Returns `true` when the caller should `return` (strict reset), `false` to
484
- * `break` the parse loop. Hot path never reaches here — only error/incomplete
485
- * edges. Extracted as a method so it is not recreated on every `flushBuffer`
486
- * call.
487
- */
488
- private _handleIncomplete;
489
- private flushBuffer;
490
516
  flush(): void;
491
517
  addCustomFunctionCode(cfc: CustomFunctionCode): void;
492
518
  removeCustomFunctionCode(fc: number): void;
@@ -498,12 +524,12 @@ declare class TcpApplicationLayer extends AbstractApplicationLayer {
498
524
  readonly ROLE: 'MASTER' | 'SLAVE';
499
525
  private _connection;
500
526
  private _transactionId;
501
- private _buffer;
527
+ private _residual;
528
+ private _residualLen;
529
+ private _expectedLen;
502
530
  private _cleanupCbs;
503
531
  get connection(): AbstractPhysicalConnection;
504
532
  constructor(role: 'MASTER' | 'SLAVE', connection: AbstractPhysicalConnection);
505
- private tryExtract;
506
- private processFrame;
507
533
  flush(): void;
508
534
  encode(unit: number, fc: number, data: Buffer, transaction?: number): Buffer;
509
535
  }
@@ -549,11 +575,12 @@ interface RtuProtocolOptions {
549
575
  */
550
576
  interCharTimeout?: RtuTimingValue;
551
577
  /**
552
- * Buffer pool size per connection (bytes). Defaults to `MAX_FRAME_LENGTH * 2`
553
- * (512 bytes). Increase this if you expect frames larger than 256 bytes or
554
- * 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
555
582
  */
556
- poolSize?: number;
583
+ strictTiming?: boolean;
557
584
  }
558
585
 
559
586
  interface ReturnValue<T> {
@@ -583,7 +610,7 @@ interface ModbusMasterOptions {
583
610
  opts?: AsciiApplicationLayerOptions;
584
611
  };
585
612
  }
586
- declare class ModbusMaster<T extends ModbusMasterOptions = ModbusMasterOptions> extends EventEmitter<AbstractPhysicalLayerEvents> {
613
+ declare class ModbusMaster<T extends ModbusMasterOptions = ModbusMasterOptions> extends EventEmitter<AbstractPhysicalLayerEvents & ConnectionDebugEvents & ConnectionApplicationEvents> {
587
614
  readonly timeout: number;
588
615
  readonly concurrent: boolean;
589
616
  private _masterSession;
@@ -617,10 +644,10 @@ declare class ModbusMaster<T extends ModbusMasterOptions = ModbusMasterOptions>
617
644
  private writeFC1Or2;
618
645
  writeFC1: this['readCoils'];
619
646
  readCoils(unit: 0, address: number, length: number, timeout?: number): Promise<void>;
620
- 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>>;
621
648
  writeFC2: this['readDiscreteInputs'];
622
649
  readDiscreteInputs(unit: 0, address: number, length: number, timeout?: number): Promise<void>;
623
- 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>>;
624
651
  private writeFC3Or4;
625
652
  writeFC3: this['readHoldingRegisters'];
626
653
  readHoldingRegisters(unit: 0, address: number, length: number, timeout?: number): Promise<void>;
@@ -629,14 +656,14 @@ declare class ModbusMaster<T extends ModbusMasterOptions = ModbusMasterOptions>
629
656
  readInputRegisters(unit: 0, address: number, length: number, timeout?: number): Promise<void>;
630
657
  readInputRegisters(unit: number, address: number, length: number, timeout?: number): Promise<ReturnValue<number[]>>;
631
658
  writeFC5: this['writeSingleCoil'];
632
- writeSingleCoil(unit: 0, address: number, value: boolean, timeout?: number): Promise<void>;
633
- 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>>;
634
661
  writeFC6: this['writeSingleRegister'];
635
662
  writeSingleRegister(unit: 0, address: number, value: number, timeout?: number): Promise<void>;
636
663
  writeSingleRegister(unit: number, address: number, value: number, timeout?: number): Promise<ReturnValue<number>>;
637
664
  writeFC15: this['writeMultipleCoils'];
638
- writeMultipleCoils(unit: 0, address: number, value: boolean[], timeout?: number): Promise<void>;
639
- 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>>;
640
667
  writeFC16: this['writeMultipleRegisters'];
641
668
  writeMultipleRegisters(unit: 0, address: number, value: number[], timeout?: number): Promise<void>;
642
669
  writeMultipleRegisters(unit: number, address: number, value: number[], timeout?: number): Promise<ReturnValue<number[]>>;
@@ -717,15 +744,13 @@ interface ModbusSlaveModel {
717
744
  * Otherwise keep the default read and write behavior.
718
745
  */
719
746
  interceptor?: MaybeAsyncFunction<(fc: number, data: Buffer) => Buffer | undefined>;
720
- /** Return `Uint8Array` to avoid the `Array.from` allocation overhead. */
721
- readDiscreteInputs?: MaybeAsyncFunction<(address: number, length: number) => boolean[] | Uint8Array>;
722
- /** Return `Uint8Array` to avoid the `Array.from` allocation overhead. */
723
- readCoils?: MaybeAsyncFunction<(address: number, length: number) => boolean[] | Uint8Array>;
724
- 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>;
725
750
  /**
726
751
  * If omitted, defaults to loop and call `writeSingleCoil`.
727
752
  */
728
- writeMultipleCoils?: MaybeAsyncFunction<(address: number, value: boolean[]) => void>;
753
+ writeMultipleCoils?: MaybeAsyncFunction<(address: number, value: Uint8Array) => void>;
729
754
  /** Return `Uint16Array` to avoid the `Array.from` allocation overhead. */
730
755
  readInputRegisters?: MaybeAsyncFunction<(address: number, length: number) => number[] | Uint16Array>;
731
756
  /** Return `Uint16Array` to avoid the `Array.from` allocation overhead. */
@@ -768,7 +793,7 @@ interface ModbusSlaveOptions {
768
793
  opts?: AsciiApplicationLayerOptions;
769
794
  };
770
795
  }
771
- declare class ModbusSlave<T extends ModbusSlaveOptions = ModbusSlaveOptions> extends EventEmitter<AbstractPhysicalLayerEvents> {
796
+ declare class ModbusSlave<T extends ModbusSlaveOptions = ModbusSlaveOptions> extends EventEmitter<AbstractPhysicalLayerEvents & ConnectionDebugEvents & ConnectionApplicationEvents> {
772
797
  readonly models: Map<number, ModbusSlaveModel>;
773
798
  readonly concurrent: boolean;
774
799
  private _physicalLayer;
@@ -839,4 +864,4 @@ declare class ModbusSlave<T extends ModbusSlaveOptions = ModbusSlaveOptions> ext
839
864
  }
840
865
 
841
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 };
842
- 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 };