mirror of
				https://github.com/Pablo2048/modbus-rtu-master.git
				synced 2025-10-31 00:12:42 +01:00 
			
		
		
		
	Refactoring, correctly handling disconnect & reconnect, use of trace.js
This commit is contained in:
		| @@ -9,21 +9,23 @@ class ModbusRTUMaster { | |||||||
|         this.parity = config.parity || 'none'; |         this.parity = config.parity || 'none'; | ||||||
|         this.flowControl = config.flowControl || 'none'; |         this.flowControl = config.flowControl || 'none'; | ||||||
|         this.timeout = config.timeout || 2000; // Default timeout is 2 seconds |         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() { |     async connect() { | ||||||
|         try { |         try { | ||||||
|             this.port = await navigator.serial.requestPort(); |             this.port = await navigator.serial.requestPort(); | ||||||
|             await this.port.open({ |             await this.openPort(); | ||||||
|                 baudRate: this.baudRate, |             TRACE_VERBOSE("mbrtu", 'Connected to serial port with configuration:', { | ||||||
|                 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:', { |  | ||||||
|                 baudRate: this.baudRate, |                 baudRate: this.baudRate, | ||||||
|                 dataBits: this.dataBits, |                 dataBits: this.dataBits, | ||||||
|                 stopBits: this.stopBits, |                 stopBits: this.stopBits, | ||||||
| @@ -32,12 +34,24 @@ class ModbusRTUMaster { | |||||||
|                 timeout: this.timeout |                 timeout: this.timeout | ||||||
|             }); |             }); | ||||||
|         } catch (error) { |         } 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() { |     async disconnect() { | ||||||
|         console.log('Disconnect requested'); |         TRACE_ERROR("mbrtu", "Disconnect."); | ||||||
|         try { |         try { | ||||||
|             if (this.reader) { |             if (this.reader) { | ||||||
|                 await this.reader.cancel(); |                 await this.reader.cancel(); | ||||||
| @@ -51,13 +65,42 @@ class ModbusRTUMaster { | |||||||
|             if (this.port) { |             if (this.port) { | ||||||
|                 await this.port.close(); |                 await this.port.close(); | ||||||
|                 this.port = null; |                 this.port = null; | ||||||
|                 console.log('Serial port closed'); |                 TRACE_INFO("mbrtu", 'Serial port closed'); | ||||||
|             } |             } | ||||||
|         } catch (error) { |         } 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) { |     async readHoldingRegisters(slaveId, startAddress, quantity) { | ||||||
|         const response = await this.readRegisters(slaveId, 0x03, startAddress, quantity); |         const response = await this.readRegisters(slaveId, 0x03, startAddress, quantity); | ||||||
|         return this.parseRegisterValues(response, quantity); |         return this.parseRegisterValues(response, quantity); | ||||||
| @@ -85,6 +128,7 @@ class ModbusRTUMaster { | |||||||
|         } |         } | ||||||
|  |  | ||||||
|         const request = this.buildRequest(slaveId, functionCode, startAddress, quantity); |         const request = this.buildRequest(slaveId, functionCode, startAddress, quantity); | ||||||
|  |         TRACE_VERBOSE("mbrtu", "Send request"); | ||||||
|         await this.sendRequest(request); |         await this.sendRequest(request); | ||||||
|         return await this.receiveResponse(slaveId, functionCode, quantity); |         return await this.receiveResponse(slaveId, functionCode, quantity); | ||||||
|     } |     } | ||||||
| @@ -121,12 +165,10 @@ class ModbusRTUMaster { | |||||||
|  |  | ||||||
|     async sendRequest(request) { |     async sendRequest(request) { | ||||||
|         await this.writer.write(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) { |     async receiveResponse(slaveId, functionCode, quantity) { | ||||||
|         // Set the expected length of the response (slaveId, functionCode, byte count, data, CRC) |  | ||||||
|         let expectedLength = 5 + quantity * 2; |         let expectedLength = 5 + quantity * 2; | ||||||
|         let response = new Uint8Array(expectedLength); |         let response = new Uint8Array(expectedLength); | ||||||
|         let index = 0; |         let index = 0; | ||||||
| @@ -140,9 +182,8 @@ class ModbusRTUMaster { | |||||||
|                         response.set(value, index); |                         response.set(value, index); | ||||||
|                         index += value.length; |                         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)) { |                         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) { | ||||||
|             if (response[1] & 0x80) { // Check if the highest bit of the function code is set |  | ||||||
|                 const exceptionCode = response[2]; |                 const exceptionCode = response[2]; | ||||||
|                 throw new Error(`Modbus Exception Code: ${this.getExceptionMessage(exceptionCode)} (Code: ${exceptionCode})`); |                 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 dataWithoutCRC = response.slice(0, index - 2); | ||||||
|             const receivedCRC = (response[index - 1] << 8) | response[index - 2]; |             const receivedCRC = (response[index - 1] << 8) | response[index - 2]; | ||||||
|             const calculatedCRC = this.calculateCRC(dataWithoutCRC); |             const calculatedCRC = this.calculateCRC(dataWithoutCRC); | ||||||
|  |  | ||||||
|             if (calculatedCRC === receivedCRC) { |             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); |                 return response.slice(0, index); | ||||||
|             } else { |             } else { | ||||||
|                 throw new Error(`CRC Error: Calculated CRC ${calculatedCRC} does not match received CRC ${receivedCRC}.`); |                 throw new Error(`CRC Error: Calculated CRC ${calculatedCRC} does not match received CRC ${receivedCRC}.`); | ||||||
|             } |             } | ||||||
|         } catch (error) { |         } catch (error) { | ||||||
|             console.error('Error receiving response:', error.message); |             TRACE_ERROR("mbrtu", 'Error receiving response:', error.message); | ||||||
|             if (this.reader) { |             if (this.reader) { | ||||||
|                 await this.reader.cancel(); |                 await this.reader.cancel(); | ||||||
|                 await this.reader.releaseLock(); |                 await this.reader.releaseLock(); | ||||||
| @@ -179,16 +218,10 @@ class ModbusRTUMaster { | |||||||
|             if (error.message.includes('Device has been lost')) { |             if (error.message.includes('Device has been lost')) { | ||||||
|                 await this.handleDeviceLost(); |                 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) { |     getExceptionMessage(code) { | ||||||
|         const exceptionMessages = { |         const exceptionMessages = { | ||||||
|             1: 'Illegal Function', |             1: 'Illegal Function', | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user