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

Refactor port handler (#3984)

* Refactor port handler and fix reconnect

* Fix as per review

* Don't auto-connect for virtual or manual

* Fix auto-connect switch state

* Move auto-connect title to the parent div

The checkbox is "hidden" under the switchary
library, so move to the parent to be able to show
it.

* Select active port when request permission port exists before

* Fix retun value for webserial requestPemission
This commit is contained in:
Míguel Ángel Mulero Martínez 2024-05-30 20:10:09 +02:00 committed by GitHub
parent a6e3761c26
commit ff83600a43
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
6 changed files with 228 additions and 386 deletions

View file

@ -44,13 +44,15 @@
</select> </select>
</div> </div>
<div id="auto-connect-and-baud"> <div id="auto-connect-and-baud">
<div id="auto-connect-switch"> <div
id="auto-connect-switch"
:title="value.autoConnect ? $t('autoConnectEnabled') : $t('autoConnectDisabled')"
>
<input <input
id="auto-connect" id="auto-connect"
class="auto_connect togglesmall" class="auto_connect togglesmall"
type="checkbox" type="checkbox"
:value="value.autoConnect" :checked="value.autoConnect"
:title="value.autoConnect ? $t('autoConnectEnabled') : $t('autoConnectDisabled')"
@change="onChangeAutoConnect" @change="onChangeAutoConnect"
> >
<span class="auto_connect"> <span class="auto_connect">

View file

@ -14,7 +14,6 @@ const DEFAULT_BAUDS = 115200;
const PortHandler = new function () { const PortHandler = new function () {
this.currentPorts = []; this.currentPorts = [];
this.initialPorts = false;
this.portPicker = { this.portPicker = {
selectedPort: DEFAULT_PORT, selectedPort: DEFAULT_PORT,
selectedBauds: DEFAULT_BAUDS, selectedBauds: DEFAULT_BAUDS,
@ -23,24 +22,29 @@ const PortHandler = new function () {
autoConnect: getConfig('autoConnect').autoConnect, autoConnect: getConfig('autoConnect').autoConnect,
}; };
this.portPickerDisabled = false; this.portPickerDisabled = false;
this.port_detected_callbacks = []; this.dfuAvailable = false;
this.port_removed_callbacks = []; this.portAvailable = false;
this.dfu_available = false;
this.port_available = false;
this.showAllSerialDevices = false; this.showAllSerialDevices = false;
this.showVirtualMode = getConfig('showVirtualMode').showVirtualMode; this.showVirtualMode = getConfig('showVirtualMode').showVirtualMode;
this.showManualMode = getConfig('showManualMode').showManualMode; this.showManualMode = getConfig('showManualMode').showManualMode;
this.showAllSerialDevices = getConfig('showAllSerialDevices').showAllSerialDevices;
}; };
PortHandler.initialize = function () { PortHandler.initialize = function () {
EventBus.$on('ports-input:request-permission', this.askPermissionPort.bind(this)); EventBus.$on('ports-input:request-permission', this.askSerialPermissionPort.bind(this));
EventBus.$on('ports-input:change', this.onChangeSelectedPort.bind(this)); EventBus.$on('ports-input:change', this.onChangeSelectedPort.bind(this));
serial.addEventListener("addedDevice", this.check_serial_devices.bind(this)); serial.addEventListener("addedDevice", (event) => this.addedSerialDevice(event.detail));
serial.addEventListener("removedDevice", this.check_serial_devices.bind(this)); serial.addEventListener("removedDevice", (event) => this.removedSerialDevice(event.detail));
this.reinitialize(); // just to prevent code redundancy if (!this.portAvailable) {
this.check_usb_devices();
}
if (!this.dfuAvailable) {
this.addedSerialDevice();
}
}; };
PortHandler.setShowVirtualMode = function (showVirtualMode) { PortHandler.setShowVirtualMode = function (showVirtualMode) {
@ -53,57 +57,103 @@ PortHandler.setShowManualMode = function (showManualMode) {
this.selectActivePort(); this.selectActivePort();
}; };
PortHandler.reinitialize = function () { PortHandler.addedSerialDevice = function (device) {
this.initialPorts = false; this.updateCurrentPortsList()
.then(() => {
this.showAllSerialDevices = getConfig('showAllSerialDevices').showAllSerialDevices; const selectedPort = this.selectActivePort(device);
if (!device || selectedPort === device.path) {
this.check(); // start listening, check after TIMEOUT_CHECK ms // Send this event when the port handler auto selects a new device
}; EventBus.$emit('port-handler:auto-select-device', selectedPort);
PortHandler.check = function () {
const self = this;
if (!self.port_available) {
self.check_usb_devices();
}
if (!self.dfu_available) {
self.check_serial_devices();
}
};
PortHandler.check_serial_devices = function () {
const self = this;
const updatePorts = function(cp) {
self.currentPorts = cp;
// auto-select port (only during initialization)
if (!self.initialPorts) {
self.updatePortSelect(self.currentPorts);
self.selectActivePort();
self.initialPorts = {...self.currentPorts};
GUI.updateManualPortVisibility();
self.detectPort();
} else {
self.removePort();
self.detectPort();
// already done in detectPort
// self.selectActivePort();
} }
}; });
};
PortHandler.removedSerialDevice = function (device) {
serial.getDevices().then(updatePorts); this.updateCurrentPortsList()
.then(() => {
if (this.portPicker.selectedPort === device.path) {
this.selectActivePort();
}
});
}; };
PortHandler.onChangeSelectedPort = function(port) { PortHandler.onChangeSelectedPort = function(port) {
this.portPicker.selectedPort = port; this.portPicker.selectedPort = port;
}; };
PortHandler.updateCurrentPortsList = function () {
return serial.getDevices()
.then((ports) => {
ports = this.sortPorts(ports);
this.currentPorts = ports;
});
};
PortHandler.sortPorts = function(ports) {
return ports.sort(function(a, b) {
return a.path.localeCompare(b.path, window.navigator.language, {
numeric: true,
sensitivity: 'base',
});
});
};
PortHandler.askSerialPermissionPort = function() {
serial.requestPermissionDevice()
.then((port) => {
// When giving permission to a new device, the port is selected in the handleNewDevice method, but if the user
// selects a device that had already permission, or cancels the permission request, we need to select the port
// so do it here too
this.selectActivePort(port);
});
};
PortHandler.selectActivePort = function(suggestedDevice) {
// Return the same that is connected
if (serial.connected) {
return serial.getConnectedPort();
}
let selectedPort;
const deviceFilter = ['AT32', 'CP210', 'SPR', 'STM'];
if (suggestedDevice) {
selectedPort = suggestedDevice.path;
this.portAvailable = true;
} else {
for (let port of this.currentPorts) {
const portName = port.displayName;
const pathSelect = port.path;
const deviceRecognized = deviceFilter.some(device => portName.includes(device));
const legacyDeviceRecognized = portName.includes('usb');
if (deviceRecognized || legacyDeviceRecognized) {
selectedPort = pathSelect;
this.portAvailable = true;
console.log(`Porthandler detected device ${portName} on port: ${pathSelect}`);
break;
}
}
if (!selectedPort) {
this.portAvailable = false;
if (this.showVirtualMode) {
selectedPort = "virtual";
} else if (this.showManualMode) {
selectedPort = "manual";
}
}
}
this.portPicker.selectedPort = selectedPort || DEFAULT_PORT;
console.log(`Porthandler default device is '${this.portPicker.selectedPort}'`);
return selectedPort;
};
/************************************
// TODO all the methods from here need to be refactored or removed
************************************/
PortHandler.check_usb_devices = function (callback) { PortHandler.check_usb_devices = function (callback) {
// TODO needs USB code refactor for web // TODO needs USB code refactor for web
@ -144,243 +194,34 @@ PortHandler.check_usb_devices = function (callback) {
self.portPickerElement.val('DFU').trigger('change'); self.portPickerElement.val('DFU').trigger('change');
self.setPortsInputWidth(); self.setPortsInputWidth();
self.dfu_available = true; self.dfuAvailable = true;
} }
} else if (dfuElement.length) { } else if (dfuElement.length) {
dfuElement.remove(); dfuElement.remove();
self.setPortsInputWidth(); self.setPortsInputWidth();
self.dfu_available = false; self.dfuAvailable = false;
if ($('option:selected', self.portPickerElement).val() !== 'DFU') { if ($('option:selected', self.portPickerElement).val() !== 'DFU') {
if (!(GUI.connected_to || GUI.connect_lock)) { if (!(GUI.connected_to || GUI.connect_lock)) {
FC.resetState(); FC.resetState();
} }
if (self.dfu_available) { if (self.dfuAvailable) {
self.portPickerElement.trigger('change'); self.portPickerElement.trigger('change');
} }
} }
} }
if (callback) { if (callback) {
callback(self.dfu_available); callback(self.dfuAvailable);
} }
}); });
}; };
PortHandler.removePort = function() {
const self = this;
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 (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--) {
const obj = self.port_removed_callbacks[i];
// remove timeout
clearTimeout(obj.timer);
// trigger callback
obj.code(removePorts);
// remove object from array
const index = self.port_removed_callbacks.indexOf(obj);
if (index > -1) {
self.port_removed_callbacks.splice(index, 1);
}
}
for (const port of removePorts) {
self.initialPorts.splice(self.initialPorts.indexOf(port, 1));
}
self.updatePortSelect(self.initialPorts);
}
};
PortHandler.detectPort = function() {
const self = this;
const newPorts = self.array_difference(self.currentPorts, self.initialPorts);
if (newPorts.length) {
self.updatePortSelect(self.currentPorts);
console.log(`PortHandler - Found: ${JSON.stringify(newPorts)}`);
if (newPorts.length === 1) {
this.portPicker.selectedPort = newPorts[0].path;
} else {
self.selectActivePort();
}
self.port_available = true;
// auto-connect if enabled
if (this.portPicker.autoConnect && !GUI.connecting_to && !GUI.connected_to && GUI.active_tab !== 'firmware_flasher') {
// start connect procedure. We need firmware flasher protection over here
$('div.connect_controls a.connect').click();
}
// trigger callbacks
for (let i = (self.port_detected_callbacks.length - 1); i >= 0; i--) {
const obj = self.port_detected_callbacks[i];
// remove timeout
clearTimeout(obj.timer);
// trigger callback
obj.code(newPorts);
// remove object from array
const index = self.port_detected_callbacks.indexOf(obj);
if (index > -1) {
self.port_detected_callbacks.splice(index, 1);
}
}
self.initialPorts = self.currentPorts;
}
};
PortHandler.sortPorts = function(ports) {
return ports.sort(function(a, b) {
return a.path.localeCompare(b.path, window.navigator.language, {
numeric: true,
sensitivity: 'base',
});
});
};
PortHandler.updatePortSelect = function (ports) {
ports = this.sortPorts(ports);
this.currentPorts = ports;
};
PortHandler.askPermissionPort = function() {
serial.requestPermissionDevice().then(() => {
this.check_serial_devices();
}).catch(() => {
// In the catch we call the check_serial_devices too to change the request permission option from the select for other
this.check_serial_devices();
});
};
PortHandler.selectActivePort = function() {
let selectedPort;
const deviceFilter = ['AT32', 'CP210', 'SPR', 'STM'];
for (let port of this.currentPorts) {
const portName = port.displayName;
if (portName) {
const pathSelect = port.path;
const deviceRecognized = deviceFilter.some(device => portName.includes(device));
const legacyDeviceRecognized = portName.includes('usb');
if (deviceRecognized || legacyDeviceRecognized) {
selectedPort = pathSelect;
this.port_available = true;
console.log(`Porthandler detected device ${portName} on port: ${pathSelect}`);
}
}
}
if (!selectedPort) {
if (this.showVirtualMode) {
selectedPort = "virtual";
} else if (this.showManualMode) {
selectedPort = "manual";
}
}
this.portPicker.selectedPort = selectedPort || DEFAULT_PORT;
console.log(`Porthandler default device is '${this.portPicker.selectedPort}'`);
};
PortHandler.port_detected = function(name, code, timeout, ignore_timeout) {
const self = this;
const obj = {'name': name,
'code': code,
'timeout': (timeout) ? timeout : 10000,
};
if (!ignore_timeout) {
obj.timer = setTimeout(function() {
console.log(`PortHandler - timeout - ${obj.name}`);
// trigger callback
code(false);
// remove object from array
const index = self.port_detected_callbacks.indexOf(obj);
if (index > -1) {
self.port_detected_callbacks.splice(index, 1);
}
}, (timeout) ? timeout : 10000);
} else {
obj.timer = false;
obj.timeout = false;
}
this.port_detected_callbacks.push(obj);
return obj;
};
PortHandler.port_removed = function (name, code, timeout, ignore_timeout) {
const self = this;
const obj = {'name': name,
'code': code,
'timeout': (timeout) ? timeout : 10000,
};
if (!ignore_timeout) {
obj.timer = setTimeout(function () {
console.log(`PortHandler - timeout - ${obj.name}`);
// trigger callback
code(false);
// remove object from array
const index = self.port_removed_callbacks.indexOf(obj);
if (index > -1) {
self.port_removed_callbacks.splice(index, 1);
}
}, (timeout) ? timeout : 10000);
} else {
obj.timer = false;
obj.timeout = false;
}
this.port_removed_callbacks.push(obj);
return obj;
};
// accepting single level array with "value" as key
PortHandler.array_difference = function (firstArray, secondArray) {
const cloneArray = [];
// create hardcopy
for (let i = 0; i < firstArray.length; i++) {
cloneArray.push(firstArray[i]);
}
for (let i = 0; i < secondArray.length; i++) {
const elementExists = cloneArray.findIndex(element => element.path === secondArray[i].path);
if (elementExists !== -1) {
cloneArray.splice(elementExists, 1);
}
}
return cloneArray;
};
PortHandler.flush_callbacks = function () { PortHandler.flush_callbacks = function () {
let killed = 0; let killed = 0;
for (let i = this.port_detected_callbacks.length - 1; i >= 0; i--) { for (let i = this.port_detected_callbacks?.length - 1; i >= 0; i--) {
if (this.port_detected_callbacks[i].timer) { if (this.port_detected_callbacks[i].timer) {
clearTimeout(this.port_detected_callbacks[i].timer); clearTimeout(this.port_detected_callbacks[i].timer);
} }
@ -389,7 +230,7 @@ PortHandler.flush_callbacks = function () {
killed++; killed++;
} }
for (let i = this.port_removed_callbacks.length - 1; i >= 0; i--) { for (let i = this.port_removed_callbacks?.length - 1; i >= 0; i--) {
if (this.port_removed_callbacks[i].timer) { if (this.port_removed_callbacks[i].timer) {
clearTimeout(this.port_removed_callbacks[i].timer); clearTimeout(this.port_removed_callbacks[i].timer);
} }
@ -401,6 +242,4 @@ PortHandler.flush_callbacks = function () {
return killed; return killed;
}; };
// temp workaround till everything is in modules
window.PortHandler = PortHandler;
export default PortHandler; export default PortHandler;

View file

@ -136,7 +136,7 @@ STM32_protocol.prototype.connect = function (port, baud, hex, options, callback)
// wait until board boots into bootloader mode // wait until board boots into bootloader mode
// MacOs may need 5 seconds delay // MacOs may need 5 seconds delay
function waitForDfu() { function waitForDfu() {
if (PortHandler.dfu_available) { if (PortHandler.dfuAvailable) {
console.log(`DFU available after ${failedAttempts / 10} seconds`); console.log(`DFU available after ${failedAttempts / 10} seconds`);
clearInterval(dfuWaitInterval); clearInterval(dfuWaitInterval);
startFlashing(); startFlashing();

View file

@ -36,6 +36,9 @@ let liveDataRefreshTimerId = false;
let isConnected = false; let isConnected = false;
const REBOOT_CONNECT_MAX_TIME_MS = 10000;
let rebootTimestamp = 0;
const toggleStatus = function () { const toggleStatus = function () {
isConnected = !isConnected; isConnected = !isConnected;
}; };
@ -50,99 +53,31 @@ function disconnectHandler(event) {
} }
export function initializeSerialBackend() { export function initializeSerialBackend() {
GUI.updateManualPortVisibility = function() {
if(isWeb()) {
return;
}
const selected_port = $('#port').val();
$('#port-override-option').toggle(selected_port === 'manual');
$('#firmware-virtual-option').toggle(selected_port === 'virtual');
$('#auto-connect-and-baud').toggle(selected_port !== 'DFU');
};
GUI.updateManualPortVisibility();
// TODO move to Vue
$('#port-override').change(function () { $('#port-override').change(function () {
setConfig({'portOverride': $('#port-override').val()}); setConfig({'portOverride': $('#port-override').val()});
}); });
// TODO move to Vue
const data = getConfig('portOverride'); const data = getConfig('portOverride');
if (data.portOverride) { if (data.portOverride) {
$('#port-override').val(data.portOverride); $('#port-override').val(data.portOverride);
} }
EventBus.$on('ports-input:change', () => GUI.updateManualPortVisibility()); $("div.connect_controls a.connect").on('click', connectDisconnect);
$("div.connect_controls a.connect").on('click', function () { EventBus.$on('port-handler:auto-select-device', function(device) {
if (!GUI.connected_to && !GUI.connecting_to
const selectedPort = PortHandler.portPicker.selectedPort; && ((PortHandler.portPicker.autoConnect && !["manual", "virtual"].includes(device))
let portName; || Date.now() - rebootTimestamp < REBOOT_CONNECT_MAX_TIME_MS)) {
if (selectedPort === 'manual') { connectDisconnect();
portName = $('#port-override').val();
} else {
portName = selectedPort;
} }
});
if (!GUI.connect_lock && selectedPort !== 'none') { serial.addEventListener("removedDevice", (event) => {
// GUI control overrides the user control if (event.detail.path === GUI.connected_to) {
connectDisconnect();
GUI.configuration_loaded = false;
const selected_baud = PortHandler.portPicker.selectedBauds;
const selectedPort = portName;
if (selectedPort === 'DFU') {
$('select#baud').hide();
return;
}
if (!isConnected) {
console.log(`Connecting to: ${portName}`);
GUI.connecting_to = portName;
// lock port select & baud while we are connecting / connected
PortHandler.portPickerDisabled = true;
$('div.connect_controls div.connect_state').text(i18n.getMessage('connecting'));
const baudRate = selected_baud;
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 {
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(portName, { 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);
function onFinishCallback() {
finishClose(toggleStatus);
}
mspHelper?.setArmingEnabled(true, false, onFinishCallback);
}
} }
}); });
@ -158,24 +93,79 @@ export function initializeSerialBackend() {
} }
}); });
// auto-connect
const result = PortHandler.portPicker.autoConnect;
if (result === undefined || result) {
$('input.auto_connect').prop('checked', true);
$('input.auto_connect, span.auto_connect').prop('title', i18n.getMessage('autoConnectEnabled'));
$('select#baud').val(115200).prop('disabled', true);
} else {
$('input.auto_connect').prop('checked', false);
$('input.auto_connect, span.auto_connect').prop('title', i18n.getMessage('autoConnectDisabled'));
}
PortHandler.initialize(); PortHandler.initialize();
PortUsage.initialize(); PortUsage.initialize();
} }
function connectDisconnect() {
const selectedPort = PortHandler.portPicker.selectedPort;
let portName;
if (selectedPort === 'manual') {
portName = PortHandler.portPicker.portOverride;
} else {
portName = selectedPort;
}
if (!GUI.connect_lock && selectedPort !== 'none') {
// GUI control overrides the user control
GUI.configuration_loaded = false;
const selected_baud = PortHandler.portPicker.selectedBauds;
const selectedPort = portName;
if (selectedPort === 'DFU') {
$('select#baud').hide();
return;
}
if (!isConnected) {
console.log(`Connecting to: ${portName}`);
GUI.connecting_to = portName;
// lock port select & baud while we are connecting / connected
PortHandler.portPickerDisabled = true;
$('div.connect_controls div.connect_state').text(i18n.getMessage('connecting'));
const baudRate = selected_baud;
if (selectedPort === 'virtual') {
CONFIGURATOR.virtualMode = true;
CONFIGURATOR.virtualApiVersion = PortHandler.portPicker.virtualMspVersion;
// Hack to get virtual working on the web
serial = serialShim();
serial.connect('virtual', {}, onOpenVirtual);
} else {
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(portName, { 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);
function onFinishCallback() {
finishClose(toggleStatus);
}
mspHelper?.setArmingEnabled(true, false, onFinishCallback);
}
}
}
function finishClose(finishedCallback) { function finishClose(finishedCallback) {
if (GUI.isCordova()) { if (GUI.isCordova()) {
UI_PHONES.reset(); UI_PHONES.reset();
@ -228,7 +218,7 @@ function setConnectionTimeout() {
if (!CONFIGURATOR.connectionValid) { if (!CONFIGURATOR.connectionValid) {
gui_log(i18n.getMessage('noConfigurationReceived')); gui_log(i18n.getMessage('noConfigurationReceived'));
$('div.connect_controls a.connect').click(); // disconnect connectDisconnect();
} }
}, 10000); }, 10000);
} }
@ -396,7 +386,7 @@ function processCustomDefaults() {
dialog.close(); dialog.close();
GUI.timeout_add('disconnect', function () { GUI.timeout_add('disconnect', function () {
$('div.connect_controls a.connect').click(); // disconnect connectDisconnect(); // disconnect
}, 0); }, 0);
}); });
@ -469,7 +459,7 @@ function checkReportProblems() {
abort = true; abort = true;
GUI.timeout_remove('connecting'); // kill connecting timer GUI.timeout_remove('connecting'); // kill connecting timer
$('div.connect_controls a.connect').click(); // disconnect connectDisconnect(); // disconnect
} }
if (!abort) { if (!abort) {
@ -792,6 +782,7 @@ export function reinitializeConnection(callback) {
}, 500); }, 500);
} }
rebootTimestamp = Date.now();
MSP.send_message(MSPCodes.MSP_SET_REBOOT, false, false); MSP.send_message(MSPCodes.MSP_SET_REBOOT, false, false);
gui_log(i18n.getMessage('deviceRebooting')); gui_log(i18n.getMessage('deviceRebooting'));
@ -805,3 +796,4 @@ export function reinitializeConnection(callback) {
callback(); callback();
} }
} }

