diff --git a/locales/en/messages.json b/locales/en/messages.json index 476e9230f3..a956439b53 100755 --- a/locales/en/messages.json +++ b/locales/en/messages.json @@ -55,6 +55,9 @@ "portsSelectManual": { "message": "Manual Selection" }, + "portsSelectNone": { + "message": "No connection available" + }, "portsSelectVirtual": { "message": "Virtual Mode (Experimental)", "description": "Configure a Virtual Flight Controller without the need of a physical FC." @@ -113,6 +116,10 @@ "message": "Use mDNS Browser Device discovery on network (experimental)", "description": "Enable mDNS Browser Device discovery in PortHandler (experimental)" }, + "showManualMode": { + "message": "Enable manual connection mode", + "description": "Text for the option to enable or disable manual connection mode" + }, "showVirtualMode": { "message": "Enable virtual connection mode", "description": "Text for the option to enable or disable the virtual FC" diff --git a/src/js/port_handler.js b/src/js/port_handler.js index e1dafe7fd2..da1cb87678 100644 --- a/src/js/port_handler.js +++ b/src/js/port_handler.js @@ -5,7 +5,6 @@ import { generateVirtualApiVersions, getTextWidth } from './utils/common'; import { get as getConfig } from "./ConfigStorage"; import serial from "./serial"; import MdnsDiscovery from "./mdns_discovery"; -import $ from 'jquery'; import { isWeb } from "./utils/isWeb"; const TIMEOUT_CHECK = 500 ; // With 250 it seems that it produces a memory leak and slowdown in some versions, reason unknown @@ -18,6 +17,7 @@ export const usbDevices = { filters: [ ] }; const PortHandler = new function () { + this.currentPorts = []; this.initialPorts = false; this.port_detected_callbacks = []; this.port_removed_callbacks = []; @@ -26,6 +26,7 @@ const PortHandler = new function () { this.showAllSerialDevices = false; this.useMdnsBrowser = false; this.showVirtualMode = false; + this.showManualMode = false; }; PortHandler.initialize = function () { @@ -56,6 +57,7 @@ PortHandler.reinitialize = function () { } this.showVirtualMode = getConfig('showVirtualMode').showVirtualMode; + this.showManualMode = getConfig('showManualMode').showManualMode; this.showAllSerialDevices = getConfig('showAllSerialDevices').showAllSerialDevices; this.useMdnsBrowser = getConfig('useMdnsBrowser').useMdnsBrowser; @@ -86,10 +88,10 @@ PortHandler.check_serial_devices = function () { const self = this; serial.getDevices(function(cp) { - let currentPorts = []; + self.currentPorts = []; if (self.useMdnsBrowser) { - currentPorts = [ + self.currentPorts = [ ...cp, ...(MdnsDiscovery.mdnsBrowser.services?.filter(s => s.txt?.vendor === 'elrs' && s.txt?.type === 'rx' && s.ready === true) .map(s => s.addresses.map(a => ({ @@ -101,36 +103,35 @@ PortHandler.check_serial_devices = function () { }))).flat() ?? []), ].filter(Boolean); } else { - currentPorts = cp; + self.currentPorts = cp; } // auto-select port (only during initialization) if (!self.initialPorts) { - currentPorts = self.updatePortSelect(currentPorts); - self.selectPort(currentPorts); - self.initialPorts = currentPorts; + self.updatePortSelect(self.currentPorts); + self.selectActivePort(); + self.initialPorts = self.currentPorts; GUI.updateManualPortVisibility(); } else { - self.removePort(currentPorts); - self.detectPort(currentPorts); + self.removePort(); + self.detectPort(); } }); }; PortHandler.check_usb_devices = function (callback) { const self = this; + chrome.usb.getDevices(usbDevices, function (result) { const dfuElement = self.portPickerElement.children("[value='DFU']"); if (result.length) { + // Found device in DFU mode, add it to the list if (!dfuElement.length) { self.portPickerElement.empty(); - let usbText; - if (result[0].productName) { - usbText = (`DFU - ${result[0].productName}`); - } else { - usbText = "DFU"; - } + + const productName = result[0].productName; + const usbText = productName ? `DFU - ${productName}` : 'DFU'; self.portPickerElement.append($('', { value: "DFU", @@ -176,21 +177,17 @@ PortHandler.check_usb_devices = function (callback) { }); }; -PortHandler.removePort = function(currentPorts) { +PortHandler.removePort = function() { const self = this; - const removePorts = self.array_difference(self.initialPorts, currentPorts); + const removePorts = self.array_difference(self.initialPorts, self.currentPorts); if (removePorts.length) { console.log(`PortHandler - Removed: ${JSON.stringify(removePorts)}`); self.port_available = false; // disconnect "UI" - routine can't fire during atmega32u4 reboot procedure !!! - if (GUI.connected_to) { - for (let i = 0; i < removePorts.length; i++) { - if (removePorts[i].path === GUI.connected_to) { - $('div.connect_controls a.connect').click(); - $('div.connect_controls a.connect.active').click(); - } - } + if (removePorts.some(port => port.path === GUI.connected_to)) { + $('div.connect_controls a.connect').click(); + $('div.connect_controls a.connect.active').click(); } // trigger callbacks (only after initialization) for (let i = (self.port_removed_callbacks.length - 1); i >= 0; i--) { @@ -208,26 +205,26 @@ PortHandler.removePort = function(currentPorts) { self.port_removed_callbacks.splice(index, 1); } } - for (let i = 0; i < removePorts.length; i++) { - self.initialPorts.splice(self.initialPorts.indexOf(removePorts[i]), 1); + for (const port of removePorts) { + self.initialPorts.splice(self.initialPorts.indexOf(port, 1)); } self.updatePortSelect(self.initialPorts); self.portPickerElement.trigger('change'); } }; -PortHandler.detectPort = function(currentPorts) { +PortHandler.detectPort = function() { const self = this; - const newPorts = self.array_difference(currentPorts, self.initialPorts); + const newPorts = self.array_difference(self.currentPorts, self.initialPorts); if (newPorts.length) { - currentPorts = self.updatePortSelect(currentPorts); + self.updatePortSelect(self.currentPorts); console.log(`PortHandler - Found: ${JSON.stringify(newPorts)}`); if (newPorts.length === 1) { self.portPickerElement.val(newPorts[0].path); } else if (newPorts.length > 1) { - self.selectPort(currentPorts); + self.selectActivePort(); } self.port_available = true; @@ -239,11 +236,9 @@ PortHandler.detectPort = function(currentPorts) { self.portPickerElement.trigger('change'); // auto-connect if enabled - if (GUI.auto_connect && !GUI.connecting_to && !GUI.connected_to) { + if (GUI.auto_connect && !GUI.connecting_to && !GUI.connected_to && GUI.active_tab !== 'firmware_flasher') { // start connect procedure. We need firmware flasher protection over here - if (GUI.active_tab !== 'firmware_flasher') { - $('div.connect_controls a.connect').click(); - } + $('div.connect_controls a.connect').click(); } // trigger callbacks for (let i = (self.port_detected_callbacks.length - 1); i >= 0; i--) { @@ -261,7 +256,7 @@ PortHandler.detectPort = function(currentPorts) { self.port_detected_callbacks.splice(index, 1); } } - self.initialPorts = currentPorts; + self.initialPorts = self.currentPorts; } }; @@ -274,20 +269,24 @@ PortHandler.sortPorts = function(ports) { }); }; +PortHandler.addNoPortSelection = function() { + if (!this.showVirtualMode && !this.showManualMode) { + this.portPickerElement.append($("", { + value: 'none', + text: i18n.getMessage('portsSelectNone'), + })); + } +}; + PortHandler.updatePortSelect = function (ports) { ports = this.sortPorts(ports); this.portPickerElement.empty(); - for (let i = 0; i < ports.length; i++) { - let portText; - if (ports[i].displayName) { - portText = (`${ports[i].path} - ${ports[i].displayName}`); - } else { - portText = ports[i].path; - } + for (const port of ports) { + const portText = port.displayName ? `${port.path} - ${port.displayName}` : port.path; this.portPickerElement.append($("", { - value: ports[i].path, + value: port.path, text: portText, /** * @deprecated please avoid using `isDFU` and friends for new code. @@ -307,20 +306,27 @@ PortHandler.updatePortSelect = function (ports) { })); } - this.portPickerElement.append($("", { - value: 'manual', - text: i18n.getMessage('portsSelectManual'), - /** - * @deprecated please avoid using `isDFU` and friends for new code. - */ - data: {isManual: true}, - })); + if (this.showManualMode) { + this.portPickerElement.append($("", { + value: 'manual', + text: i18n.getMessage('portsSelectManual'), + /** + * @deprecated please avoid using `isDFU` and friends for new code. + */ + data: {isManual: true}, + })); + } + + if (!ports.length) { + this.addNoPortSelection(); + } this.setPortsInputWidth(); - return ports; + this.currentPorts = ports; }; -PortHandler.selectPort = function(ports) { +PortHandler.selectActivePort = function() { + const ports = this.currentPorts; const OS = GUI.operating_system; for (let i = 0; i < ports.length; i++) { const portName = ports[i].displayName; diff --git a/src/js/serial_backend.js b/src/js/serial_backend.js index 6384fca35b..dc17175bce 100644 --- a/src/js/serial_backend.js +++ b/src/js/serial_backend.js @@ -55,18 +55,9 @@ export function initializeSerialBackend() { } const selected_port = $('#port').val(); - if (selected_port === 'manual') { - $('#port-override-option').show(); - } - else { - $('#port-override-option').hide(); - } - if (selected_port === 'virtual') { - $('#firmware-virtual-option').show(); - } - else { - $('#firmware-virtual-option').hide(); - } + $('#port-override-option').toggle(selected_port === 'manual'); + + $('#firmware-virtual-option').toggle(selected_port === 'virtual'); $('#auto-connect-and-baud').toggle(selected_port !== 'DFU'); }; @@ -97,7 +88,7 @@ export function initializeSerialBackend() { portName = String($('div#port-picker #port').val()); } - if (!GUI.connect_lock) { + if (!GUI.connect_lock && selectedPort !== 'none') { // GUI control overrides the user control GUI.configuration_loaded = false; @@ -107,58 +98,55 @@ export function initializeSerialBackend() { if (selectedPort === 'DFU') { $('select#baud').hide(); - } else if (portName !== '0') { - if (!isConnected) { - console.log(`Connecting to: ${portName}`); - GUI.connecting_to = portName; - - // lock port select & baud while we are connecting / connected - $('div#port-picker #port, div#port-picker #baud, div#port-picker #delay').prop('disabled', true); - $('div.connect_controls div.connect_state').text(i18n.getMessage('connecting')); - - const baudRate = parseInt($('#baud').val()); - if (selectedPort === 'virtual') { - CONFIGURATOR.virtualMode = true; - CONFIGURATOR.virtualApiVersion = $('#firmware-version-dropdown').val(); - - // Hack to get virtual working on the web - serial = serialShim(); - serial.connect('virtual', {}, onOpenVirtual); - } else if (isWeb()) { - CONFIGURATOR.virtualMode = false; - serial = serialShim(); - // Explicitly disconnect the event listeners before attaching the new ones. - serial.removeEventListener('connect', connectHandler); - serial.addEventListener('connect', connectHandler); - - serial.removeEventListener('disconnect', disconnectHandler); - serial.addEventListener('disconnect', disconnectHandler); - - serial.connect({ baudRate }); - } else { - serial.connect( - portName, - { bitrate: selected_baud }, - onOpen, - ); - toggleStatus(); - } + return; + } + if (!isConnected) { + console.log(`Connecting to: ${portName}`); + GUI.connecting_to = portName; + + // lock port select & baud while we are connecting / connected + $('div#port-picker #port, div#port-picker #baud, div#port-picker #delay').prop('disabled', true); + $('div.connect_controls div.connect_state').text(i18n.getMessage('connecting')); + + const baudRate = parseInt($('#baud').val()); + if (selectedPort === 'virtual') { + CONFIGURATOR.virtualMode = true; + CONFIGURATOR.virtualApiVersion = $('#firmware-version-dropdown').val(); + + // Hack to get virtual working on the web + serial = serialShim(); + serial.connect('virtual', {}, onOpenVirtual); + } else if (isWeb()) { + CONFIGURATOR.virtualMode = false; + serial = serialShim(); + // Explicitly disconnect the event listeners before attaching the new ones. + serial.removeEventListener('connect', connectHandler); + serial.addEventListener('connect', connectHandler); + + serial.removeEventListener('disconnect', disconnectHandler); + serial.addEventListener('disconnect', disconnectHandler); + + serial.connect({ baudRate }); } else { - if ($('div#flashbutton a.flash_state').hasClass('active') && $('div#flashbutton a.flash').hasClass('active')) { - $('div#flashbutton a.flash_state').removeClass('active'); - $('div#flashbutton a.flash').removeClass('active'); - } - GUI.timeout_kill_all(); - GUI.interval_kill_all(); - GUI.tab_switch_cleanup(() => GUI.tab_switch_in_progress = false); + serial.connect(portName, { bitrate: selected_baud }, onOpen); + toggleStatus(); + } - function onFinishCallback() { - finishClose(toggleStatus); - } + } else { + if ($('div#flashbutton a.flash_state').hasClass('active') && $('div#flashbutton a.flash').hasClass('active')) { + $('div#flashbutton a.flash_state').removeClass('active'); + $('div#flashbutton a.flash').removeClass('active'); + } + GUI.timeout_kill_all(); + GUI.interval_kill_all(); + GUI.tab_switch_cleanup(() => GUI.tab_switch_in_progress = false); - mspHelper?.setArmingEnabled(true, false, onFinishCallback); + function onFinishCallback() { + finishClose(toggleStatus); } + + mspHelper?.setArmingEnabled(true, false, onFinishCallback); } } }); diff --git a/src/js/tabs/options.js b/src/js/tabs/options.js index 0544cbae92..99ba828e2f 100644 --- a/src/js/tabs/options.js +++ b/src/js/tabs/options.js @@ -25,6 +25,7 @@ options.initialize = function (callback) { TABS.options.initShowAllSerialDevices(); TABS.options.initUseMdnsBrowser(); TABS.options.initShowVirtualMode(); + TABS.options.initUseManualConnection(); TABS.options.initCordovaForceComputerUI(); TABS.options.initDarkTheme(); TABS.options.initShowDevToolsOnStartup(); @@ -145,6 +146,17 @@ options.initShowVirtualMode = function() { }); }; +options.initUseManualConnection = function() { + const showManualModeElement = $('div.showManualMode input'); + const result = getConfig('showManualMode'); + showManualModeElement + .prop('checked', !!result.showManualMode) + .on('change', () => { + setConfig({ showManualMode: showManualModeElement.is(':checked') }); + PortHandler.reinitialize(); + }); +}; + options.initUseMdnsBrowser = function() { const useMdnsBrowserElement = $('div.useMdnsBrowser input'); const result = getConfig('useMdnsBrowser'); diff --git a/src/tabs/options.html b/src/tabs/options.html index 6acbc695e7..c37f81fd99 100644 --- a/src/tabs/options.html +++ b/src/tabs/options.html @@ -41,6 +41,12 @@ +