Skip to content

Commit 4dcae8e

Browse files
committed
fix: disconnect logic on the web
1 parent bb76112 commit 4dcae8e

File tree

2 files changed

+83
-36
lines changed

2 files changed

+83
-36
lines changed

src/js/serial_backend.js

+34-12
Original file line numberDiff line numberDiff line change
@@ -31,9 +31,23 @@ const serial = import.meta.env ? serialWeb : serialNWJS;
3131

3232
let mspHelper;
3333
let connectionTimestamp;
34-
let clicks = false;
3534
let liveDataRefreshTimerId = false;
3635

36+
let isConnected = false;
37+
38+
const toggleStatus = function () {
39+
isConnected = !isConnected;
40+
};
41+
42+
function connectHandler(event) {
43+
onOpen(event.detail);
44+
toggleStatus();
45+
}
46+
47+
function disconnectHandler(event) {
48+
onClosed(event.detail);
49+
}
50+
3751
export function initializeSerialBackend() {
3852
GUI.updateManualPortVisibility = function() {
3953
const selected_port = $('div#port-picker #port option:selected');
@@ -82,10 +96,6 @@ export function initializeSerialBackend() {
8296
if (!GUI.connect_lock) {
8397
// GUI control overrides the user control
8498

85-
const toggleStatus = function () {
86-
clicks = !clicks;
87-
};
88-
8999
GUI.configuration_loaded = false;
90100

91101
const selected_baud = parseInt($('div#port-picker #baud').val());
@@ -94,7 +104,7 @@ export function initializeSerialBackend() {
94104
if (selectedPort.data().isDFU) {
95105
$('select#baud').hide();
96106
} else if (portName !== '0') {
97-
if (!clicks) {
107+
if (!isConnected) {
98108
console.log(`Connecting to: ${portName}`);
99109
GUI.connecting_to = portName;
100110

@@ -109,10 +119,13 @@ export function initializeSerialBackend() {
109119

110120
serial.connect('virtual', {}, onOpenVirtual);
111121
} else if (import.meta.env) {
112-
serial.addEventListener('connect', (event) => {
113-
onOpen(event.detail);
114-
toggleStatus();
115-
});
122+
// Explicitly disconnect the event listeners before attaching the new ones.
123+
serial.removeEventListener('connect', connectHandler);
124+
serial.addEventListener('connect', connectHandler);
125+
126+
serial.removeEventListener('disconnect', disconnectHandler);
127+
serial.addEventListener('disconnect', disconnectHandler);
128+
116129
serial.connect({ baudRate });
117130
} else {
118131
serial.connect(
@@ -276,7 +289,15 @@ function abortConnection() {
276289
$('div#port-picker #port, div#port-picker #baud, div#port-picker #delay').prop('disabled', false);
277290

278291
// reset data
279-
clicks = false;
292+
isConnected = false;
293+
}
294+
295+
/**
296+
* purpose of this is to bridge the old and new api
297+
* when serial events are handled.
298+
*/
299+
function read_serial_adapter(event) {
300+
read_serial(event.detail.buffer);
280301
}
281302

282303
function onOpen(openInfo) {
@@ -307,7 +328,8 @@ function onOpen(openInfo) {
307328
$('input[name="expertModeCheckbox"]').prop('checked', result).trigger('change');
308329

309330
if(import.meta.env) {
310-
serial.addEventListener('receive', (e) => read_serial(e.detail.buffer));
331+
serial.removeEventListener('receive', read_serial_adapter);
332+
serial.addEventListener('receive', read_serial_adapter);
311333
} else {
312334
serial.onReceive.addListener(read_serial);
313335
}

src/js/webSerial.js

+49-24
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,8 @@
11
import { webSerialDevices } from "./serial_devices";
22

3-
async function* streamAsyncIterable(stream) {
4-
const reader = stream.getReader();
3+
async function* streamAsyncIterable(reader, keepReadingFlag) {
54
try {
6-
while (true) {
5+
while (keepReadingFlag()) {
76
const { done, value } = await reader.read();
87
if (done) {
98
return;
@@ -34,10 +33,20 @@ class WebSerial extends EventTarget {
3433
this.port = null;
3534
this.reader = null;
3635
this.writer = null;
36+
this.reading = false;
3737

3838
this.connect = this.connect.bind(this);
3939
}
4040

41+
handleReceiveBytes(info) {
42+
this.bytesReceived += info.detail.byteLength;
43+
}
44+
45+
handleDisconnect() {
46+
this.removeEventListener('receive', this.handleReceiveBytes);
47+
this.removeEventListener('disconnect', this.handleDisconnect);
48+
}
49+
4150
async connect(options) {
4251
this.openRequested = true;
4352
this.port = await navigator.serial.requestPort({
@@ -48,6 +57,7 @@ class WebSerial extends EventTarget {
4857
const connectionInfo = this.port.getInfo();
4958
this.connectionInfo = connectionInfo;
5059
this.writer = this.port.writable.getWriter();
60+
this.reader = this.port.readable.getReader();
5161

5262
if (connectionInfo && !this.openCanceled) {
5363
this.connected = true;
@@ -58,9 +68,8 @@ class WebSerial extends EventTarget {
5868
this.failed = 0;
5969
this.openRequested = false;
6070

61-
this.addEventListener("receive", (info) => {
62-
this.bytesReceived += info.detail.byteLength;
63-
});
71+
this.addEventListener("receive", this.handleReceiveBytes);
72+
this.addEventListener('disconnect', this.handleDisconnect);
6473

6574
console.log(
6675
`${this.logHead} Connection opened with ID: ${connectionInfo.connectionId}, Baud: ${options.baudRate}`,
@@ -73,7 +82,9 @@ class WebSerial extends EventTarget {
7382
// the stream async iterable interface:
7483
// https://web.dev/streams/#asynchronous-iteration
7584

76-
for await (let value of streamAsyncIterable(this.port.readable)) {
85+
86+
this.reading = true;
87+
for await (let value of streamAsyncIterable(this.reader, () => this.reading)) {
7788
this.dispatchEvent(
7889
new CustomEvent("receive", { detail: value }),
7990
);
@@ -108,32 +119,46 @@ class WebSerial extends EventTarget {
108119

109120
async disconnect() {
110121
this.connected = false;
122+
this.transmitting = false;
123+
this.reading = false;
124+
this.bytesReceived = 0;
125+
this.bytesSent = 0;
111126

112-
if (this.port) {
113-
this.transmitting = false;
127+
const doCleanup = async () => {
128+
if (this.reader) {
129+
this.reader.releaseLock();
130+
this.reader = null;
131+
}
114132
if (this.writer) {
115-
await this.writer.close();
133+
await this.writer.releaseLock();
116134
this.writer = null;
117135
}
118-
try {
136+
if (this.port) {
119137
await this.port.close();
120138
this.port = null;
139+
}
140+
};
121141

122-
console.log(
123-
`${this.logHead}Connection with ID: ${this.connectionId} closed, Sent: ${this.bytesSent} bytes, Received: ${this.bytesReceived} bytes`,
124-
);
142+
try {
143+
await doCleanup();
125144

126-
this.connectionId = false;
127-
this.bitrate = 0;
128-
this.dispatchEvent(new CustomEvent("disconnect"));
129-
} catch (error) {
130-
console.error(error);
131-
console.error(
132-
`${this.logHead}Failed to close connection with ID: ${this.connectionId} closed, Sent: ${this.bytesSent} bytes, Received: ${this.bytesReceived} bytes`,
133-
);
145+
console.log(
146+
`${this.logHead}Connection with ID: ${this.connectionId} closed, Sent: ${this.bytesSent} bytes, Received: ${this.bytesReceived} bytes`,
147+
);
148+
149+
this.connectionId = false;
150+
this.bitrate = 0;
151+
this.dispatchEvent(new CustomEvent("disconnect", { detail: true }));
152+
} catch (error) {
153+
console.error(error);
154+
console.error(
155+
`${this.logHead}Failed to close connection with ID: ${this.connectionId} closed, Sent: ${this.bytesSent} bytes, Received: ${this.bytesReceived} bytes`,
156+
);
157+
this.dispatchEvent(new CustomEvent("disconnect", { detail: false }));
158+
} finally {
159+
if (this.openCanceled) {
160+
this.openCanceled = false;
134161
}
135-
} else {
136-
this.openCanceled = true;
137162
}
138163
}
139164

0 commit comments

Comments
 (0)