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:
parent
3069fd986c
commit
0a93ea478c
2 changed files with 85 additions and 38 deletions
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue