@@ -40,6 +40,13 @@ const firmware_flasher = {
4040 // Properties to preserve firmware state during flashing
4141 preFlashingMessage : null ,
4242 preFlashingMessageType : null ,
43+ // Minimal module-scoped handler references for cleanup to use
44+ detectedUsbDevice : null ,
45+ detectedSerialDevice : null ,
46+ onPortChange : null ,
47+ onDeviceRemoved : null ,
48+ // Single debounce timer shared across instances
49+ portChangeTimer : null ,
4350} ;
4451
4552firmware_flasher . initialize = async function ( callback ) {
@@ -742,20 +749,87 @@ firmware_flasher.initialize = async function (callback) {
742749 return output . join ( "" ) . split ( "\n" ) ;
743750 }
744751
745- function detectedUsbDevice ( device ) {
752+ firmware_flasher . detectedUsbDevice = function ( device ) {
746753 const isFlashOnConnect = $ ( "input.flash_on_connect" ) . is ( ":checked" ) ;
747754
748755 console . log ( `${ self . logHead } Detected USB device:` , device ) ;
749756 console . log ( `${ self . logHead } Reboot mode: %s, flash on connect` , STM32 . rebootMode , isFlashOnConnect ) ;
750757
758+ // If another operation is in progress, ignore port events (unless we're resuming from a reboot)
759+ if ( GUI . connect_lock && ! STM32 . rebootMode ) {
760+ console . log ( `${ self . logHead } Port event ignored due to active operation (connect_lock)` ) ;
761+ return ;
762+ }
763+
764+ // Proceed if we're resuming a reboot sequence or if flash-on-connect is enabled and no operation is active
751765 if ( STM32 . rebootMode || isFlashOnConnect ) {
752766 STM32 . rebootMode = 0 ;
753767 GUI . connect_lock = false ;
754768 startFlashing ( ) ;
755769 }
756- }
770+ } ;
771+
772+ firmware_flasher . detectedSerialDevice = function ( device ) {
773+ AutoDetect . verifyBoard ( ) ;
774+ } ;
775+
776+ firmware_flasher . onPortChange = function ( port ) {
777+ const PORT_CHANGE_DEBOUNCE_MS = 500 ;
778+ // Clear any pending debounce timer so rapid port events don't re-enter the handler.
779+ if ( firmware_flasher . portChangeTimer ) {
780+ clearTimeout ( firmware_flasher . portChangeTimer ) ;
781+ firmware_flasher . portChangeTimer = null ;
782+ }
783+
784+ console . log ( `${ self . logHead } Port changed to:` , port ) ;
785+
786+ if ( GUI . connect_lock ) {
787+ console . log ( `${ self . logHead } Port change ignored during active operation (connect_lock set)` ) ;
788+ return ;
789+ }
790+
791+ // Auto-detect board when port changes and we're on firmware flasher tab
792+ if ( port && port !== "0" && $ ( "input.flash_on_connect" ) . is ( ":checked" ) === false && ! STM32 . rebootMode ) {
793+ console . log ( `${ self . logHead } Auto-detecting board for port change (debounced)` ) ;
794+
795+ // Debounced verification: re-check connect lock when the timeout fires
796+ firmware_flasher . portChangeTimer = setTimeout ( ( ) => {
797+ firmware_flasher . portChangeTimer = null ;
798+ if ( GUI . connect_lock ) {
799+ console . log ( `${ self . logHead } Skipping auto-detect due to active operation at timeout` ) ;
800+ return ;
801+ }
802+ AutoDetect . verifyBoard ( ) ;
803+ } , PORT_CHANGE_DEBOUNCE_MS ) ; // Small delay to ensure port is ready
804+ } else if ( ! port || port === "0" ) {
805+ if ( ! GUI . connect_lock ) {
806+ console . log ( `${ self . logHead } Clearing board selection - no port selected` ) ;
807+ $ ( 'select[name="board"]' ) . val ( "0" ) . trigger ( "change" ) ;
808+ } else {
809+ console . log ( `${ self . logHead } Not clearing board selection because operation in progress` ) ;
810+ }
811+ }
812+ } ;
813+
814+ firmware_flasher . onDeviceRemoved = function ( devicePath ) {
815+ console . log ( `${ self . logHead } Device removed:` , devicePath ) ;
816+
817+ // If a flashing operation or reboot is in progress, the device may be
818+ // removed intentionally (it reboots into DFU). Don't clear selection or
819+ // buffered firmware in that case as it would prevent the flashing flow.
820+ if ( GUI . connect_lock || STM32 . rebootMode ) {
821+ console . log ( `${ self . logHead } Device removal during active operation/reboot; skipping clear` ) ;
822+ return ;
823+ }
757824
758- EventBus . $on ( "port-handler:auto-select-usb-device" , detectedUsbDevice ) ;
825+ $ ( 'select[name="board"]' ) . val ( "0" ) . trigger ( "change" ) ;
826+ clearBufferedFirmware ( ) ;
827+ } ;
828+
829+ EventBus . $on ( "port-handler:auto-select-usb-device" , firmware_flasher . detectedUsbDevice ) ;
830+ EventBus . $on ( "port-handler:auto-select-serial-device" , firmware_flasher . detectedSerialDevice ) ;
831+ EventBus . $on ( "ports-input:change" , firmware_flasher . onPortChange ) ;
832+ EventBus . $on ( "port-handler:device-removed" , firmware_flasher . onDeviceRemoved ) ;
759833
760834 async function saveFirmware ( ) {
761835 const fileType = self . firmware_type ;
@@ -1472,6 +1546,17 @@ firmware_flasher.initialize = async function (callback) {
14721546 $ ( "a.exit_dfu" ) . removeClass ( "disabled" ) ;
14731547 }
14741548
1549+ // Auto-detect board if drone is already connected when tab becomes active
1550+ if (
1551+ ( PortHandler . portAvailable && ! $ ( 'select[name="board"]' ) . val ( ) ) ||
1552+ $ ( 'select[name="board"]' ) . val ( ) === "0"
1553+ ) {
1554+ console . log ( `${ self . logHead } Auto-detecting board for already connected device` ) ;
1555+ setTimeout ( ( ) => {
1556+ AutoDetect . verifyBoard ( ) ;
1557+ } , 1000 ) ; // Small delay to ensure tab is fully loaded
1558+ }
1559+
14751560 GUI . content_ready ( callback ) ;
14761561 }
14771562
@@ -1490,6 +1575,24 @@ firmware_flasher.cleanup = function (callback) {
14901575 $ ( document ) . unbind ( "keypress" ) ;
14911576 $ ( document ) . off ( "click" , "span.progressLabel a" ) ;
14921577
1578+ const cleanupHandler = ( evt , property ) => {
1579+ const handler = firmware_flasher [ property ] ;
1580+ if ( handler ) {
1581+ EventBus . $off ( evt , handler ) ;
1582+ }
1583+ } ;
1584+
1585+ cleanupHandler ( "port-handler:auto-select-usb-device" , "detectedUsbDevice" ) ;
1586+ cleanupHandler ( "port-handler:auto-select-serial-device" , "detectedSerialDevice" ) ;
1587+ cleanupHandler ( "ports-input:change" , "onPortChange" ) ;
1588+ cleanupHandler ( "port-handler:device-removed" , "onDeviceRemoved" ) ;
1589+
1590+ // Clear any pending debounce timer so it cannot fire after cleanup
1591+ if ( firmware_flasher . portChangeTimer ) {
1592+ clearTimeout ( firmware_flasher . portChangeTimer ) ;
1593+ firmware_flasher . portChangeTimer = null ;
1594+ }
1595+
14931596 if ( callback ) callback ( ) ;
14941597} ;
14951598
0 commit comments