import { Logger } from 'pino'; import { ITransport, INodeSerialTransportOptions, EConnectionErrorType, TDeviceStateHandler, TPortStateHandler, TRSMode } from '../../types/public.js'; import { TrafficSniffer } from '../trackers/traffic-sniffer.js'; /** * NodeSerialTransport implements the ITransport interface using the 'serialport' library. * It provides reliable serial communication (RS232/RS485) with support for: * - Automatic reconnection with configurable attempts and intervals * - Read/write operations with timeouts and mutex protection * - Buffer management and overflow protection * - Device and port state tracking via callbacks * - Non-invasive traffic sniffing and real-time protocol analysis */ export default class NodeSerialTransport implements ITransport { isOpen: boolean; logger: Logger; private path; private options; private port; private _sniffer; private _waitingForResponse; private _readBuffer; private _readBufferHead; private _readBufferTail; private _readBufferCount; private _reconnectAttempts; private _shouldReconnect; private _reconnectTimeout; private _isConnecting; private _isDisconnecting; private _isFlushing; private _pendingFlushPromises; private _operationMutex; private _connectionPromise; private _resolveConnection; private _rejectConnection; private _connectedSlaveIds; private _deviceStateHandler; private _portStateHandler; private _wasEverConnected; /** * Creates a new NodeSerialTransport instance. * @param portPath - Path to the serial port (e.g. '/dev/ttyUSB0' or 'COM3') * @param options - Configuration options for baud rate, timeouts, reconnection, etc. */ constructor(portPath: string, options?: INodeSerialTransportOptions); /** * Attaches a TrafficSniffer instance to monitor and analyze raw serial traffic. * This allows for sub-millisecond latency tracking and real-time protocol inspection. * @param sniffer - The TrafficSniffer instance to use for monitoring. */ setSniffer(sniffer: TrafficSniffer): void; /** * Opens the serial port and establishes the connection. * Handles reconnection logic, resource cleanup, and port state notifications. * If connection fails and reconnection is enabled, it will schedule automatic retries. * @throws NodeSerialConnectionError if connection fails and max attempts are reached */ connect(): Promise; /** * Creates and opens the SerialPort instance. * Sets up event listeners for data, error, and close events. */ private _createAndOpenPort; /** * Handles incoming data from the serial port. * Appends data to the internal read buffer with overflow protection. */ private _onData; /** * Handles serial port error events and maps them to appropriate Modbus errors. */ private _onError; /** * Handles the 'close' event of the serial port. */ private _onClose; /** * Schedules a reconnection attempt after a delay. */ private _scheduleReconnect; /** * Attempts to reconnect to the serial port. */ private _attemptReconnect; /** * Flushes the internal read buffer, discarding all pending data. * Useful before sending a new request in half-duplex (RS485) mode. */ flush(): Promise; /** * Writes data to the serial port. * Uses mutex to ensure exclusive access and includes drain to guarantee data is sent. * @param buffer - Data to send * @throws NodeSerialWriteError if write or drain fails */ write(buffer: Uint8Array): Promise; /** * Reads a specified number of bytes from the internal buffer. * Polls the buffer at regular intervals until data is available or timeout occurs. * @param length - Number of bytes to read * @param timeout - Maximum time to wait for data * @returns Uint8Array containing the requested data * @throws ModbusTimeoutError, NodeSerialReadError, ModbusFlushError, etc. */ read(length: number, timeout?: number): Promise; /** * Gracefully disconnects the serial port and stops reconnection attempts. */ disconnect(): Promise; /** * Immediately destroys the transport, releases all resources and stops reconnection. */ destroy(): void; /** * Centralized error handler that triggers connection loss logic. */ private _handleError; /** * Handles connection loss by updating state and notifying listeners. */ private _handleConnectionLoss; /** * Returns the current RS mode (RS485 or RS232). */ getRSMode(): TRSMode; /** * Sets the handler for device connection state changes (per slave ID). */ setDeviceStateHandler(handler: TDeviceStateHandler): void; /** * Sets the handler for port-level connection state changes. */ setPortStateHandler(handler: TPortStateHandler): void; /** * Disables device tracking (clears the device state handler). */ disableDeviceTracking(): Promise; /** * Enables device tracking and optionally sets a new handler. */ enableDeviceTracking(handler?: TDeviceStateHandler): Promise; /** * Notifies that a specific slave/device has become connected. */ notifyDeviceConnected(slaveId: number): void; /** * Notifies that a specific slave/device has disconnected with error details. */ notifyDeviceDisconnected(slaveId: number, errorType: EConnectionErrorType, errorMessage: string): void; /** * Manually removes a device from the connected set. */ removeConnectedDevice(slaveId: number): void; /** * Notifies listeners that the port has successfully connected. */ private _notifyPortConnected; /** * Notifies listeners that the port has disconnected with reason. */ private _notifyPortDisconnected; /** * Releases all resources: removes listeners, closes the port, clears buffers and connected devices. */ private _releaseAllResources; /** * Removes all event listeners from the SerialPort instance. */ private _removeAllListeners; }