From d604c80990815f502c1e755c15ac9fa1483bc79c Mon Sep 17 00:00:00 2001 From: Mark Haslinghuis Date: Tue, 14 Oct 2025 23:05:19 +0200 Subject: [PATCH 1/6] Fix cleanup of autodetect handlers --- src/js/utils/AutoDetect.js | 82 +++++++++++++++++++------------------- 1 file changed, 41 insertions(+), 41 deletions(-) diff --git a/src/js/utils/AutoDetect.js b/src/js/utils/AutoDetect.js index a0ac55e605..15ced101da 100644 --- a/src/js/utils/AutoDetect.js +++ b/src/js/utils/AutoDetect.js @@ -33,42 +33,38 @@ class AutoDetect { MSP.read(event.detail); } - verifyBoard() { + async verifyBoard() { const port = PortHandler.portPicker.selectedPort; - const isLoaded = TABS.firmware_flasher.targets ? Object.keys(TABS.firmware_flasher.targets).length > 0 : false; - - if (!PortHandler.portAvailable) { - gui_log(i18n.getMessage("firmwareFlasherNoValidPort")); - return; - } - - if (!isLoaded) { - gui_log(i18n.getMessage("firmwareFlasherNoTargetsLoaded")); - return; - } - - if (serial.connected || serial.connectionId) { - console.warn( - "Attempting to connect while there still is a connection", - serial.connected, - serial.connectionId, - serial.openCanceled, - ); - serial.disconnect(); - return; - } - - gui_log(i18n.getMessage("firmwareFlasherDetectBoardQuery")); if (!port.startsWith("virtual")) { serial.addEventListener("connect", this.boundHandleConnect, { once: true }); serial.addEventListener("disconnect", this.boundHandleDisconnect, { once: true }); - console.log("Connecting to serial port", port, serial.connected, serial.connectionId); - - serial.connect(port, { baudRate: PortHandler.portPicker.selectedBauds || 115200 }); - } else { - gui_log(i18n.getMessage("serialPortOpenFail")); + const isLoaded = TABS.firmware_flasher.targets + ? Object.keys(TABS.firmware_flasher.targets).length > 0 + : false; + let result = false; + + try { + if (!PortHandler.portAvailable) { + gui_log(i18n.getMessage("firmwareFlasherNoValidPort")); + } else if (!isLoaded) { + gui_log(i18n.getMessage("firmwareFlasherNoTargetsLoaded")); + } else if (serial.connected || serial.connectionId) { + console.warn("Attempting to connect while there still is a connection", serial.connected); + gui_log(i18n.getMessage("serialPortOpenFail")); + } else { + console.log("Connecting to serial port", port); + gui_log(i18n.getMessage("firmwareFlasherDetectBoardQuery")); + result = await serial.connect(port, { baudRate: PortHandler.portPicker.selectedBauds || 115200 }); + } + } catch (error) { + console.error("Failed to connect:", error); + } finally { + if (!result) { + this.cleanup(); + } + } } } @@ -116,17 +112,7 @@ class AutoDetect { ); } - // Remove event listeners using stored references - serial.removeEventListener("receive", this.boundHandleSerialReceive); - serial.removeEventListener("connect", this.boundHandleConnect); - serial.removeEventListener("disconnect", this.boundHandleDisconnect); - - // Clean up MSP listeners - MSP.clearListeners(); - MSP.disconnect_cleanup(); - - // Disconnect without passing onClosed as a callback - serial.disconnect(); + this.cleanup(); } async getBoardInfo() { @@ -195,6 +181,20 @@ class AutoDetect { gui_log(i18n.getMessage("serialPortOpenFail")); } } + + cleanup() { + // Remove event listeners using stored references + serial.removeEventListener("receive", this.boundHandleSerialReceive); + serial.removeEventListener("connect", this.boundHandleConnect); + serial.removeEventListener("disconnect", this.boundHandleDisconnect); + + // Clean up MSP listeners + MSP.clearListeners(); + MSP.disconnect_cleanup(); + + // Disconnect without passing onClosed as a callback + serial.disconnect(); + } } export default new AutoDetect(); From f9c2fdeb924f5ae850de5193aada833235b84401 Mon Sep 17 00:00:00 2001 From: Mark Haslinghuis Date: Tue, 14 Oct 2025 23:18:41 +0200 Subject: [PATCH 2/6] Keep onClosed firing by delaying listener removal --- src/js/utils/AutoDetect.js | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/js/utils/AutoDetect.js b/src/js/utils/AutoDetect.js index 15ced101da..828702e1c2 100644 --- a/src/js/utils/AutoDetect.js +++ b/src/js/utils/AutoDetect.js @@ -182,18 +182,18 @@ class AutoDetect { } } - cleanup() { - // Remove event listeners using stored references + async cleanup() { + // Disconnect first, so the once-registered disconnect handler can fire + await serial.disconnect(); + + // Remove event listeners using stored references (disconnect listener is once-registered and already removed) serial.removeEventListener("receive", this.boundHandleSerialReceive); serial.removeEventListener("connect", this.boundHandleConnect); - serial.removeEventListener("disconnect", this.boundHandleDisconnect); + // Do NOT remove disconnect listener, as it is once-registered and will be auto-removed - // Clean up MSP listeners + // Clean up MSP listeners after disconnect MSP.clearListeners(); MSP.disconnect_cleanup(); - - // Disconnect without passing onClosed as a callback - serial.disconnect(); } } From 12c37fac2774e8d010deef9ecfcb1189a0f31fa1 Mon Sep 17 00:00:00 2001 From: Mark Haslinghuis Date: Tue, 14 Oct 2025 23:28:47 +0200 Subject: [PATCH 3/6] Wrap serial.disconnect in try/catch/finally to guarantee cleanup on errors --- src/js/utils/AutoDetect.js | 23 ++++++++++++++--------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/src/js/utils/AutoDetect.js b/src/js/utils/AutoDetect.js index 828702e1c2..b66bfd0323 100644 --- a/src/js/utils/AutoDetect.js +++ b/src/js/utils/AutoDetect.js @@ -184,16 +184,21 @@ class AutoDetect { async cleanup() { // Disconnect first, so the once-registered disconnect handler can fire - await serial.disconnect(); - - // Remove event listeners using stored references (disconnect listener is once-registered and already removed) - serial.removeEventListener("receive", this.boundHandleSerialReceive); - serial.removeEventListener("connect", this.boundHandleConnect); - // Do NOT remove disconnect listener, as it is once-registered and will be auto-removed + try { + await serial.disconnect(); + } catch (error) { + // Log the error with context but continue to run cleanup + console.error(`${this.logHead || "AutoDetect"}: serial.disconnect() failed:`, error); + } finally { + // Remove event listeners using stored references (disconnect listener is once-registered and already removed) + serial.removeEventListener("receive", this.boundHandleSerialReceive); + serial.removeEventListener("connect", this.boundHandleConnect); + // Do NOT remove disconnect listener, as it is once-registered and will be auto-removed - // Clean up MSP listeners after disconnect - MSP.clearListeners(); - MSP.disconnect_cleanup(); + // Clean up MSP listeners after disconnect (always run) + MSP.clearListeners(); + MSP.disconnect_cleanup(); + } } } From b607a1d3aa0e0dca63b4887755926bb7b85b2c52 Mon Sep 17 00:00:00 2001 From: Mark Haslinghuis Date: Tue, 14 Oct 2025 23:30:54 +0200 Subject: [PATCH 4/6] Guard against undefined TABS.firmware_flasher (runtime TypeError) --- src/js/utils/AutoDetect.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/js/utils/AutoDetect.js b/src/js/utils/AutoDetect.js index b66bfd0323..0f569026be 100644 --- a/src/js/utils/AutoDetect.js +++ b/src/js/utils/AutoDetect.js @@ -40,7 +40,8 @@ class AutoDetect { serial.addEventListener("connect", this.boundHandleConnect, { once: true }); serial.addEventListener("disconnect", this.boundHandleDisconnect, { once: true }); - const isLoaded = TABS.firmware_flasher.targets + // Safely check firmware_flasher.targets (use optional chaining so this doesn't throw when undefined) + const isLoaded = TABS.firmware_flasher?.targets ? Object.keys(TABS.firmware_flasher.targets).length > 0 : false; let result = false; From 59a980a89b0b146e113a042381fa933815a93f33 Mon Sep 17 00:00:00 2001 From: Mark Haslinghuis Date: Tue, 14 Oct 2025 23:33:52 +0200 Subject: [PATCH 5/6] Avoid disconnecting existing sessions and stale listeners; only cleanup if a connect was attempted. Move listener registration to just-in-time. --- src/js/utils/AutoDetect.js | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/src/js/utils/AutoDetect.js b/src/js/utils/AutoDetect.js index 0f569026be..295c3a3808 100644 --- a/src/js/utils/AutoDetect.js +++ b/src/js/utils/AutoDetect.js @@ -37,14 +37,12 @@ class AutoDetect { const port = PortHandler.portPicker.selectedPort; if (!port.startsWith("virtual")) { - serial.addEventListener("connect", this.boundHandleConnect, { once: true }); - serial.addEventListener("disconnect", this.boundHandleDisconnect, { once: true }); - // Safely check firmware_flasher.targets (use optional chaining so this doesn't throw when undefined) const isLoaded = TABS.firmware_flasher?.targets ? Object.keys(TABS.firmware_flasher.targets).length > 0 : false; let result = false; + let attempted = false; try { if (!PortHandler.portAvailable) { @@ -55,6 +53,11 @@ class AutoDetect { console.warn("Attempting to connect while there still is a connection", serial.connected); gui_log(i18n.getMessage("serialPortOpenFail")); } else { + // We're about to attempt a connection: register listeners just-in-time + attempted = true; + serial.addEventListener("connect", this.boundHandleConnect, { once: true }); + serial.addEventListener("disconnect", this.boundHandleDisconnect, { once: true }); + console.log("Connecting to serial port", port); gui_log(i18n.getMessage("firmwareFlasherDetectBoardQuery")); result = await serial.connect(port, { baudRate: PortHandler.portPicker.selectedBauds || 115200 }); @@ -62,7 +65,8 @@ class AutoDetect { } catch (error) { console.error("Failed to connect:", error); } finally { - if (!result) { + // Only run cleanup when we actually attempted a connection and it failed + if (attempted && !result) { this.cleanup(); } } From e0039c96c3bc78918500713fcd495c0397dab94a Mon Sep 17 00:00:00 2001 From: Mark Haslinghuis Date: Tue, 14 Oct 2025 23:40:54 +0200 Subject: [PATCH 6/6] Fix the undefined this.logHead reference --- src/js/utils/AutoDetect.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/js/utils/AutoDetect.js b/src/js/utils/AutoDetect.js index 295c3a3808..10aaee89c7 100644 --- a/src/js/utils/AutoDetect.js +++ b/src/js/utils/AutoDetect.js @@ -193,7 +193,7 @@ class AutoDetect { await serial.disconnect(); } catch (error) { // Log the error with context but continue to run cleanup - console.error(`${this.logHead || "AutoDetect"}: serial.disconnect() failed:`, error); + console.error("Serial disconnection failed:", error); } finally { // Remove event listeners using stored references (disconnect listener is once-registered and already removed) serial.removeEventListener("receive", this.boundHandleSerialReceive);