1
0
Fork 0
mirror of https://github.com/betaflight/betaflight-configurator.git synced 2025-07-25 17:25:16 +03:00

fix: web serial disconnect logic (#3575)

fix: disconnect logic on the web
This commit is contained in:
Tomas Chmelevskij 2023-09-13 00:32:26 +03:00 committed by GitHub
parent 3069fd986c
commit 0a93ea478c
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 85 additions and 38 deletions

View file

@ -31,9 +31,23 @@ const serial = import.meta.env ? serialWeb : serialNWJS;
let mspHelper; let mspHelper;
let connectionTimestamp; let connectionTimestamp;
let clicks = false;
let liveDataRefreshTimerId = false; let liveDataRefreshTimerId = false;
let isConnected = false;
const toggleStatus = function () {
isConnected = !isConnected;
};
function connectHandler(event) {
onOpen(event.detail);
toggleStatus();
}
function disconnectHandler(event) {
onClosed(event.detail);
}
export function initializeSerialBackend() { export function initializeSerialBackend() {
GUI.updateManualPortVisibility = function() { GUI.updateManualPortVisibility = function() {
const selected_port = $('div#port-picker #port option:selected'); const selected_port = $('div#port-picker #port option:selected');
@ -82,10 +96,6 @@ export function initializeSerialBackend() {
if (!GUI.connect_lock) { if (!GUI.connect_lock) {
// GUI control overrides the user control // GUI control overrides the user control
const toggleStatus = function () {
clicks = !clicks;
};
GUI.configuration_loaded = false; GUI.configuration_loaded = false;
const selected_baud = parseInt($('div#port-picker #baud').val()); const selected_baud = parseInt($('div#port-picker #baud').val());
@ -94,7 +104,7 @@ export function initializeSerialBackend() {
if (selectedPort.data().isDFU) { if (selectedPort.data().isDFU) {
$('select#baud').hide(); $('select#baud').hide();
} else if (portName !== '0') { } else if (portName !== '0') {
if (!clicks) { if (!isConnected) {
console.log(`Connecting to: ${portName}`); console.log(`Connecting to: ${portName}`);
GUI.connecting_to = portName; GUI.connecting_to = portName;
@ -109,10 +119,13 @@ export function initializeSerialBackend() {
serial.connect('virtual', {}, onOpenVirtual); serial.connect('virtual', {}, onOpenVirtual);
} else if (import.meta.env) { } else if (import.meta.env) {
serial.addEventListener('connect', (event) => { // Explicitly disconnect the event listeners before attaching the new ones.
onOpen(event.detail); serial.removeEventListener('connect', connectHandler);
toggleStatus(); serial.addEventListener('connect', connectHandler);
});
serial.removeEventListener('disconnect', disconnectHandler);
serial.addEventListener('disconnect', disconnectHandler);
serial.connect({ baudRate }); serial.connect({ baudRate });
} else { } else {
serial.connect( serial.connect(
@ -276,7 +289,15 @@ function abortConnection() {
$('div#port-picker #port, div#port-picker #baud, div#port-picker #delay').prop('disabled', false); $('div#port-picker #port, div#port-picker #baud, div#port-picker #delay').prop('disabled', false);
// reset data // reset data
clicks = false; isConnected = false;
}
/**
* purpose of this is to bridge the old and new api
* when serial events are handled.
*/
function read_serial_adapter(event) {
read_serial(event.detail.buffer);
} }
function onOpen(openInfo) { function onOpen(openInfo) {
@ -307,7 +328,8 @@ function onOpen(openInfo) {
$('input[name="expertModeCheckbox"]').prop('checked', result).trigger('change'); $('input[name="expertModeCheckbox"]').prop('checked', result).trigger('change');
if(import.meta.env) { if(import.meta.env) {
serial.addEventListener('receive', (e) => read_serial(e.detail.buffer)); serial.removeEventListener('receive', read_serial_adapter);
serial.addEventListener('receive', read_serial_adapter);
} else { } else {
serial.onReceive.addListener(read_serial); serial.onReceive.addListener(read_serial);
} }

View file

@ -1,9 +1,8 @@
import { webSerialDevices } from "./serial_devices"; import { webSerialDevices } from "./serial_devices";
async function* streamAsyncIterable(stream) { async function* streamAsyncIterable(reader, keepReadingFlag) {
const reader = stream.getReader();
try { try {
while (true) { while (keepReadingFlag()) {
const { done, value } = await reader.read(); const { done, value } = await reader.read();
if (done) { if (done) {
return; return;
@ -34,10 +33,20 @@ class WebSerial extends EventTarget {
this.port = null; this.port = null;
this.reader = null; this.reader = null;
this.writer = null; this.writer = null;
this.reading = false;
this.connect = this.connect.bind(this); this.connect = this.connect.bind(this);
} }
handleReceiveBytes(info) {
this.bytesReceived += info.detail.byteLength;
}
handleDisconnect() {
this.removeEventListener('receive', this.handleReceiveBytes);
this.removeEventListener('disconnect', this.handleDisconnect);
}
async connect(options) { async connect(options) {
this.openRequested = true; this.openRequested = true;
this.port = await navigator.serial.requestPort({ this.port = await navigator.serial.requestPort({
@ -48,6 +57,7 @@ class WebSerial extends EventTarget {
const connectionInfo = this.port.getInfo(); const connectionInfo = this.port.getInfo();
this.connectionInfo = connectionInfo; this.connectionInfo = connectionInfo;
this.writer = this.port.writable.getWriter(); this.writer = this.port.writable.getWriter();
this.reader = this.port.readable.getReader();
if (connectionInfo && !this.openCanceled) { if (connectionInfo && !this.openCanceled) {
this.connected = true; this.connected = true;
@ -58,9 +68,8 @@ class WebSerial extends EventTarget {
this.failed = 0; this.failed = 0;
this.openRequested = false; this.openRequested = false;
this.addEventListener("receive", (info) => { this.addEventListener("receive", this.handleReceiveBytes);
this.bytesReceived += info.detail.byteLength; this.addEventListener('disconnect', this.handleDisconnect);
});
console.log( console.log(
`${this.logHead} Connection opened with ID: ${connectionInfo.connectionId}, Baud: ${options.baudRate}`, `${this.logHead} Connection opened with ID: ${connectionInfo.connectionId}, Baud: ${options.baudRate}`,
@ -73,7 +82,9 @@ class WebSerial extends EventTarget {
// the stream async iterable interface: // the stream async iterable interface:
// https://web.dev/streams/#asynchronous-iteration // https://web.dev/streams/#asynchronous-iteration
for await (let value of streamAsyncIterable(this.port.readable)) {
this.reading = true;
for await (let value of streamAsyncIterable(this.reader, () => this.reading)) {
this.dispatchEvent( this.dispatchEvent(
new CustomEvent("receive", { detail: value }), new CustomEvent("receive", { detail: value }),
); );
@ -108,32 +119,46 @@ class WebSerial extends EventTarget {
async disconnect() { async disconnect() {
this.connected = false; this.connected = false;
this.transmitting = false;
this.reading = false;
this.bytesReceived = 0;
this.bytesSent = 0;
if (this.port) { const doCleanup = async () => {
this.transmitting = false; if (this.reader) {
this.reader.releaseLock();
this.reader = null;
}
if (this.writer) { if (this.writer) {
await this.writer.close(); await this.writer.releaseLock();
this.writer = null; this.writer = null;
} }
try { if (this.port) {
await this.port.close(); await this.port.close();
this.port = null; this.port = null;
console.log(
`${this.logHead}Connection with ID: ${this.connectionId} closed, Sent: ${this.bytesSent} bytes, Received: ${this.bytesReceived} bytes`,
);
this.connectionId = false;
this.bitrate = 0;
this.dispatchEvent(new CustomEvent("disconnect"));
} catch (error) {
console.error(error);
console.error(
`${this.logHead}Failed to close connection with ID: ${this.connectionId} closed, Sent: ${this.bytesSent} bytes, Received: ${this.bytesReceived} bytes`,
);
} }
} else { };
this.openCanceled = true;
try {
await doCleanup();
console.log(
`${this.logHead}Connection with ID: ${this.connectionId} closed, Sent: ${this.bytesSent} bytes, Received: ${this.bytesReceived} bytes`,
);
this.connectionId = false;
this.bitrate = 0;
this.dispatchEvent(new CustomEvent("disconnect", { detail: true }));
} catch (error) {
console.error(error);
console.error(
`${this.logHead}Failed to close connection with ID: ${this.connectionId} closed, Sent: ${this.bytesSent} bytes, Received: ${this.bytesReceived} bytes`,
);
this.dispatchEvent(new CustomEvent("disconnect", { detail: false }));
} finally {
if (this.openCanceled) {
this.openCanceled = false;
}
} }
} }