1
0
Fork 0
mirror of https://github.com/betaflight/betaflight-configurator.git synced 2025-07-25 17:25:16 +03:00

Add manual connection option (#3703)

* Hide manual connection and refactor port_handler

* Rebased
This commit is contained in:
Mark Haslinghuis 2024-05-08 20:01:44 +02:00 committed by GitHub
parent d354567129
commit 6105210e9e
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 129 additions and 110 deletions

View file

@ -55,6 +55,9 @@
"portsSelectManual": { "portsSelectManual": {
"message": "Manual Selection" "message": "Manual Selection"
}, },
"portsSelectNone": {
"message": "No connection available"
},
"portsSelectVirtual": { "portsSelectVirtual": {
"message": "Virtual Mode (Experimental)", "message": "Virtual Mode (Experimental)",
"description": "Configure a Virtual Flight Controller without the need of a physical FC." "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)", "message": "Use mDNS Browser Device discovery on network (experimental)",
"description": "Enable mDNS Browser Device discovery in PortHandler (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": { "showVirtualMode": {
"message": "Enable virtual connection mode", "message": "Enable virtual connection mode",
"description": "Text for the option to enable or disable the virtual FC" "description": "Text for the option to enable or disable the virtual FC"

View file

@ -5,7 +5,6 @@ import { generateVirtualApiVersions, getTextWidth } from './utils/common';
import { get as getConfig } from "./ConfigStorage"; import { get as getConfig } from "./ConfigStorage";
import serial from "./serial"; import serial from "./serial";
import MdnsDiscovery from "./mdns_discovery"; import MdnsDiscovery from "./mdns_discovery";
import $ from 'jquery';
import { isWeb } from "./utils/isWeb"; 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 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 () { const PortHandler = new function () {
this.currentPorts = [];
this.initialPorts = false; this.initialPorts = false;
this.port_detected_callbacks = []; this.port_detected_callbacks = [];
this.port_removed_callbacks = []; this.port_removed_callbacks = [];
@ -26,6 +26,7 @@ const PortHandler = new function () {
this.showAllSerialDevices = false; this.showAllSerialDevices = false;
this.useMdnsBrowser = false; this.useMdnsBrowser = false;
this.showVirtualMode = false; this.showVirtualMode = false;
this.showManualMode = false;
}; };
PortHandler.initialize = function () { PortHandler.initialize = function () {
@ -56,6 +57,7 @@ PortHandler.reinitialize = function () {
} }
this.showVirtualMode = getConfig('showVirtualMode').showVirtualMode; this.showVirtualMode = getConfig('showVirtualMode').showVirtualMode;
this.showManualMode = getConfig('showManualMode').showManualMode;
this.showAllSerialDevices = getConfig('showAllSerialDevices').showAllSerialDevices; this.showAllSerialDevices = getConfig('showAllSerialDevices').showAllSerialDevices;
this.useMdnsBrowser = getConfig('useMdnsBrowser').useMdnsBrowser; this.useMdnsBrowser = getConfig('useMdnsBrowser').useMdnsBrowser;
@ -86,10 +88,10 @@ PortHandler.check_serial_devices = function () {
const self = this; const self = this;
serial.getDevices(function(cp) { serial.getDevices(function(cp) {
let currentPorts = []; self.currentPorts = [];
if (self.useMdnsBrowser) { if (self.useMdnsBrowser) {
currentPorts = [ self.currentPorts = [
...cp, ...cp,
...(MdnsDiscovery.mdnsBrowser.services?.filter(s => s.txt?.vendor === 'elrs' && s.txt?.type === 'rx' && s.ready === true) ...(MdnsDiscovery.mdnsBrowser.services?.filter(s => s.txt?.vendor === 'elrs' && s.txt?.type === 'rx' && s.ready === true)
.map(s => s.addresses.map(a => ({ .map(s => s.addresses.map(a => ({
@ -101,36 +103,35 @@ PortHandler.check_serial_devices = function () {
}))).flat() ?? []), }))).flat() ?? []),
].filter(Boolean); ].filter(Boolean);
} else { } else {
currentPorts = cp; self.currentPorts = cp;
} }
// auto-select port (only during initialization) // auto-select port (only during initialization)
if (!self.initialPorts) { if (!self.initialPorts) {
currentPorts = self.updatePortSelect(currentPorts); self.updatePortSelect(self.currentPorts);
self.selectPort(currentPorts); self.selectActivePort();
self.initialPorts = currentPorts; self.initialPorts = self.currentPorts;
GUI.updateManualPortVisibility(); GUI.updateManualPortVisibility();
} else { } else {
self.removePort(currentPorts); self.removePort();
self.detectPort(currentPorts); self.detectPort();
} }
}); });
}; };
PortHandler.check_usb_devices = function (callback) { PortHandler.check_usb_devices = function (callback) {
const self = this; const self = this;
chrome.usb.getDevices(usbDevices, function (result) { chrome.usb.getDevices(usbDevices, function (result) {
const dfuElement = self.portPickerElement.children("[value='DFU']"); const dfuElement = self.portPickerElement.children("[value='DFU']");
if (result.length) { if (result.length) {
// Found device in DFU mode, add it to the list
if (!dfuElement.length) { if (!dfuElement.length) {
self.portPickerElement.empty(); self.portPickerElement.empty();
let usbText;
if (result[0].productName) { const productName = result[0].productName;
usbText = (`DFU - ${result[0].productName}`); const usbText = productName ? `DFU - ${productName}` : 'DFU';
} else {
usbText = "DFU";
}
self.portPickerElement.append($('<option/>', { self.portPickerElement.append($('<option/>', {
value: "DFU", value: "DFU",
@ -176,21 +177,17 @@ PortHandler.check_usb_devices = function (callback) {
}); });
}; };
PortHandler.removePort = function(currentPorts) { PortHandler.removePort = function() {
const self = this; const self = this;
const removePorts = self.array_difference(self.initialPorts, currentPorts); const removePorts = self.array_difference(self.initialPorts, self.currentPorts);
if (removePorts.length) { if (removePorts.length) {
console.log(`PortHandler - Removed: ${JSON.stringify(removePorts)}`); console.log(`PortHandler - Removed: ${JSON.stringify(removePorts)}`);
self.port_available = false; self.port_available = false;
// disconnect "UI" - routine can't fire during atmega32u4 reboot procedure !!! // disconnect "UI" - routine can't fire during atmega32u4 reboot procedure !!!
if (GUI.connected_to) { if (removePorts.some(port => port.path === GUI.connected_to)) {
for (let i = 0; i < removePorts.length; i++) { $('div.connect_controls a.connect').click();
if (removePorts[i].path === GUI.connected_to) { $('div.connect_controls a.connect.active').click();
$('div.connect_controls a.connect').click();
$('div.connect_controls a.connect.active').click();
}
}
} }
// trigger callbacks (only after initialization) // trigger callbacks (only after initialization)
for (let i = (self.port_removed_callbacks.length - 1); i >= 0; i--) { 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); self.port_removed_callbacks.splice(index, 1);
} }
} }
for (let i = 0; i < removePorts.length; i++) { for (const port of removePorts) {
self.initialPorts.splice(self.initialPorts.indexOf(removePorts[i]), 1); self.initialPorts.splice(self.initialPorts.indexOf(port, 1));
} }
self.updatePortSelect(self.initialPorts); self.updatePortSelect(self.initialPorts);
self.portPickerElement.trigger('change'); self.portPickerElement.trigger('change');
} }
}; };
PortHandler.detectPort = function(currentPorts) { PortHandler.detectPort = function() {
const self = this; const self = this;
const newPorts = self.array_difference(currentPorts, self.initialPorts); const newPorts = self.array_difference(self.currentPorts, self.initialPorts);
if (newPorts.length) { if (newPorts.length) {
currentPorts = self.updatePortSelect(currentPorts); self.updatePortSelect(self.currentPorts);
console.log(`PortHandler - Found: ${JSON.stringify(newPorts)}`); console.log(`PortHandler - Found: ${JSON.stringify(newPorts)}`);
if (newPorts.length === 1) { if (newPorts.length === 1) {
self.portPickerElement.val(newPorts[0].path); self.portPickerElement.val(newPorts[0].path);
} else if (newPorts.length > 1) { } else if (newPorts.length > 1) {
self.selectPort(currentPorts); self.selectActivePort();
} }
self.port_available = true; self.port_available = true;
@ -239,11 +236,9 @@ PortHandler.detectPort = function(currentPorts) {
self.portPickerElement.trigger('change'); self.portPickerElement.trigger('change');
// auto-connect if enabled // 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 // 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 // trigger callbacks
for (let i = (self.port_detected_callbacks.length - 1); i >= 0; i--) { 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.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($("<option/>", {
value: 'none',
text: i18n.getMessage('portsSelectNone'),
}));
}
};
PortHandler.updatePortSelect = function (ports) { PortHandler.updatePortSelect = function (ports) {
ports = this.sortPorts(ports); ports = this.sortPorts(ports);
this.portPickerElement.empty(); this.portPickerElement.empty();
for (let i = 0; i < ports.length; i++) { for (const port of ports) {
let portText; const portText = port.displayName ? `${port.path} - ${port.displayName}` : port.path;
if (ports[i].displayName) {
portText = (`${ports[i].path} - ${ports[i].displayName}`);
} else {
portText = ports[i].path;
}
this.portPickerElement.append($("<option/>", { this.portPickerElement.append($("<option/>", {
value: ports[i].path, value: port.path,
text: portText, text: portText,
/** /**
* @deprecated please avoid using `isDFU` and friends for new code. * @deprecated please avoid using `isDFU` and friends for new code.
@ -307,20 +306,27 @@ PortHandler.updatePortSelect = function (ports) {
})); }));
} }
this.portPickerElement.append($("<option/>", { if (this.showManualMode) {
value: 'manual', this.portPickerElement.append($("<option/>", {
text: i18n.getMessage('portsSelectManual'), value: 'manual',
/** text: i18n.getMessage('portsSelectManual'),
* @deprecated please avoid using `isDFU` and friends for new code. /**
*/ * @deprecated please avoid using `isDFU` and friends for new code.
data: {isManual: true}, */
})); data: {isManual: true},
}));
}
if (!ports.length) {
this.addNoPortSelection();
}
this.setPortsInputWidth(); this.setPortsInputWidth();
return ports; this.currentPorts = ports;
}; };
PortHandler.selectPort = function(ports) { PortHandler.selectActivePort = function() {
const ports = this.currentPorts;
const OS = GUI.operating_system; const OS = GUI.operating_system;
for (let i = 0; i < ports.length; i++) { for (let i = 0; i < ports.length; i++) {
const portName = ports[i].displayName; const portName = ports[i].displayName;

View file

@ -55,18 +55,9 @@ export function initializeSerialBackend() {
} }
const selected_port = $('#port').val(); const selected_port = $('#port').val();
if (selected_port === 'manual') { $('#port-override-option').toggle(selected_port === 'manual');
$('#port-override-option').show();
} $('#firmware-virtual-option').toggle(selected_port === 'virtual');
else {
$('#port-override-option').hide();
}
if (selected_port === 'virtual') {
$('#firmware-virtual-option').show();
}
else {
$('#firmware-virtual-option').hide();
}
$('#auto-connect-and-baud').toggle(selected_port !== 'DFU'); $('#auto-connect-and-baud').toggle(selected_port !== 'DFU');
}; };
@ -97,7 +88,7 @@ export function initializeSerialBackend() {
portName = String($('div#port-picker #port').val()); portName = String($('div#port-picker #port').val());
} }
if (!GUI.connect_lock) { if (!GUI.connect_lock && selectedPort !== 'none') {
// GUI control overrides the user control // GUI control overrides the user control
GUI.configuration_loaded = false; GUI.configuration_loaded = false;
@ -107,58 +98,55 @@ export function initializeSerialBackend() {
if (selectedPort === 'DFU') { if (selectedPort === 'DFU') {
$('select#baud').hide(); $('select#baud').hide();
} else if (portName !== '0') { return;
if (!isConnected) { }
console.log(`Connecting to: ${portName}`);
GUI.connecting_to = portName;
// lock port select & baud while we are connecting / connected if (!isConnected) {
$('div#port-picker #port, div#port-picker #baud, div#port-picker #delay').prop('disabled', true); console.log(`Connecting to: ${portName}`);
$('div.connect_controls div.connect_state').text(i18n.getMessage('connecting')); GUI.connecting_to = portName;
const baudRate = parseInt($('#baud').val()); // lock port select & baud while we are connecting / connected
if (selectedPort === 'virtual') { $('div#port-picker #port, div#port-picker #baud, div#port-picker #delay').prop('disabled', true);
CONFIGURATOR.virtualMode = true; $('div.connect_controls div.connect_state').text(i18n.getMessage('connecting'));
CONFIGURATOR.virtualApiVersion = $('#firmware-version-dropdown').val();
// Hack to get virtual working on the web const baudRate = parseInt($('#baud').val());
serial = serialShim(); if (selectedPort === 'virtual') {
serial.connect('virtual', {}, onOpenVirtual); CONFIGURATOR.virtualMode = true;
} else if (isWeb()) { CONFIGURATOR.virtualApiVersion = $('#firmware-version-dropdown').val();
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); // Hack to get virtual working on the web
serial.addEventListener('disconnect', disconnectHandler); 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.connect({ baudRate }); serial.removeEventListener('disconnect', disconnectHandler);
} else { serial.addEventListener('disconnect', disconnectHandler);
serial.connect(
portName,
{ bitrate: selected_baud },
onOpen,
);
toggleStatus();
}
serial.connect({ baudRate });
} else { } else {
if ($('div#flashbutton a.flash_state').hasClass('active') && $('div#flashbutton a.flash').hasClass('active')) { serial.connect(portName, { bitrate: selected_baud }, onOpen);
$('div#flashbutton a.flash_state').removeClass('active'); toggleStatus();
$('div#flashbutton a.flash').removeClass('active');
}
GUI.timeout_kill_all();
GUI.interval_kill_all();
GUI.tab_switch_cleanup(() => GUI.tab_switch_in_progress = false);
function onFinishCallback() {
finishClose(toggleStatus);
}
mspHelper?.setArmingEnabled(true, false, onFinishCallback);
} }
} 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);
function onFinishCallback() {
finishClose(toggleStatus);
}
mspHelper?.setArmingEnabled(true, false, onFinishCallback);
} }
} }
}); });

View file

@ -25,6 +25,7 @@ options.initialize = function (callback) {
TABS.options.initShowAllSerialDevices(); TABS.options.initShowAllSerialDevices();
TABS.options.initUseMdnsBrowser(); TABS.options.initUseMdnsBrowser();
TABS.options.initShowVirtualMode(); TABS.options.initShowVirtualMode();
TABS.options.initUseManualConnection();
TABS.options.initCordovaForceComputerUI(); TABS.options.initCordovaForceComputerUI();
TABS.options.initDarkTheme(); TABS.options.initDarkTheme();
TABS.options.initShowDevToolsOnStartup(); 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() { options.initUseMdnsBrowser = function() {
const useMdnsBrowserElement = $('div.useMdnsBrowser input'); const useMdnsBrowserElement = $('div.useMdnsBrowser input');
const result = getConfig('useMdnsBrowser'); const result = getConfig('useMdnsBrowser');

View file

@ -41,6 +41,12 @@
</div> </div>
<span class="freelabel" i18n="useMdnsBrowser"></span> <span class="freelabel" i18n="useMdnsBrowser"></span>
</div> </div>
<div class="showManualMode margin-bottom">
<div>
<input type="checkbox" class="toggle" />
</div>
<span class="freelabel" i18n="showManualMode"></span>
</div>
<div class="showVirtualMode margin-bottom"> <div class="showVirtualMode margin-bottom">
<div> <div>
<input type="checkbox" class="toggle" /> <input type="checkbox" class="toggle" />