From 1350335c75b7b81e191dda6db5a4756e40326028 Mon Sep 17 00:00:00 2001 From: Pablo2048 Date: Thu, 10 Oct 2024 14:48:54 +0200 Subject: [PATCH] Refactoring, correctly handling disconnect & reconnect, use of trace.js --- src/modbus-rtu-master.js | 95 +++++++++++++++++++++++++++------------- 1 file changed, 64 insertions(+), 31 deletions(-) diff --git a/src/modbus-rtu-master.js b/src/modbus-rtu-master.js index 447cc4f..e43d849 100644 --- a/src/modbus-rtu-master.js +++ b/src/modbus-rtu-master.js @@ -9,21 +9,23 @@ class ModbusRTUMaster { this.parity = config.parity || 'none'; this.flowControl = config.flowControl || 'none'; this.timeout = config.timeout || 2000; // Default timeout is 2 seconds + + navigator.serial.addEventListener('disconnect', (event) => { + TRACE_WARNING("mbrtu", 'Port disconnected, attempting to reconnect...'); + this.handleDeviceLost(); + }); + + navigator.serial.addEventListener('connect', (event) => { + TRACE_INFO("mbrtu", 'Device connected:', event); + this.reconnect(); + }); } async connect() { try { this.port = await navigator.serial.requestPort(); - await this.port.open({ - baudRate: this.baudRate, - dataBits: this.dataBits, - stopBits: this.stopBits, - parity: this.parity, - flowControl: this.flowControl - }); - this.reader = this.port.readable.getReader(); - this.writer = this.port.writable.getWriter(); - console.log('Connected to serial port with configuration:', { + await this.openPort(); + TRACE_VERBOSE("mbrtu", 'Connected to serial port with configuration:', { baudRate: this.baudRate, dataBits: this.dataBits, stopBits: this.stopBits, @@ -32,12 +34,24 @@ class ModbusRTUMaster { timeout: this.timeout }); } catch (error) { - console.error('Failed to open serial port:', error); + TRACE_ERROR("mbrtu", 'Failed to open serial port:', error); } } + async openPort() { + await this.port.open({ + baudRate: this.baudRate, + dataBits: this.dataBits, + stopBits: this.stopBits, + parity: this.parity, + flowControl: this.flowControl + }); + this.reader = this.port.readable.getReader(); + this.writer = this.port.writable.getWriter(); + } + async disconnect() { - console.log('Disconnect requested'); + TRACE_ERROR("mbrtu", "Disconnect."); try { if (this.reader) { await this.reader.cancel(); @@ -51,13 +65,42 @@ class ModbusRTUMaster { if (this.port) { await this.port.close(); this.port = null; - console.log('Serial port closed'); + TRACE_INFO("mbrtu", 'Serial port closed'); } } catch (error) { - console.error('Error during disconnect:', error); + TRACE_ERROR("mbrtu", 'Error during disconnect:', error); } } + async handleDeviceLost() { + TRACE_WARNING("mbrtu", 'Attempting to reconnect...'); + await this.disconnect(); + this.reconnect(); + } + + reconnect() { + setTimeout(async () => { + try { + const ports = await navigator.serial.getPorts(); + if (ports.length > 0) { + this.port = ports[0]; + if (!this.port.readable && !this.port.writable) { + await this.openPort(); + TRACE_INFO("mbrtu", 'Reconnected to serial port.'); + } else { + TRACE_WARNING("mbrtu", 'Port is already open. Skipping open operation.'); + } + } else { + TRACE_WARNING("mbrtu", 'Port not found, retrying...'); + this.reconnect(); + } + } catch (error) { + TRACE_ERROR("mbrtu", 'Reconnect failed:', error); + this.reconnect(); + } + }, 1000); // Retry every 1 second + } + async readHoldingRegisters(slaveId, startAddress, quantity) { const response = await this.readRegisters(slaveId, 0x03, startAddress, quantity); return this.parseRegisterValues(response, quantity); @@ -85,6 +128,7 @@ class ModbusRTUMaster { } const request = this.buildRequest(slaveId, functionCode, startAddress, quantity); + TRACE_VERBOSE("mbrtu", "Send request"); await this.sendRequest(request); return await this.receiveResponse(slaveId, functionCode, quantity); } @@ -121,12 +165,10 @@ class ModbusRTUMaster { async sendRequest(request) { await this.writer.write(request); - //console.log('Request sent:', request); + TRACE_VERBOSE("mbrtu", 'Request sent:', request); } - // Receiving and validating response with timeout detection and Modbus Exception Codes processing async receiveResponse(slaveId, functionCode, quantity) { - // Set the expected length of the response (slaveId, functionCode, byte count, data, CRC) let expectedLength = 5 + quantity * 2; let response = new Uint8Array(expectedLength); let index = 0; @@ -140,9 +182,8 @@ class ModbusRTUMaster { response.set(value, index); index += value.length; - // If an exception is detected (highest bit of the function code), set the length to 5 bytes (slaveId, functionCode, exceptionCode, CRC) if (index >= 2 && (response[1] & 0x80)) { - expectedLength = 5; // Override expected length in case of an exception + expectedLength = 5; } } })(), @@ -151,25 +192,23 @@ class ModbusRTUMaster { ) ]); - // Modbus Exception Codes processing - if (response[1] & 0x80) { // Check if the highest bit of the function code is set + if (response[1] & 0x80) { const exceptionCode = response[2]; throw new Error(`Modbus Exception Code: ${this.getExceptionMessage(exceptionCode)} (Code: ${exceptionCode})`); } - // Check CRC after receiving the full response const dataWithoutCRC = response.slice(0, index - 2); const receivedCRC = (response[index - 1] << 8) | response[index - 2]; const calculatedCRC = this.calculateCRC(dataWithoutCRC); if (calculatedCRC === receivedCRC) { - //console.log('Received response with valid CRC:', response.slice(0, index)); + TRACE_VERBOSE("mbrtu", 'Received response with valid CRC:', response.slice(0, index)); return response.slice(0, index); } else { throw new Error(`CRC Error: Calculated CRC ${calculatedCRC} does not match received CRC ${receivedCRC}.`); } } catch (error) { - console.error('Error receiving response:', error.message); + TRACE_ERROR("mbrtu", 'Error receiving response:', error.message); if (this.reader) { await this.reader.cancel(); await this.reader.releaseLock(); @@ -179,16 +218,10 @@ class ModbusRTUMaster { if (error.message.includes('Device has been lost')) { await this.handleDeviceLost(); } - return { error: error.message }; // TODO: or throw error; ? + throw error; } } - async handleDeviceLost() { - console.warn('Attempting to reconnect...'); - await this.disconnect(); - await this.connect(); - } - getExceptionMessage(code) { const exceptionMessages = { 1: 'Illegal Function',