import { Logger } from 'pino'; import { IInfinityChangeParams, IStopInfinityChangeParams, IRegisterDefinitions, IModbusSlaveCoreEmulator } from '../../types/public.js'; /** * Core Modbus Slave Emulator. * * This class implements the core logic of a Modbus RTU/TCP slave device. * It maintains four memory areas (Coils, Discrete Inputs, Holding Registers, Input Registers) * and supports all standard Modbus function codes for reading and writing. * * It also provides advanced features such as: * - Configurable exceptions per function code and address * - Infinite random value changing for simulation purposes * - Comprehensive validation and error handling according to Modbus specification */ declare class ModbusSlaveCore implements IModbusSlaveCoreEmulator { readonly slaveId: number; private coils; private discreteInputs; private holdingRegisters; private inputRegisters; private exceptions; private _infinityTasks; readonly logger: Logger; private readonly loggerEnabled; /** * Creates a new Modbus Slave Core instance. * * @param slaveId - Modbus slave address (Unit ID). Must be an integer between 0 and 247 (default: 1). * @param options - Configuration options * @param options.loggerEnabled - Whether to enable logging (default: false). Note: logger is always created, but output depends on this flag and environment. * @throws {ModbusInvalidAddressError} If slaveId is invalid. */ constructor(slaveId?: number, options?: { loggerEnabled?: boolean; }); /** * Central method for processing incoming Modbus PDU. * This is the main entry point used by transport layers (RTU, TCP, etc.). * @param unitId - Received Unit ID (slave address) * @param pdu - Modbus Protocol Data Unit (function code + data) * @returns Response PDU (normal response or exception response) * @throws {Error} If unitId does not match this slave (unless unitId === 0 for broadcast) */ processRequest(unitId: number, pdu: Uint8Array): Promise; /** * Handles Modbus function code 0x01 - Read Coils. * @private */ private handleReadCoils; /** * Handles Modbus function code 0x02 - Read Discrete Inputs. * @private */ private handleReadDiscreteInputs; /** * Handles Modbus function code 0x03 - Read Holding Registers. * @private */ private handleReadHoldingRegisters; /** * Handles Modbus function code 0x04 - Read Input Registers. * @private */ private handleReadInputRegisters; /** * Handles Modbus function code 0x05 - Write Single Coil. * @private */ private handleWriteSingleCoil; /** * Handles Modbus function code 0x06 - Write Single Register. * @private */ private handleWriteSingleRegister; /** * Handles Modbus function code 0x0F - Write Multiple Coils. * @private */ private handleWriteMultipleCoils; /** * Handles Modbus function code 0x10 - Write Multiple Registers. * @private */ private handleWriteMultipleRegisters; /** * Reads multiple coils (function code 0x01). * * @param startAddress - Starting address of the coils to read (0..65535) * @param quantity - Number of coils to read (1..2000) * @returns Array of boolean values representing coil states * @throws {ModbusInvalidAddressError} If address is invalid * @throws {ModbusInvalidQuantityError} If quantity is out of valid range * @throws {ModbusIllegalDataAddressError} If address range exceeds 0xFFFF * @throws {ModbusExceptionError} If an exception was configured for any address in the range */ readCoils(startAddress: number, quantity: number): boolean[]; /** * Reads multiple discrete inputs (function code 0x02). * @param startAddress - Starting address of the discrete inputs (0..65535) * @param quantity - Number of inputs to read (1..2000) * @returns Array of boolean values * @throws Same exceptions as {@link readCoils} */ readDiscreteInputs(startAddress: number, quantity: number): boolean[]; /** * Reads multiple holding registers (function code 0x03). * @param startAddress - Starting address (0..65535) * @param quantity - Number of registers to read (1..125) * @returns Array of 16-bit unsigned integer values (0..65535) * @throws Same exceptions as {@link readCoils} */ readHoldingRegisters(startAddress: number, quantity: number): number[]; /** * Reads multiple input registers (function code 0x04). * @param startAddress - Starting address (0..65535) * @param quantity - Number of registers to read (1..125) * @returns Array of 16-bit unsigned integer values * @throws Same exceptions as {@link readCoils} */ readInputRegisters(startAddress: number, quantity: number): number[]; /** * Writes a single coil (function code 0x05). * @param address - Coil address (0..65535) * @param value - New coil value (true = ON, false = OFF) * @throws {ModbusInvalidAddressError} * @throws {ModbusIllegalDataValueError} If value is not boolean * @throws {ModbusExceptionError} If exception is configured for this address */ writeSingleCoil(address: number, value: boolean): void; /** * Writes a single holding register (function code 0x06). * @param address - Register address (0..65535) * @param value - New register value (0..65535) * @throws {ModbusInvalidAddressError} * @throws {ModbusIllegalDataValueError} If value is not a valid 16-bit integer * @throws {ModbusExceptionError} */ writeSingleRegister(address: number, value: number): void; /** * Bulk adds initial values for multiple registers and coils. * @param definitions - Object containing arrays of {start, value} for each memory type * @throws {ModbusDataConversionError} If definitions format is invalid */ addRegisters(definitions: IRegisterDefinitions): void; /** * Starts an infinite random value change task for a specific register/coil. * Useful for simulating changing sensor values or dynamic data. * @param params - Parameters for the infinite change task * @throws {ModbusDataConversionError} If parameters are invalid */ infinityChange(params: IInfinityChangeParams): void; /** * Stops an active infinite change task for a specific register. * @param params - Parameters identifying the task to stop */ stopInfinityChange(params: IStopInfinityChangeParams): void; /** * Configures a custom Modbus exception for a specific function code and address. * @param functionCode - Modbus function code * @param address - Address to trigger the exception on * @param exceptionCode - Exception code to return (1..4 typically) * @throws {ModbusInvalidAddressError} If address is invalid */ setException(functionCode: number, address: number, exceptionCode: number): void; /** * Clears all registers, configured exceptions, and stops all infinity change tasks. */ clearAll(): void; /** * Validates that the address is a valid 16-bit unsigned integer (0..0xFFFF). * @private * @throws {ModbusInvalidAddressError} */ private _validateAddress; /** * Validates quantity according to Modbus specification limits for the given function. * @private * @throws {ModbusInvalidQuantityError} */ private _validateQuantity; /** * Validates the value being written to a coil or register. * @private * @throws {ModbusIllegalDataValueError} */ private _validateValue; /** * Checks if an exception is configured for the given function code and address. * If yes — throws the corresponding ModbusExceptionError. * @private */ private _checkException; /** * Internal method to set a coil value. * @private */ private _setCoil; /** * Internal method to get a coil value. * @private */ private _getCoil; /** * Internal method to set a discrete input value. * @private */ private _setDiscreteInput; /** * Internal method to get a discrete input value. * @private */ private _getDiscreteInput; /** * Internal method to set a holding register value. * @private */ private _setHoldingRegister; /** * Internal method to get a holding register value. * @private */ private _getHoldingRegister; /** * Internal method to set an input register value. * @private */ private _setInputRegister; /** * Internal method to get an input register value. * @private */ private _getInputRegister; } export = ModbusSlaveCore;