@@ -40,6 +40,94 @@ 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 ,
50+ } ;
51+
52+ // Move port/device handlers to module scope for easier maintenance and testing.
53+ // These handlers reference the `firmware_flasher` object and shared imports (STM32, AutoDetect, GUI).
54+ const PORT_CHANGE_DEBOUNCE_MS = 500 ;
55+
56+ function handleDetectedDevice ( device , isSerial ) {
57+ const isFlashOnConnect = $ ( "input.flash_on_connect" ) . is ( ":checked" ) ;
58+
59+ console . log (
60+ `${ firmware_flasher . logHead || "[FIRMWARE_FLASHER]" } Detected ${ isSerial ? "serial" : "USB" } device:` ,
61+ device ,
62+ ) ;
63+ console . log (
64+ `${ firmware_flasher . logHead || "[FIRMWARE_FLASHER]" } Reboot mode: %s, flash on connect` ,
65+ STM32 . rebootMode ,
66+ isFlashOnConnect ,
67+ ) ;
68+
69+ if ( STM32 . rebootMode || isFlashOnConnect ) {
70+ STM32 . rebootMode = 0 ;
71+ GUI . connect_lock = false ;
72+ firmware_flasher . startFlashing ( ) ;
73+ } else {
74+ if ( isSerial ) {
75+ // Auto-detect board when firmware flasher tab is active and no flash-on-connect
76+ console . log ( `${ firmware_flasher . logHead } Auto-detecting board for connected serial device` ) ;
77+ }
78+ AutoDetect . verifyBoard ( ) ;
79+ }
80+ }
81+
82+ firmware_flasher . detectedUsbDevice = function ( device ) {
83+ handleDetectedDevice ( device , false ) ;
84+ } ;
85+
86+ firmware_flasher . detectedSerialDevice = function ( device ) {
87+ handleDetectedDevice ( device , true ) ;
88+ } ;
89+
90+ firmware_flasher . onPortChange = function ( port ) {
91+ // Clear any pending debounce timer so rapid port events don't re-enter the handler.
92+ if ( firmware_flasher . _portChangeTimer ) {
93+ clearTimeout ( firmware_flasher . _portChangeTimer ) ;
94+ firmware_flasher . _portChangeTimer = null ;
95+ }
96+
97+ console . log ( `${ firmware_flasher . logHead || "[FIRMWARE_FLASHER]" } Port changed to:` , port ) ;
98+
99+ if ( GUI . connect_lock ) {
100+ console . log ( `${ firmware_flasher . logHead } Port change ignored during active operation (connect_lock set)` ) ;
101+ return ;
102+ }
103+
104+ // Auto-detect board when port changes and we're on firmware flasher tab
105+ if ( port && port !== "0" && $ ( "input.flash_on_connect" ) . is ( ":checked" ) === false && ! STM32 . rebootMode ) {
106+ console . log ( `${ firmware_flasher . logHead } Auto-detecting board for port change (debounced)` ) ;
107+
108+ // Debounced verification: re-check connect lock when the timeout fires
109+ firmware_flasher . _portChangeTimer = setTimeout ( ( ) => {
110+ firmware_flasher . _portChangeTimer = null ;
111+ if ( GUI . connect_lock ) {
112+ console . log ( `${ firmware_flasher . logHead } Skipping auto-detect due to active operation at timeout` ) ;
113+ return ;
114+ }
115+ AutoDetect . verifyBoard ( ) ;
116+ } , PORT_CHANGE_DEBOUNCE_MS ) ; // Small delay to ensure port is ready
117+ } else if ( ! port || port === "0" ) {
118+ if ( ! GUI . connect_lock ) {
119+ console . log ( `${ firmware_flasher . logHead } Clearing board selection - no port selected` ) ;
120+ $ ( 'select[name="board"]' ) . val ( "0" ) . trigger ( "change" ) ;
121+ } else {
122+ console . log ( `${ firmware_flasher . logHead } Not clearing board selection because operation in progress` ) ;
123+ }
124+ }
125+ } ;
126+
127+ firmware_flasher . onDeviceRemoved = function ( devicePath ) {
128+ console . log ( `${ firmware_flasher . logHead || "[FIRMWARE_FLASHER]" } Device removed:` , devicePath ) ;
129+ $ ( 'select[name="board"]' ) . val ( "0" ) . trigger ( "change" ) ;
130+ firmware_flasher . clearBufferedFirmware ( ) ;
43131} ;
44132
45133firmware_flasher . initialize = async function ( callback ) {
@@ -742,20 +830,16 @@ firmware_flasher.initialize = async function (callback) {
742830 return output . join ( "" ) . split ( "\n" ) ;
743831 }
744832
745- function detectedUsbDevice ( device ) {
746- const isFlashOnConnect = $ ( "input.flash_on_connect" ) . is ( ":checked" ) ;
747-
748- console . log ( `${ self . logHead } Detected USB device:` , device ) ;
749- console . log ( `${ self . logHead } Reboot mode: %s, flash on connect` , STM32 . rebootMode , isFlashOnConnect ) ;
833+ // Wire module-scoped handlers (defined above) into the instance once DOM is ready.
834+ // Expose instance functions used by the module handlers.
835+ firmware_flasher . clearBufferedFirmware = clearBufferedFirmware ;
750836
751- if ( STM32 . rebootMode || isFlashOnConnect ) {
752- STM32 . rebootMode = 0 ;
753- GUI . connect_lock = false ;
754- startFlashing ( ) ;
755- }
756- }
837+ // startFlashing implementation will be registered after its declaration below.
757838
758- EventBus . $on ( "port-handler:auto-select-usb-device" , detectedUsbDevice ) ;
839+ EventBus . $on ( "port-handler:auto-select-usb-device" , firmware_flasher . detectedUsbDevice ) ;
840+ EventBus . $on ( "port-handler:auto-select-serial-device" , firmware_flasher . detectedSerialDevice ) ;
841+ EventBus . $on ( "ports-input:change" , firmware_flasher . onPortChange ) ;
842+ EventBus . $on ( "port-handler:device-removed" , firmware_flasher . onDeviceRemoved ) ;
759843
760844 async function saveFirmware ( ) {
761845 const fileType = self . firmware_type ;
@@ -1389,6 +1473,11 @@ firmware_flasher.initialize = async function (callback) {
13891473 }
13901474 }
13911475
1476+ // Register the implementation so the module-level entrypoint can call it.
1477+ // Assign the concrete implementation directly (bound to this instance)
1478+ // so callers can invoke `firmware_flasher.startFlashing()` without a trampoline.
1479+ firmware_flasher . startFlashing = startFlashing . bind ( self ) ;
1480+
13921481 $ ( "a.flash_firmware" ) . on ( "click" , async function ( ) {
13931482 if ( GUI . connect_lock ) {
13941483 return ;
@@ -1472,6 +1561,17 @@ firmware_flasher.initialize = async function (callback) {
14721561 $ ( "a.exit_dfu" ) . removeClass ( "disabled" ) ;
14731562 }
14741563
1564+ // Auto-detect board if drone is already connected when tab becomes active
1565+ if (
1566+ ( PortHandler . portAvailable && ! $ ( 'select[name="board"]' ) . val ( ) ) ||
1567+ $ ( 'select[name="board"]' ) . val ( ) === "0"
1568+ ) {
1569+ console . log ( `${ self . logHead } Auto-detecting board for already connected device` ) ;
1570+ setTimeout ( ( ) => {
1571+ AutoDetect . verifyBoard ( ) ;
1572+ } , 1000 ) ; // Small delay to ensure tab is fully loaded
1573+ }
1574+
14751575 GUI . content_ready ( callback ) ;
14761576 }
14771577
@@ -1490,6 +1590,24 @@ firmware_flasher.cleanup = function (callback) {
14901590 $ ( document ) . unbind ( "keypress" ) ;
14911591 $ ( document ) . off ( "click" , "span.progressLabel a" ) ;
14921592
1593+ const cleanupHandler = ( evt , property ) => {
1594+ const handler = firmware_flasher [ property ] ;
1595+ if ( handler ) {
1596+ EventBus . $off ( evt , handler ) ;
1597+ }
1598+ } ;
1599+
1600+ cleanupHandler ( "port-handler:auto-select-usb-device" , "detectedUsbDevice" ) ;
1601+ cleanupHandler ( "port-handler:auto-select-serial-device" , "detectedSerialDevice" ) ;
1602+ cleanupHandler ( "ports-input:change" , "onPortChange" ) ;
1603+ cleanupHandler ( "port-handler:device-removed" , "onDeviceRemoved" ) ;
1604+
1605+ // Clear any pending debounce timer so it cannot fire after cleanup
1606+ if ( firmware_flasher . _portChangeTimer ) {
1607+ clearTimeout ( firmware_flasher . _portChangeTimer ) ;
1608+ firmware_flasher . _portChangeTimer = null ;
1609+ }
1610+
14931611 if ( callback ) callback ( ) ;
14941612} ;
14951613
0 commit comments