You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
140 lines
3.3 KiB
JavaScript
140 lines
3.3 KiB
JavaScript
class SerialConnection extends Connection {
|
|
static priority = 900;
|
|
static name = 'SERIAL';
|
|
|
|
#port;
|
|
#reader;
|
|
#running;
|
|
#packet_buffer;
|
|
#writeLock;
|
|
|
|
constructor(hub) {
|
|
super(hub);
|
|
this.#writeLock = new AsyncLock();
|
|
this.options.enabled = false;
|
|
this.options.baudrate = 115200;
|
|
this.options.offset = 2000;
|
|
|
|
this.#packet_buffer = new PacketBufferScanAll(data => {
|
|
this.hub._parsePacket(this, data);
|
|
});
|
|
}
|
|
|
|
getName() {
|
|
let id = this.#port ? this.#port.getInfo().usbProductId : null;
|
|
switch (id) {
|
|
case 0x7584: return 'CH340S';
|
|
case 0x55d3: return 'CH343';
|
|
case 0x7522: case 0x7523: return 'CH340';
|
|
case 0x5512: case 0x5523: case 0x5584: return 'CH341';
|
|
case 0x9500: case 0x0102: case 0x0501: case 0x80a9: case 0xea60: case 0xea61: case 0xea63: return 'CP210x';
|
|
case 0x0402: case 0x0403: case 0x0404: case 0x0405: case 0x6001: case 0x0602: case 0x6010: return 'FT232';
|
|
case null: return 'none';
|
|
default: return 'unknown';
|
|
}
|
|
}
|
|
|
|
async begin() {
|
|
try {
|
|
const ports = await navigator.serial.getPorts();
|
|
if (ports)
|
|
this.#port = ports[0];
|
|
|
|
} catch (e) {
|
|
return;
|
|
}
|
|
|
|
if (this.options.enabled)
|
|
await this.connect();
|
|
}
|
|
|
|
async select() {
|
|
await this.disconnect();
|
|
this.#port = undefined;
|
|
this._setState(ConnectionState.CONNECTING);
|
|
|
|
try {
|
|
const ports = await navigator.serial.getPorts();
|
|
for (let port of ports) await port.forget();
|
|
|
|
this.#port = await navigator.serial.requestPort();
|
|
this.#port.addEventListener('disconnect', this._disconnect_h);
|
|
} finally {
|
|
this._setState(ConnectionState.DISCONNECTED);
|
|
}
|
|
}
|
|
|
|
async connect() {
|
|
if (!this.#port)
|
|
return;
|
|
|
|
await this.disconnect();
|
|
|
|
this._setState(ConnectionState.CONNECTING);
|
|
|
|
try {
|
|
await this.#port.open({ baudRate: this.options.baudrate });
|
|
} catch (e) {
|
|
if (!(e instanceof InvaidStateError)) {
|
|
this._setState(ConnectionState.DISCONNECTED);
|
|
throw e;
|
|
}
|
|
}
|
|
this._setState(ConnectionState.CONNECTED);
|
|
|
|
this.#readLoop();
|
|
}
|
|
|
|
async disconnect() {
|
|
if (!this.#port)
|
|
return;
|
|
|
|
this.#running = false;
|
|
if (this.#reader) await this.#reader.cancel();
|
|
|
|
try {
|
|
await this.#port.close();
|
|
} catch (e) {}
|
|
this._setState(ConnectionState.DISCONNECTED);
|
|
}
|
|
|
|
async send(data) {
|
|
data = new TextEncoder().encode(data + '\0');
|
|
|
|
this.#writeLock.runExclusive(async () => {
|
|
if (!this.#port || !this.#port.writable)
|
|
return;
|
|
|
|
const writer = this.#port.writable.getWriter();
|
|
try {
|
|
await writer.write(data);
|
|
} finally {
|
|
writer.releaseLock();
|
|
}
|
|
});
|
|
}
|
|
|
|
async _handleDisconnection(event) {
|
|
this.disconnect();
|
|
this.#port = undefined;
|
|
}
|
|
_disconnect_h = this._handleDisconnection.bind(this);
|
|
|
|
async #readLoop() {
|
|
this.#running = true;
|
|
while (this.#running && this.#port && this.#port.readable) {
|
|
this.#reader = this.#port.readable.getReader();
|
|
try {
|
|
while (true) {
|
|
const read = await this.#reader.read();
|
|
if (read.done) break;
|
|
if (read.value) this.#packet_buffer.push(read.value);
|
|
}
|
|
} finally {
|
|
this.#reader.releaseLock();
|
|
this.#reader = null;
|
|
}
|
|
}
|
|
}
|
|
}
|