View file

@ -1112,6 +1112,7 @@ firmware_flasher.initialize = function (callback) {
if (status) { if (status) {
const catch_new_port = function () { const catch_new_port = function () {
// TODO modify by listen to a new event
PortHandler.port_detected('flash_detected_device', function (resultPort) { PortHandler.port_detected('flash_detected_device', function (resultPort) {
const port = resultPort[0]; const port = resultPort[0];
@ -1156,7 +1157,7 @@ firmware_flasher.initialize = function (callback) {
firmware_flasher.isSerialPortAvailable = function() { firmware_flasher.isSerialPortAvailable = function() {
return PortHandler.port_available && !GUI.connect_lock; return PortHandler.portAvailable && !GUI.connect_lock;
}; };
firmware_flasher.updateDetectBoardButton = function() { firmware_flasher.updateDetectBoardButton = function() {
@ -1437,7 +1438,7 @@ firmware_flasher.backupConfig = function (callback) {
// Allow reboot after CLI exit // Allow reboot after CLI exit
const waitOnReboot = () => { const waitOnReboot = () => {
const disconnect = setInterval(function() { const disconnect = setInterval(function() {
if (PortHandler.port_available) { if (PortHandler.portAvailable) {
console.log(`Connection ready for flashing in ${count / 10} seconds`); console.log(`Connection ready for flashing in ${count / 10} seconds`);
clearInterval(disconnect); clearInterval(disconnect);
if (callback) { if (callback) {

View file

@ -30,7 +30,7 @@ class WebSerial extends EventTarget {
this.logHead = "SERIAL: "; this.logHead = "SERIAL: ";
this.port_counter = 0; this.portCounter = 0;
this.ports = []; this.ports = [];
this.port = null; this.port = null;
this.reader = null; this.reader = null;
@ -65,8 +65,7 @@ class WebSerial extends EventTarget {
} }
handleDisconnect() { handleDisconnect() {
this.removeEventListener('receive', this.handleReceiveBytes); this.disconnect();
this.dispatchEvent(new CustomEvent("disconnect", { detail: false }));
} }
getConnectedPort() { getConnectedPort() {
@ -75,7 +74,7 @@ class WebSerial extends EventTarget {
createPort(port) { createPort(port) {
return { return {
path: `D${this.port_counter++}`, path: `serial_${this.portCounter++}`,
displayName: `Betaflight ${vendorIdNames[port.getInfo().usbVendorId]}`, displayName: `Betaflight ${vendorIdNames[port.getInfo().usbVendorId]}`,
vendorId: port.getInfo().usbVendorId, vendorId: port.getInfo().usbVendorId,
productId: port.getInfo().usbProductId, productId: port.getInfo().usbProductId,
@ -88,21 +87,27 @@ class WebSerial extends EventTarget {
filters: webSerialDevices, filters: webSerialDevices,
}); });
this.port_counter = 1; this.portCounter = 1;
this.ports = ports.map(function (port) { this.ports = ports.map(function (port) {
return this.createPort(port); return this.createPort(port);
}, this); }, this);
} }
async requestPermissionDevice() { async requestPermissionDevice() {
const permissionPort = await navigator.serial.requestPort({ let newPermissionPort = null;
filters: webSerialDevices, try {
}); const userSelectedPort = await navigator.serial.requestPort({
const found = this.ports.find(port => port.port === permissionPort); filters: webSerialDevices,
if (!found) { });
return this.handleNewDevice(permissionPort); newPermissionPort = this.ports.find(port => port.port === userSelectedPort);
if (!newPermissionPort) {
newPermissionPort = this.handleNewDevice(userSelectedPort);
}
console.info("User selected device from permissions:", newPermissionPort.path);
} catch (error) {
console.error("User didn't select any device when requesting permission:", error);
} }
return null; return newPermissionPort;
} }
async getDevices() { async getDevices() {
@ -123,13 +128,14 @@ class WebSerial extends EventTarget {
if (connectionInfo && !this.openCanceled) { if (connectionInfo && !this.openCanceled) {
this.connected = true; this.connected = true;
this.connectionId = connectionInfo.connectionId; this.connectionId = path;
this.bitrate = options.baudRate; this.bitrate = options.baudRate;
this.bytesReceived = 0; this.bytesReceived = 0;
this.bytesSent = 0; this.bytesSent = 0;
this.failed = 0; this.failed = 0;
this.openRequested = false; this.openRequested = false;
this.port.addEventListener("disconnect", this.handleDisconnect.bind(this));
this.addEventListener("receive", this.handleReceiveBytes); this.addEventListener("receive", this.handleReceiveBytes);
console.log( console.log(
@ -186,6 +192,7 @@ class WebSerial extends EventTarget {
this.bytesSent = 0; this.bytesSent = 0;
const doCleanup = async () => { const doCleanup = async () => {
this.removeEventListener('receive', this.handleReceiveBytes);
if (this.reader) { if (this.reader) {
// this.reader.cancel(); // this.reader.cancel();
this.reader.releaseLock(); this.reader.releaseLock();
@ -196,6 +203,7 @@ class WebSerial extends EventTarget {
this.writer = null; this.writer = null;
} }
if (this.port) { if (this.port) {
this.port.removeEventListener("disconnect", this.handleDisconnect.bind(this));
await this.port.close(); await this.port.close();
this.port = null; this.port = null;
} }
@ -235,7 +243,7 @@ class WebSerial extends EventTarget {
); );
} }
return { return {
bytesSent: this.bytesSent, bytesSent: data.byteLength,
}; };
} }
} }