Files
mixly3-server/src/common/serial.js
2025-10-04 10:33:41 +08:00

209 lines
5.9 KiB
JavaScript
Executable File

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;
}
}