import { exec } from 'node:child_process'; import _ from 'lodash'; import EventsBase from './events-base.js'; import { CURRENT_PLANTFORM, SERVER_MODE } from './config.js'; let SerialPort, ReadlineParser, ByteLengthParser; if (SERVER_MODE === 'all') { const serial = await import('serialport'); SerialPort = serial.SerialPort; ReadlineParser = serial.ReadlineParser; ByteLengthParser = serial.ByteLengthParser; } export default class Serial extends EventsBase { static { this.portsName = []; this.getCurrentPortsName = function () { return this.portsName; } this.getPorts = async function () { return new Promise((resolve, reject) => { if (CURRENT_PLANTFORM === 'linux') { exec('ls /dev/ttyACM* /dev/tty*USB*', (error, stdout) => { let portsName = _.uniq(stdout.split('\n')); let newPorts = []; for (let i = 0; i < portsName.length; i++) { if (!portsName[i]) { continue; } newPorts.push({ vendorId: null, productId: null, name: portsName[i] }); } resolve(newPorts); }); } else { SerialPort.list().then(ports => { let newPorts = []; for (let i = 0; i < ports.length; i++) { let port = ports[i]; newPorts.push({ vendorId: port.vendorId, productId: port.productId, name: port.path }); } resolve(newPorts); }).catch(reject); } }); } } #serialport_ = null; #parserBytes_ = null; #parserLine_ = null; #port_ = null; #isOpened_ = false; constructor(port) { super(); this.#port_ = port; this.addEventsType(['buffer', 'string', 'error', 'open', 'close']); } #addEventsListener_() { this.#parserBytes_.on('data', (buffer) => { const arr = []; for (let i = 0; i < buffer.length; i++) { arr.push(buffer[i]); } this.runEvent('buffer', arr); }); this.#parserLine_.on('data', (str) => { this.runEvent('string', str); }); this.#serialport_.on('error', (error) => { this.runEvent('error', error.toString()); }); this.#serialport_.on('open', () => { this.#isOpened_ = true; this.runEvent('open'); }); this.#serialport_.on('close', () => { this.#isOpened_ = false; this.runEvent('close'); }); } isOpened() { return this.#isOpened_; } getPortName() { return this.#port_; } async open(baud) { return new Promise((resolve, reject) => { if (this.isOpened()) { resolve(); return; } this.#serialport_ = new SerialPort({ path: this.getPortName(), baudRate: baud, dataBits: 8, parity: 'none', stopBits: 1, flowControl: false, autoOpen: false }, false); this.#parserBytes_ = this.#serialport_.pipe(new ByteLengthParser({ length: 1 })); this.#parserLine_ = this.#serialport_.pipe(new ReadlineParser()); this.#addEventsListener_(); this.#serialport_.open((error) => { if (error) { reject(error.toString()); } else { resolve(); } }); }); } async close() { return new Promise((resolve, reject) => { if (!this.isOpened()) { resolve(); return; } this.#serialport_.close((error) => { if (error) { reject(error.toString()); } else { resolve(); } }); }); } async setBaudRate(baud) { return new Promise((resolve, reject) => { if (!this.isOpened()) { resolve(); return; } this.#serialport_.update({ baudRate: baud }, (error) => { if (error) { reject(error.toString()); } else { resolve(); } }); }); } async send(data) { return new Promise((resolve, reject) => { if (!this.isOpened()) { resolve(); return; } this.#serialport_.write(data, (error) => { if (error) { reject(error.toString()); } else { resolve(); } }); }); } async setDTRAndRTS(dtr, rts) { return new Promise((resolve, reject) => { if (!this.isOpened()) { resolve(); return; } this.#serialport_.set({ dtr, rts }, (error) => { if (error) { reject(error.toString()); } else { resolve(); } }); }); } async dispose() { await this.close(); super.dispose(); this.#serialport_ = null; this.#parserBytes_ = null; this.#parserLine_ = null; this.#port_ = null; } }