diff --git a/src/js/protocols/webstm32.js b/src/js/protocols/webstm32.js index 3229984e29..1eae99e76c 100644 --- a/src/js/protocols/webstm32.js +++ b/src/js/protocols/webstm32.js @@ -25,19 +25,9 @@ function readSerialAdapter(event) { read_serial(event.detail.buffer); } -function onTimeoutHandler() { - GUI.connect_lock = false; - console.log(`${STM32Protocol.logHead} Looking for capabilities via MSP failed`); - - TABS.firmware_flasher.flashingMessage( - i18n.getMessage("stm32RebootingToBootloaderFailed"), - TABS.firmware_flasher.FLASH_MESSAGE_TYPES.INVALID, - ); -} - -function onFailureHandler() { - GUI.connect_lock = false; - TABS.firmware_flasher.refresh(); +function onMSPConnectionError() { + gui_log(i18n.getMessage("stm32RebootingToBootloaderFailed")); + STM32.handleError(); } class STM32Protocol { @@ -88,6 +78,18 @@ class STM32Protocol { this.handleMSPConnect = this.handleMSPConnect.bind(this); } + /** + * Centralized error handling method that resets UI state and releases connection lock + * @param {boolean} resetRebootMode - Whether to reset the reboot mode + */ + handleError(resetRebootMode = true) { + GUI.connect_lock = false; + if (resetRebootMode) { + this.rebootMode = 0; + } + TABS.firmware_flasher.resetFlashingState(); + } + handleConnect(event) { console.log(`${this.logHead} Connected to serial port`, event.detail, event); if (event) { @@ -118,19 +120,17 @@ class STM32Protocol { console.log(`${this.logHead} DFU request permission granted`, device); } else { console.error(`${this.logHead} DFU request permission denied`); - this.rebootMode = 0; - GUI.connect_lock = false; + this.handleError(); } }) .catch((e) => { console.error(`${this.logHead} DFU request permission failed`, e); - this.rebootMode = 0; - GUI.connect_lock = false; + this.handleError(); }); } }, 3000); } else { - GUI.connect_lock = false; + this.handleError(false); } } @@ -251,8 +251,8 @@ class STM32Protocol { this.port, this.mspOptions.reboot_baud, this.handleMSPConnect, - onTimeoutHandler, - onFailureHandler, + onMSPConnectionError, + onMSPConnectionError, ); } } diff --git a/src/js/tabs/firmware_flasher.js b/src/js/tabs/firmware_flasher.js index a42f857ea7..22fcd01480 100644 --- a/src/js/tabs/firmware_flasher.js +++ b/src/js/tabs/firmware_flasher.js @@ -29,7 +29,6 @@ const firmware_flasher = { selectedBoard: undefined, cloudBuildKey: null, cloudBuildOptions: null, - isFlashing: false, intel_hex: undefined, // standard intel hex in string format parsed_hex: undefined, // parsed raw hex in array format isConfigLocal: false, // Set to true if the user loads one locally @@ -38,6 +37,9 @@ const firmware_flasher = { config: {}, developmentFirmwareLoaded: false, // Is the firmware to be flashed from the development branch? cancelBuild: false, + // Properties to preserve firmware state during flashing + preFlashingMessage: null, + preFlashingMessageType: null, }; firmware_flasher.initialize = async function (callback) { @@ -803,12 +805,17 @@ firmware_flasher.initialize = async function (callback) { } else { // Maybe the board is in DFU mode, but it does not have permissions. Ask for them. console.log(`${self.logHead} No valid port detected, asking for permissions`); - DFU.requestPermission().then((device) => { - DFU.connect(device.path, firmware, options); - }); + + DFU.requestPermission() + .then((device) => { + DFU.connect(device.path, firmware, options); + }) + .catch((error) => { + console.error("Permission request failed", error); + firmware_flasher.resetFlashingState(); + }); } - self.isFlashing = false; GUI.interval_resume("sponsor"); } @@ -1258,7 +1265,6 @@ firmware_flasher.initialize = async function (callback) { const aborted = function (message) { GUI.connect_lock = false; - self.isFlashing = false; self.enableFlashButton(true); self.enableLoadRemoteFileButton(true); self.enableLoadFileButton(true); @@ -1381,7 +1387,9 @@ firmware_flasher.initialize = async function (callback) { return; } - self.isFlashing = true; + // Preserve current firmware message state before flashing + self.preservePreFlashingState(); + GUI.interval_pause("sponsor"); self.enableFlashButton(false); @@ -1404,7 +1412,6 @@ firmware_flasher.initialize = async function (callback) { : i18n.getMessage("firmwareFlasherUF2SaveFailed"), saved ? self.FLASH_MESSAGE_TYPES.VALID : self.FLASH_MESSAGE_TYPES.INVALID, ); - self.isFlashing = false; GUI.interval_resume("sponsor"); self.enableFlashButton(true); self.enableLoadRemoteFileButton(true); @@ -1500,6 +1507,42 @@ firmware_flasher.enableDfuExitButton = function (enabled) { $("a.exit_dfu").toggleClass("disabled", !enabled); }; +firmware_flasher.resetFlashingState = function () { + console.log(`${this.logHead} Reset flashing state`); + this.enableFlashButton(!!this.parsed_hex || !!this.uf2_binary); // Only enable if firmware is loaded + this.enableDfuExitButton(PortHandler.dfuAvailable); + this.enableLoadRemoteFileButton(true); + this.enableLoadFileButton(true); + + // Restore pre-flashing message if firmware is still loaded, otherwise show "not loaded" + if (this.parsed_hex || this.uf2_binary) { + if (this.preFlashingMessage && this.preFlashingMessageType) { + this.flashingMessage(this.preFlashingMessage, this.preFlashingMessageType); + } + } else { + this.flashingMessage(i18n.getMessage("firmwareFlasherFirmwareNotLoaded"), this.FLASH_MESSAGE_TYPES.NEUTRAL); + } + + GUI.interval_resume("sponsor"); +}; + +firmware_flasher.preservePreFlashingState = function () { + // Preserve the current firmware message and type before flashing starts + const progressLabel = $("span.progressLabel"); + this.preFlashingMessage = progressLabel.html(); + + // Determine the current message type based on CSS classes + if (progressLabel.hasClass("valid")) { + this.preFlashingMessageType = this.FLASH_MESSAGE_TYPES.VALID; + } else if (progressLabel.hasClass("invalid")) { + this.preFlashingMessageType = this.FLASH_MESSAGE_TYPES.INVALID; + } else if (progressLabel.hasClass("actionRequired")) { + this.preFlashingMessageType = this.FLASH_MESSAGE_TYPES.ACTION; + } else { + this.preFlashingMessageType = this.FLASH_MESSAGE_TYPES.NEUTRAL; + } +}; + firmware_flasher.refresh = function (callback) { const self = this; diff --git a/src/js/utils/AutoDetect.js b/src/js/utils/AutoDetect.js index 5d865862db..a0ac55e605 100644 --- a/src/js/utils/AutoDetect.js +++ b/src/js/utils/AutoDetect.js @@ -37,8 +37,12 @@ class AutoDetect { 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) { - console.log("Releases not loaded yet"); gui_log(i18n.getMessage("firmwareFlasherNoTargetsLoaded")); return; }