UNPKG

modbus-connect

Version:

Modbus RTU over Web Serial and Node.js SerialPort

132 lines (117 loc) 3.64 kB
// transport/web-serialport.js const logger = require("../logger.js"); class WebSerialTransport { constructor(port, options = {}) { this.port = port; this.options = { baudRate: 9600, dataBits: 8, stopBits: 1, parity: 'none', readTimeout: 1000, writeTimeout: 1000, ...options }; this.reader = null; this.writer = null; this.readBuffer = new Uint8Array(0); } async connect() { try { await this.port.open({ baudRate: this.options.baudRate, dataBits: this.options.dataBits, stopBits: this.options.stopBits, parity: this.options.parity, flowControl: 'none', }); const readable = this.port.readable; const writable = this.port.writable; if (!readable || !writable) { logger.error('WebSerial port not readable/writable'); throw new Error('Serial port not readable/writable'); } this.reader = readable.getReader(); this.writer = writable.getWriter(); this._startReading(); logger.info('WebSerial port opened'); } catch (err) { logger.error(`Failed to open WebSerial port: ${err.message}`); throw err; } } _startReading() { const loop = async () => { try { while (true) { const { value, done } = await this.reader.read(); if (done) break; if (value) { const newBuffer = new Uint8Array(this.readBuffer.length + value.length); newBuffer.set(this.readBuffer, 0); newBuffer.set(value, this.readBuffer.length); this.readBuffer = newBuffer; } } } catch (err) { logger.warn(`Read loop error: ${err.message}`); } }; loop(); } async write(buffer) { const timeout = this.options.writeTimeout; const abort = new AbortController(); const timer = setTimeout(() => abort.abort(), timeout); try { await this.writer.write(buffer); logger.debug(`Wrote ${buffer.length} bytes to WebSerial port`); } catch (err) { logger.warn(`Write timeout on WebSerial port`); throw new Error('Write timeout'); } finally { clearTimeout(timer); } } async read(length, timeout = this.options.readTimeout) { const start = Date.now(); return new Promise((resolve, reject) => { const check = () => { if (this.readBuffer.length >= length) { const data = this.readBuffer.slice(0, length); this.readBuffer = this.readBuffer.slice(length); logger.debug(`Read ${length} bytes from WebSerial port`); return resolve(data); } if (Date.now() - start > timeout) { logger.warn(`Read timeout on WebSerial port`); return reject(new Error('Read timeout')); } setTimeout(check, 10); }; check(); }); } async disconnect() { try { if (this.reader) { try { await this.reader.cancel(); } catch (err) { logger.warn(`Error when cancel(): ${err.message}`); } this.reader.releaseLock(); this.reader = null; } if (this.writer) { this.writer.releaseLock(); this.writer = null; } await this.port.close(); logger.info('Disconnected from WebSerial port'); } catch (err) { logger.error(`Shutdown error on WebSerial: ${err.message}`); } } } module.exports = { WebSerialTransport }