@@ -40,6 +40,99 @@ 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+ logHead : "[FIRMWARE_FLASHER]" ,
51+ } ;
52+
53+ // Move port/device handlers to module scope for easier maintenance and testing.
54+ // These handlers reference the `firmware_flasher` object and shared imports (STM32, AutoDetect, GUI).
55+ const PORT_CHANGE_DEBOUNCE_MS = 500 ;
56+
57+ function handleDetectedDevice ( device , isSerial ) {
58+ const isFlashOnConnect = $ ( "input.flash_on_connect" ) . is ( ":checked" ) ;
59+
60+ console . log ( `${ firmware_flasher . logHead } Detected ${ isSerial ? "serial" : "USB" } device:` , device ) ;
61+ console . log ( `${ firmware_flasher . logHead } Reboot mode: %s, flash on connect` , STM32 . rebootMode , isFlashOnConnect ) ;
62+
63+ // If another operation is in progress, ignore port events (unless we're resuming from a reboot)
64+ if ( GUI . connect_lock && ! STM32 . rebootMode ) {
65+ console . log ( `${ firmware_flasher . logHead } Port event ignored due to active operation (connect_lock)` ) ;
66+ return ;
67+ }
68+
69+ // Proceed if we're resuming a reboot sequence or if flash-on-connect is enabled and no operation is active
70+ if ( STM32 . rebootMode || ( isFlashOnConnect && ! GUI . connect_lock ) ) {
71+ // Remember whether we were in reboot mode before clearing it.
72+ const wasReboot = Boolean ( STM32 . rebootMode ) ;
73+ STM32 . rebootMode = 0 ;
74+ // Only clear the global connect lock when we are resuming from a reboot sequence.
75+ // If wasReboot is true, this clears connect_lock; otherwise, it leaves it unchanged.
76+ GUI . connect_lock &&= ! wasReboot ;
77+ firmware_flasher . startFlashing ( ) ;
78+ } else {
79+ if ( isSerial ) {
80+ // Auto-detect board when firmware flasher tab is active and no flash-on-connect
81+ console . log ( `${ firmware_flasher . logHead } Auto-detecting board for connected serial device` ) ;
82+ }
83+ AutoDetect . verifyBoard ( ) ;
84+ }
85+ }
86+
87+ firmware_flasher . detectedUsbDevice = function ( device ) {
88+ handleDetectedDevice ( device , false ) ;
89+ } ;
90+
91+ firmware_flasher . detectedSerialDevice = function ( device ) {
92+ handleDetectedDevice ( device , true ) ;
93+ } ;
94+
95+ firmware_flasher . onPortChange = function ( port ) {
96+ // Clear any pending debounce timer so rapid port events don't re-enter the handler.
97+ if ( firmware_flasher . _portChangeTimer ) {
98+ clearTimeout ( firmware_flasher . _portChangeTimer ) ;
99+ firmware_flasher . _portChangeTimer = null ;
100+ }
101+
102+ console . log ( `${ firmware_flasher . logHead } Port changed to:` , port ) ;
103+
104+ if ( GUI . connect_lock ) {
105+ console . log ( `${ firmware_flasher . logHead } Port change ignored during active operation (connect_lock set)` ) ;
106+ return ;
107+ }
108+
109+ // Auto-detect board when port changes and we're on firmware flasher tab
110+ if ( port && port !== "0" && $ ( "input.flash_on_connect" ) . is ( ":checked" ) === false && ! STM32 . rebootMode ) {
111+ console . log ( `${ firmware_flasher . logHead } Auto-detecting board for port change (debounced)` ) ;
112+
113+ // Debounced verification: re-check connect lock when the timeout fires
114+ firmware_flasher . _portChangeTimer = setTimeout ( ( ) => {
115+ firmware_flasher . _portChangeTimer = null ;
116+ if ( GUI . connect_lock ) {
117+ console . log ( `${ firmware_flasher . logHead } Skipping auto-detect due to active operation at timeout` ) ;
118+ return ;
119+ }
120+ AutoDetect . verifyBoard ( ) ;
121+ } , PORT_CHANGE_DEBOUNCE_MS ) ; // Small delay to ensure port is ready
122+ } else if ( ! port || port === "0" ) {
123+ if ( ! GUI . connect_lock ) {
124+ console . log ( `${ firmware_flasher . logHead } Clearing board selection - no port selected` ) ;
125+ $ ( 'select[name="board"]' ) . val ( "0" ) . trigger ( "change" ) ;
126+ } else {
127+ console . log ( `${ firmware_flasher . logHead } Not clearing board selection because operation in progress` ) ;
128+ }
129+ }
130+ } ;
131+
132+ firmware_flasher . onDeviceRemoved = function ( devicePath ) {
133+ console . log ( `${ firmware_flasher . logHead } Device removed:` , devicePath ) ;
134+ $ ( 'select[name="board"]' ) . val ( "0" ) . trigger ( "change" ) ;
135+ firmware_flasher . clearBufferedFirmware ( ) ;
43136} ;
44137
45138firmware_flasher . initialize = async function ( callback ) {
@@ -60,8 +153,6 @@ firmware_flasher.initialize = async function (callback) {
60153 self . intel_hex = undefined ;
61154 self . parsed_hex = undefined ;
62155
63- self . logHead = "[FIRMWARE_FLASHER]" ;
64-
65156 function getExtension ( key ) {
66157 if ( ! key ) {
67158 return undefined ;
@@ -742,20 +833,16 @@ firmware_flasher.initialize = async function (callback) {
742833 return output . join ( "" ) . split ( "\n" ) ;
743834 }
744835
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 ) ;
836+ // Wire module-scoped handlers (defined above) into the instance once DOM is ready.
837+ // Expose instance functions used by the module handlers.
838+ firmware_flasher . clearBufferedFirmware = clearBufferedFirmware ;
750839
751- if ( STM32 . rebootMode || isFlashOnConnect ) {
752- STM32 . rebootMode = 0 ;
753- GUI . connect_lock = false ;
754- startFlashing ( ) ;
755- }
756- }
840+ // startFlashing implementation will be registered after its declaration below.
757841
758- EventBus . $on ( "port-handler:auto-select-usb-device" , detectedUsbDevice ) ;
842+ EventBus . $on ( "port-handler:auto-select-usb-device" , firmware_flasher . detectedUsbDevice ) ;
843+ EventBus . $on ( "port-handler:auto-select-serial-device" , firmware_flasher . detectedSerialDevice ) ;
844+ EventBus . $on ( "ports-input:change" , firmware_flasher . onPortChange ) ;
845+ EventBus . $on ( "port-handler:device-removed" , firmware_flasher . onDeviceRemoved ) ;
759846
760847 async function saveFirmware ( ) {
761848 const fileType = self . firmware_type ;
@@ -1389,6 +1476,11 @@ firmware_flasher.initialize = async function (callback) {
13891476 }
13901477 }
13911478
1479+ // Register the implementation so the module-level entrypoint can call it.
1480+ // Assign the concrete implementation directly (bound to this instance)
1481+ // so callers can invoke `firmware_flasher.startFlashing()` without a trampoline.
1482+ firmware_flasher . startFlashing = startFlashing . bind ( self ) ;
1483+
13921484 $ ( "a.flash_firmware" ) . on ( "click" , async function ( ) {
13931485 if ( GUI . connect_lock ) {
13941486 return ;
@@ -1472,6 +1564,17 @@ firmware_flasher.initialize = async function (callback) {
14721564 $ ( "a.exit_dfu" ) . removeClass ( "disabled" ) ;
14731565 }
14741566
1567+ // Auto-detect board if drone is already connected when tab becomes active
1568+ if (
1569+ ( PortHandler . portAvailable && ! $ ( 'select[name="board"]' ) . val ( ) ) ||
1570+ $ ( 'select[name="board"]' ) . val ( ) === "0"
1571+ ) {
1572+ console . log ( `${ self . logHead } Auto-detecting board for already connected device` ) ;
1573+ setTimeout ( ( ) => {
1574+ AutoDetect . verifyBoard ( ) ;
1575+ } , 1000 ) ; // Small delay to ensure tab is fully loaded
1576+ }
1577+
14751578 GUI . content_ready ( callback ) ;
14761579 }
14771580
@@ -1490,6 +1593,24 @@ firmware_flasher.cleanup = function (callback) {
14901593 $ ( document ) . unbind ( "keypress" ) ;
14911594 $ ( document ) . off ( "click" , "span.progressLabel a" ) ;
14921595
1596+ const cleanupHandler = ( evt , property ) => {
1597+ const handler = firmware_flasher [ property ] ;
1598+ if ( handler ) {
1599+ EventBus . $off ( evt , handler ) ;
1600+ }
1601+ } ;
1602+
1603+ cleanupHandler ( "port-handler:auto-select-usb-device" , "detectedUsbDevice" ) ;
1604+ cleanupHandler ( "port-handler:auto-select-serial-device" , "detectedSerialDevice" ) ;
1605+ cleanupHandler ( "ports-input:change" , "onPortChange" ) ;
1606+ cleanupHandler ( "port-handler:device-removed" , "onDeviceRemoved" ) ;
1607+
1608+ // Clear any pending debounce timer so it cannot fire after cleanup
1609+ if ( firmware_flasher . _portChangeTimer ) {
1610+ clearTimeout ( firmware_flasher . _portChangeTimer ) ;
1611+ firmware_flasher . _portChangeTimer = null ;
1612+ }
1613+
14931614 if ( callback ) callback ( ) ;
14941615} ;
14951616
0 commit comments