mirror of
https://github.com/betaflight/betaflight-configurator.git
synced 2025-07-18 22:05:13 +03:00
Serial refactor part 2 (#4404)
* Serial refactor part 2 * Fix callback * Fix some sonar complaints * Does no longer block dfu * Excempt presets * Add interval for reboot dialog * Update message after review Vitroid
This commit is contained in:
parent
c842b9d60b
commit
2d36e62474
6 changed files with 402 additions and 135 deletions
|
@ -320,6 +320,12 @@
|
|||
"configuratorUpdateWebsite": {
|
||||
"message": "Go to Release Website"
|
||||
},
|
||||
"rebootFlightController": {
|
||||
"message": "Rebooting flight controller, reconnect when ready"
|
||||
},
|
||||
"rebootFlightControllerReady": {
|
||||
"message": "Flight Controller is ready"
|
||||
},
|
||||
"deviceRebooting": {
|
||||
"message": "Device - <span class=\"message-negative\">Rebooting</span>"
|
||||
},
|
||||
|
|
|
@ -2,7 +2,6 @@ import { get as getConfig } from "./ConfigStorage";
|
|||
import { EventBus } from "../components/eventBus";
|
||||
import { serial } from "./serial.js";
|
||||
import WEBUSBDFU from "./protocols/webusbdfu";
|
||||
import WebBluetooth from "./protocols/WebBluetooth.js";
|
||||
import { reactive } from "vue";
|
||||
|
||||
const DEFAULT_PORT = "noselection";
|
||||
|
@ -35,44 +34,42 @@ const PortHandler = new (function () {
|
|||
})();
|
||||
|
||||
PortHandler.initialize = function () {
|
||||
EventBus.$on("ports-input:request-permission-bluetooth", this.askBluetoothPermissionPort.bind(this));
|
||||
EventBus.$on("ports-input:request-permission", this.askSerialPermissionPort.bind(this));
|
||||
EventBus.$on("ports-input:request-permission-bluetooth", () => this.requestDevicePermission("bluetooth"));
|
||||
EventBus.$on("ports-input:request-permission", () => this.requestDevicePermission("serial"));
|
||||
EventBus.$on("ports-input:change", this.onChangeSelectedPort.bind(this));
|
||||
|
||||
// Use serial for all protocol events
|
||||
serial.addEventListener("addedDevice", (event) => {
|
||||
const detail = event.detail;
|
||||
|
||||
// Determine the device type based on its properties
|
||||
if (detail?.path?.startsWith("bluetooth")) {
|
||||
this.addedBluetoothDevice(detail);
|
||||
} else if (detail?.path?.startsWith("usb_")) {
|
||||
this.addedUsbDevice(detail);
|
||||
this.handleDeviceAdded(detail, "bluetooth");
|
||||
} else {
|
||||
this.addedSerialDevice(detail);
|
||||
this.handleDeviceAdded(detail, "serial");
|
||||
}
|
||||
});
|
||||
|
||||
serial.addEventListener("removedDevice", (event) => {
|
||||
const detail = event.detail;
|
||||
|
||||
// Determine the device type based on its properties
|
||||
if (detail?.path?.startsWith("bluetooth")) {
|
||||
this.removedBluetoothDevice(detail);
|
||||
} else if (detail?.path?.startsWith("usb_")) {
|
||||
// Handle USB device removal if needed
|
||||
} else {
|
||||
this.removedSerialDevice(detail);
|
||||
}
|
||||
this.removedSerialDevice(event.detail);
|
||||
});
|
||||
|
||||
// Keep USB listener separate as it's not part of the serial protocols
|
||||
WEBUSBDFU.addEventListener("addedDevice", (event) => this.addedUsbDevice(event.detail));
|
||||
|
||||
// Initial device discovery
|
||||
this.addedSerialDevice();
|
||||
this.addedBluetoothDevice();
|
||||
this.addedUsbDevice();
|
||||
// Initial device discovery using the serial facade
|
||||
this.refreshAllDeviceLists();
|
||||
};
|
||||
|
||||
// Refactored refreshAllDeviceLists to use updateDeviceList
|
||||
PortHandler.refreshAllDeviceLists = async function () {
|
||||
// Update all device lists in parallel
|
||||
return Promise.all([
|
||||
this.updateDeviceList("serial"),
|
||||
this.updateDeviceList("bluetooth"),
|
||||
this.updateDeviceList("usb"),
|
||||
]).then(() => {
|
||||
this.selectActivePort();
|
||||
});
|
||||
};
|
||||
|
||||
PortHandler.setShowVirtualMode = function (showVirtualMode) {
|
||||
|
@ -89,44 +86,40 @@ PortHandler.setShowAllSerialDevices = function (showAllSerialDevices) {
|
|||
this.showAllSerialDevices = showAllSerialDevices;
|
||||
};
|
||||
|
||||
PortHandler.addedSerialDevice = function (device) {
|
||||
this.updateCurrentSerialPortsList().then(() => {
|
||||
const selectedPort = this.selectActivePort(device);
|
||||
if (!device || selectedPort === device.path) {
|
||||
// Send this event when the port handler auto selects a new device
|
||||
EventBus.$emit("port-handler:auto-select-serial-device", selectedPort);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
PortHandler.removedSerialDevice = function (device) {
|
||||
this.updateCurrentSerialPortsList().then(() => {
|
||||
if (this.portPicker.selectedPort === device.path) {
|
||||
this.selectActivePort();
|
||||
}
|
||||
});
|
||||
};
|
||||
console.log(`${this.logHead} Device removal event received:`, device);
|
||||
|
||||
PortHandler.addedBluetoothDevice = function (device) {
|
||||
this.updateCurrentBluetoothPortsList().then(() => {
|
||||
const selectedPort = this.selectActivePort(device);
|
||||
if (!device || selectedPort === device.path) {
|
||||
// Send this event when the port handler auto selects a new device
|
||||
EventBus.$emit("port-handler:auto-select-bluetooth-device", selectedPort);
|
||||
}
|
||||
});
|
||||
};
|
||||
// Get device path safely
|
||||
const devicePath = device?.path || (typeof device === "string" ? device : null);
|
||||
|
||||
PortHandler.removedBluetoothDevice = function (device) {
|
||||
this.updateCurrentBluetoothPortsList().then(() => {
|
||||
if (this.portPicker.selectedPort === device.path) {
|
||||
if (!devicePath) {
|
||||
console.warn(`${this.logHead} Device removal event missing path information`, device);
|
||||
// Still update ports, but don't try to use the undefined path
|
||||
this.updateDeviceList("serial").then(() => {
|
||||
this.selectActivePort();
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
// Update the appropriate ports list based on the device type
|
||||
const updatePromise = devicePath.startsWith("bluetooth")
|
||||
? this.updateDeviceList("bluetooth")
|
||||
: this.updateDeviceList("serial");
|
||||
|
||||
const wasSelectedPort = this.portPicker.selectedPort === devicePath;
|
||||
|
||||
updatePromise.then(() => {
|
||||
if (wasSelectedPort) {
|
||||
this.selectActivePort();
|
||||
|
||||
// Send event for UI components that might need to update
|
||||
EventBus.$emit("port-handler:device-removed", devicePath);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
PortHandler.addedUsbDevice = function (device) {
|
||||
this.updateCurrentUsbPortsList().then(() => {
|
||||
this.updateDeviceList("usb").then(() => {
|
||||
const selectedPort = this.selectActivePort(device);
|
||||
if (!device || selectedPort === device.path) {
|
||||
// Send this event when the port handler auto selects a new device
|
||||
|
@ -139,30 +132,28 @@ PortHandler.onChangeSelectedPort = function (port) {
|
|||
this.portPicker.selectedPort = port;
|
||||
};
|
||||
|
||||
PortHandler.updateCurrentSerialPortsList = async function () {
|
||||
const ports = await serial.getDevices();
|
||||
const orderedPorts = this.sortPorts(ports);
|
||||
this.portAvailable = orderedPorts.length > 0;
|
||||
console.log(`${this.logHead} Found serial port`, orderedPorts);
|
||||
this.currentSerialPorts = [...orderedPorts];
|
||||
};
|
||||
/**
|
||||
* Request permission for a device of the specified type
|
||||
* @param {string} deviceType - Type of device ('serial' or 'bluetooth')
|
||||
*/
|
||||
PortHandler.requestDevicePermission = function (deviceType = "serial") {
|
||||
// Determine whether to show all devices based on device type
|
||||
const showAllDevices = deviceType === "serial" ? this.showAllSerialDevices : false;
|
||||
|
||||
PortHandler.updateCurrentUsbPortsList = async function () {
|
||||
const ports = await WEBUSBDFU.getDevices();
|
||||
const orderedPorts = this.sortPorts(ports);
|
||||
this.dfuAvailable = orderedPorts.length > 0;
|
||||
console.log(`${this.logHead} Found DFU port`, orderedPorts);
|
||||
this.currentUsbPorts = [...orderedPorts];
|
||||
};
|
||||
|
||||
PortHandler.updateCurrentBluetoothPortsList = async function () {
|
||||
if (WebBluetooth.bluetooth) {
|
||||
const ports = await WebBluetooth.getDevices();
|
||||
const orderedPorts = this.sortPorts(ports);
|
||||
this.bluetoothAvailable = orderedPorts.length > 0;
|
||||
console.log(`${this.logHead} Found bluetooth port`, orderedPorts);
|
||||
this.currentBluetoothPorts = [...orderedPorts];
|
||||
}
|
||||
// Use serial facade to request permission
|
||||
serial
|
||||
.requestPermissionDevice(showAllDevices, deviceType)
|
||||
.then((port) => {
|
||||
if (port) {
|
||||
console.log(`${this.logHead} Permission granted for ${deviceType} device:`, port);
|
||||
this.selectActivePort(port);
|
||||
} else {
|
||||
console.log(`${this.logHead} Permission request cancelled or failed for ${deviceType}`);
|
||||
}
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error(`${this.logHead} Error requesting permission for ${deviceType}:`, error);
|
||||
});
|
||||
};
|
||||
|
||||
PortHandler.sortPorts = function (ports) {
|
||||
|
@ -174,31 +165,11 @@ PortHandler.sortPorts = function (ports) {
|
|||
});
|
||||
};
|
||||
|
||||
PortHandler.askBluetoothPermissionPort = function () {
|
||||
if (WebBluetooth.bluetooth) {
|
||||
WebBluetooth.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.askSerialPermissionPort = function () {
|
||||
serial.requestPermissionDevice(this.showAllSerialDevices).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) {
|
||||
PortHandler.selectActivePort = function (suggestedDevice = false) {
|
||||
const deviceFilter = ["AT32", "CP210", "SPR", "STM"];
|
||||
let selectedPort;
|
||||
|
||||
// Return the same that is connected to serial
|
||||
// First check for active connections
|
||||
if (serial.connected) {
|
||||
selectedPort = this.currentSerialPorts.find((device) => device === serial.getConnectedPort());
|
||||
}
|
||||
|
@ -208,10 +179,22 @@ PortHandler.selectActivePort = function (suggestedDevice) {
|
|||
selectedPort = this.currentUsbPorts.find((device) => device === WEBUSBDFU.getConnectedPort());
|
||||
}
|
||||
|
||||
// Return the same that is connected to bluetooth
|
||||
if (WebBluetooth.device) {
|
||||
selectedPort = this.currentBluetoothPorts.find((device) => device === WebBluetooth.getConnectedPort());
|
||||
}
|
||||
// If there is a connection, return it
|
||||
// if (selectedPort) {
|
||||
// console.log(`${this.logHead} Using connected device: ${selectedPort.path}`);
|
||||
// selectedPort = selectedPort.path;
|
||||
// return selectedPort;
|
||||
// }
|
||||
|
||||
// If there is no connection, check for the last used device
|
||||
// Check if the device is already connected
|
||||
// if (this.portPicker.selectedPort && this.portPicker.selectedPort !== DEFAULT_PORT) {
|
||||
// selectedPort = this.currentSerialPorts.find((device) => device.path === this.portPicker.selectedPort);
|
||||
// if (selectedPort) {
|
||||
// console.log(`${this.logHead} Using previously selected device: ${selectedPort.path}`);
|
||||
// return selectedPort.path;
|
||||
// }
|
||||
// }
|
||||
|
||||
// Return the suggested device (the new device that has been detected)
|
||||
if (!selectedPort && suggestedDevice) {
|
||||
|
@ -269,6 +252,81 @@ PortHandler.selectActivePort = function (suggestedDevice) {
|
|||
return selectedPort;
|
||||
};
|
||||
|
||||
// Create a unified handler for device addition
|
||||
PortHandler.handleDeviceAdded = function (device, deviceType) {
|
||||
if (!device) {
|
||||
console.warn(`${this.logHead} Invalid ${deviceType} device added event`);
|
||||
return;
|
||||
}
|
||||
|
||||
console.log(`${this.logHead} ${deviceType} device added:`, device);
|
||||
|
||||
// Update the appropriate device list
|
||||
const updatePromise =
|
||||
deviceType === "bluetooth" ? this.updateDeviceList("bluetooth") : this.updateDeviceList("serial");
|
||||
|
||||
updatePromise.then(() => {
|
||||
const selectedPort = this.selectActivePort(device);
|
||||
|
||||
if (selectedPort === device.path) {
|
||||
// Emit an event with the proper type for backward compatibility
|
||||
EventBus.$emit(`port-handler:auto-select-${deviceType}-device`, selectedPort);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Update device list with common implementation
|
||||
* @param {string} deviceType - Type of device ('serial', 'bluetooth', 'usb')
|
||||
* @returns {Promise} - Promise that resolves after updating the ports list
|
||||
*/
|
||||
PortHandler.updateDeviceList = async function (deviceType) {
|
||||
let ports = [];
|
||||
|
||||
try {
|
||||
switch (deviceType) {
|
||||
case "bluetooth":
|
||||
ports = await serial.getDevices("bluetooth");
|
||||
break;
|
||||
case "usb":
|
||||
ports = await WEBUSBDFU.getDevices();
|
||||
break;
|
||||
case "serial":
|
||||
default:
|
||||
ports = await serial.getDevices("serial");
|
||||
break;
|
||||
}
|
||||
|
||||
// Sort the ports
|
||||
const orderedPorts = this.sortPorts(ports);
|
||||
|
||||
// Update the appropriate properties based on device type
|
||||
switch (deviceType) {
|
||||
case "bluetooth":
|
||||
this.bluetoothAvailable = orderedPorts.length > 0;
|
||||
this.currentBluetoothPorts = [...orderedPorts];
|
||||
console.log(`${this.logHead} Found bluetooth port(s)`, orderedPorts);
|
||||
break;
|
||||
case "usb":
|
||||
this.dfuAvailable = orderedPorts.length > 0;
|
||||
this.currentUsbPorts = [...orderedPorts];
|
||||
console.log(`${this.logHead} Found DFU port(s)`, orderedPorts);
|
||||
break;
|
||||
case "serial":
|
||||
default:
|
||||
this.portAvailable = orderedPorts.length > 0;
|
||||
this.currentSerialPorts = [...orderedPorts];
|
||||
console.log(`${this.logHead} Found serial port(s)`, orderedPorts);
|
||||
break;
|
||||
}
|
||||
|
||||
return orderedPorts;
|
||||
} catch (error) {
|
||||
console.error(`${this.logHead} Error updating ${deviceType} devices:`, error);
|
||||
return [];
|
||||
}
|
||||
};
|
||||
|
||||
// We need to explicit make it reactive. If not, Vue3 does not detect correctly changes in array properties
|
||||
// like currentSerialPorts, currentUsbPorts, currentBluetoothPorts
|
||||
export default reactive(PortHandler);
|
||||
|
|
|
@ -272,7 +272,7 @@ class WebSerial extends EventTarget {
|
|||
|
||||
if (this.reader) {
|
||||
await this.reader.cancel();
|
||||
this.reader.releaseLock();
|
||||
await this.reader.releaseLock();
|
||||
this.reader = null;
|
||||
}
|
||||
|
||||
|
|
126
src/js/serial.js
126
src/js/serial.js
|
@ -22,6 +22,14 @@ class Serial extends EventTarget {
|
|||
this._websocket = new Websocket();
|
||||
this._virtual = new VirtualSerial();
|
||||
|
||||
// Update protocol map to use consistent naming
|
||||
this._protocolMap = {
|
||||
serial: this._webSerial, // TODO: should be 'webserial'
|
||||
bluetooth: this._bluetooth, // TODO: should be 'webbluetooth'
|
||||
websocket: this._websocket,
|
||||
virtual: this._virtual,
|
||||
};
|
||||
|
||||
// Initialize with default protocol
|
||||
this.selectProtocol(false);
|
||||
|
||||
|
@ -29,6 +37,42 @@ class Serial extends EventTarget {
|
|||
this._setupEventForwarding();
|
||||
}
|
||||
|
||||
// Add a getter method to safely access the protocol map
|
||||
_getProtocolByType(type) {
|
||||
if (!type) {
|
||||
return this._protocol;
|
||||
}
|
||||
|
||||
const protocol = this._protocolMap[type.toLowerCase()];
|
||||
|
||||
if (!protocol) {
|
||||
console.warn(`${this.logHead} Unknown protocol type: ${type}`);
|
||||
}
|
||||
|
||||
return protocol || null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the protocol type as a string
|
||||
* @param {Object} protocol - Protocol instance
|
||||
* @returns {string} - Protocol type name
|
||||
*/
|
||||
_getProtocolType(protocol) {
|
||||
if (protocol === this._webSerial) {
|
||||
return "webserial";
|
||||
}
|
||||
if (protocol === this._bluetooth) {
|
||||
return "webbluetooth";
|
||||
}
|
||||
if (protocol === this._websocket) {
|
||||
return "websocket";
|
||||
}
|
||||
if (protocol === this._virtual) {
|
||||
return "virtual";
|
||||
}
|
||||
return "unknown";
|
||||
}
|
||||
|
||||
/**
|
||||
* Set up event forwarding from all protocols to the Serial class
|
||||
*/
|
||||
|
@ -41,27 +85,20 @@ class Serial extends EventTarget {
|
|||
events.forEach((eventType) => {
|
||||
protocol.addEventListener(eventType, (event) => {
|
||||
let newDetail;
|
||||
|
||||
// Special handling for 'receive' events to ensure data is properly passed through
|
||||
if (eventType === "receive") {
|
||||
// If it's already a Uint8Array or ArrayBuffer, keep it as is
|
||||
if (event.type === "receive") {
|
||||
// For 'receive' events, we need to handle the data differently
|
||||
newDetail = {
|
||||
data: event.detail,
|
||||
protocolType:
|
||||
protocol === this._webSerial
|
||||
? "webSerial"
|
||||
: protocol === this._bluetooth
|
||||
? "bluetooth"
|
||||
: protocol === this._websocket
|
||||
? "websocket"
|
||||
: protocol === this._virtual
|
||||
? "virtual"
|
||||
: "unknown",
|
||||
protocolType: this._getProtocolType(protocol),
|
||||
};
|
||||
} else {
|
||||
// For all other events, pass through the detail as is
|
||||
newDetail = event.detail;
|
||||
// For other events, we can use the detail directly
|
||||
newDetail = {
|
||||
...event.detail,
|
||||
protocolType: this._getProtocolType(protocol),
|
||||
};
|
||||
}
|
||||
|
||||
// Dispatch the event with the new detail
|
||||
this.dispatchEvent(
|
||||
new CustomEvent(event.type, {
|
||||
|
@ -102,14 +139,14 @@ class Serial extends EventTarget {
|
|||
CONFIGURATOR.bluetoothMode = false;
|
||||
CONFIGURATOR.manualMode = true;
|
||||
} else if (portPath.startsWith("bluetooth")) {
|
||||
console.log(`${this.logHead} Using bluetooth protocol (based on port path)`);
|
||||
console.log(`${this.logHead} Using bluetooth protocol (based on port path: ${portPath})`);
|
||||
newProtocol = this._bluetooth;
|
||||
// Update CONFIGURATOR flags for consistency
|
||||
CONFIGURATOR.virtualMode = false;
|
||||
CONFIGURATOR.bluetoothMode = true;
|
||||
CONFIGURATOR.manualMode = false;
|
||||
} else {
|
||||
console.log(`${this.logHead} Using web serial protocol (based on port path)`);
|
||||
console.log(`${this.logHead} Using web serial protocol (based on port path: ${portPath})`);
|
||||
newProtocol = this._webSerial;
|
||||
// Update CONFIGURATOR flags for consistency
|
||||
CONFIGURATOR.virtualMode = false;
|
||||
|
@ -258,17 +295,58 @@ class Serial extends EventTarget {
|
|||
}
|
||||
|
||||
/**
|
||||
* Get devices from the current protocol
|
||||
* Get devices from a specific protocol type or current protocol
|
||||
* @param {string} protocolType - Optional protocol type ('serial', 'bluetooth', 'websocket', 'virtual')
|
||||
* @returns {Promise<Array>} - List of devices
|
||||
*/
|
||||
async getDevices() {
|
||||
return this._protocol?.getDevices() || [];
|
||||
async getDevices(protocolType = null) {
|
||||
try {
|
||||
// Get the appropriate protocol
|
||||
const targetProtocol = this._getProtocolByType(protocolType);
|
||||
|
||||
if (!targetProtocol) {
|
||||
console.warn(`${this.logHead} No valid protocol for getting devices`);
|
||||
return [];
|
||||
}
|
||||
|
||||
if (typeof targetProtocol.getDevices !== "function") {
|
||||
console.error(`${this.logHead} Selected protocol does not implement getDevices`);
|
||||
return [];
|
||||
}
|
||||
|
||||
return targetProtocol.getDevices() || [];
|
||||
} catch (error) {
|
||||
console.error(`${this.logHead} Error getting devices:`, error);
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Request permission for a device
|
||||
* Request permission to access a device
|
||||
* @param {boolean} showAllDevices - Whether to show all devices or only those with filters
|
||||
* @param {string} protocolType - Optional protocol type ('serial', 'bluetooth', etc.)
|
||||
* @returns {Promise<Object>} - Promise resolving to the selected device
|
||||
*/
|
||||
async requestPermissionDevice(showAllSerialDevices = false) {
|
||||
return this._protocol?.requestPermissionDevice(showAllSerialDevices) || null;
|
||||
async requestPermissionDevice(showAllDevices = false, protocolType = null) {
|
||||
try {
|
||||
// Get the appropriate protocol
|
||||
const targetProtocol = this._getProtocolByType(protocolType);
|
||||
|
||||
if (!targetProtocol) {
|
||||
console.warn(`${this.logHead} No valid protocol for permission request`);
|
||||
return null;
|
||||
}
|
||||
|
||||
if (typeof targetProtocol.requestPermissionDevice !== "function") {
|
||||
console.error(`${this.logHead} Selected protocol does not support permission requests`);
|
||||
return null;
|
||||
}
|
||||
|
||||
return targetProtocol.requestPermissionDevice(showAllDevices);
|
||||
} catch (error) {
|
||||
console.error(`${this.logHead} Error requesting device permission:`, error);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -810,12 +810,16 @@ function startLiveDataRefreshTimer() {
|
|||
|
||||
export function reinitializeConnection(callback) {
|
||||
// In virtual mode reconnect when autoconnect is enabled
|
||||
if (CONFIGURATOR.virtualMode && PortHandler.portPicker.autoConnect) {
|
||||
return setTimeout(function () {
|
||||
$("a.connection_button__link").trigger("click");
|
||||
}, 500);
|
||||
if (CONFIGURATOR.virtualMode) {
|
||||
connectDisconnect();
|
||||
if (PortHandler.portPicker.autoConnect) {
|
||||
return setTimeout(function () {
|
||||
$("a.connection_button__link").trigger("click");
|
||||
}, 500);
|
||||
}
|
||||
}
|
||||
|
||||
// Send reboot command to the flight controller
|
||||
rebootTimestamp = Date.now();
|
||||
MSP.send_message(MSPCodes.MSP_SET_REBOOT, false, false);
|
||||
|
||||
|
@ -824,14 +828,131 @@ export function reinitializeConnection(callback) {
|
|||
connectDisconnect();
|
||||
}
|
||||
|
||||
// Show reboot progress modal except for presets tab
|
||||
if (GUI.active_tab === "presets") {
|
||||
console.log("Rebooting in presets tab, skipping reboot dialog", GUI.active_tab);
|
||||
gui_log(i18n.getMessage("deviceRebooting"));
|
||||
gui_log(i18n.getMessage("deviceReady"));
|
||||
if (callback && typeof callback === "function") {
|
||||
callback();
|
||||
}
|
||||
return;
|
||||
}
|
||||
// Show reboot progress modal
|
||||
showRebootDialog(callback);
|
||||
}
|
||||
|
||||
function showRebootDialog(callback) {
|
||||
gui_log(i18n.getMessage("deviceRebooting"));
|
||||
|
||||
// wait for the device to reboot
|
||||
setTimeout(function () {
|
||||
gui_log(i18n.getMessage("deviceReady"));
|
||||
}, 2000);
|
||||
// Show reboot progress modal
|
||||
const rebootDialog = document.getElementById("rebootProgressDialog") || createRebootProgressDialog();
|
||||
rebootDialog.querySelector(".reboot-progress-bar").style.width = "0%";
|
||||
rebootDialog.querySelector(".reboot-status").textContent = i18n.getMessage("rebootFlightController");
|
||||
rebootDialog.showModal();
|
||||
|
||||
if (callback && typeof callback === "function") {
|
||||
callback();
|
||||
// Update progress during reboot
|
||||
let progress = 0;
|
||||
const progressInterval = setInterval(() => {
|
||||
progress += 5;
|
||||
if (progress <= 100) {
|
||||
rebootDialog.querySelector(".reboot-progress-bar").style.width = `${progress}%`;
|
||||
}
|
||||
}, 100);
|
||||
|
||||
// Check for successful connection every 100ms
|
||||
const connectionCheckInterval = setInterval(() => {
|
||||
if (CONFIGURATOR.connectionValid) {
|
||||
// Connection established, device is ready
|
||||
clearInterval(connectionCheckInterval);
|
||||
clearInterval(progressInterval);
|
||||
|
||||
rebootDialog.querySelector(".reboot-progress-bar").style.width = "100%";
|
||||
rebootDialog.querySelector(".reboot-status").textContent = i18n.getMessage("rebootFlightControllerReady");
|
||||
|
||||
// Close the dialog after showing "ready" message briefly
|
||||
setTimeout(() => {
|
||||
rebootDialog.close();
|
||||
}, 1000);
|
||||
|
||||
gui_log(i18n.getMessage("deviceReady"));
|
||||
if (callback && typeof callback === "function") {
|
||||
callback();
|
||||
}
|
||||
}
|
||||
}, 100);
|
||||
|
||||
// Set a maximum timeout for the reboot process (5 seconds)
|
||||
setTimeout(function () {
|
||||
clearInterval(connectionCheckInterval);
|
||||
clearInterval(progressInterval);
|
||||
|
||||
rebootDialog.querySelector(".reboot-progress-bar").style.width = "100%";
|
||||
rebootDialog.querySelector(".reboot-status").textContent = i18n.getMessage("rebootFlightControllerReady");
|
||||
|
||||
// Close the dialog after showing "ready" message briefly
|
||||
setTimeout(() => {
|
||||
rebootDialog.close();
|
||||
}, 1000);
|
||||
|
||||
gui_log(i18n.getMessage("deviceReady"));
|
||||
if (callback && typeof callback === "function") {
|
||||
callback();
|
||||
}
|
||||
}, 5000);
|
||||
|
||||
// Helper function to create the reboot dialog if it doesn't exist
|
||||
function createRebootProgressDialog() {
|
||||
const dialog = document.createElement("dialog");
|
||||
dialog.id = "rebootProgressDialog";
|
||||
dialog.className = "dialogReboot";
|
||||
|
||||
dialog.innerHTML = `
|
||||
<div class="content">
|
||||
<div class="reboot-status">${i18n.getMessage("rebootFlightController")}</div>
|
||||
<div class="reboot-progress-container">
|
||||
<div class="reboot-progress-bar"></div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
document.body.appendChild(dialog);
|
||||
|
||||
// Add styles if not already defined
|
||||
if (!document.getElementById("rebootProgressStyle")) {
|
||||
const style = document.createElement("style");
|
||||
style.id = "rebootProgressStyle";
|
||||
style.textContent = `
|
||||
.dialogReboot {
|
||||
border: 1px solid #3f4241;
|
||||
border-radius: 5px;
|
||||
background-color: #2d3233;
|
||||
color: #fff;
|
||||
padding: 20px;
|
||||
max-width: 400px;
|
||||
}
|
||||
.reboot-progress-container {
|
||||
width: 100%;
|
||||
background-color: #424546;
|
||||
border-radius: 3px;
|
||||
margin: 15px 0 5px;
|
||||
height: 10px;
|
||||
}
|
||||
.reboot-progress-bar {
|
||||
height: 100%;
|
||||
background-color: #ffbb00;
|
||||
border-radius: 3px;
|
||||
transition: width 0.1s ease-in-out;
|
||||
width: 0%;
|
||||
}
|
||||
.reboot-status {
|
||||
text-align: center;
|
||||
margin: 10px 0;
|
||||
}
|
||||
`;
|
||||
document.head.appendChild(style);
|
||||
}
|
||||
|
||||
return dialog;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -19,6 +19,7 @@ import AutoBackup from "../utils/AutoBackup.js";
|
|||
import AutoDetect from "../utils/AutoDetect.js";
|
||||
import { EventBus } from "../../components/eventBus";
|
||||
import { ispConnected } from "../utils/connection.js";
|
||||
import FC from "../fc";
|
||||
|
||||
const firmware_flasher = {
|
||||
targets: null,
|
||||
|
@ -255,6 +256,9 @@ firmware_flasher.initialize = function (callback) {
|
|||
}
|
||||
|
||||
// extract osd protocols from general options and add to osdProtocols
|
||||
console.log(`${self.logHead} buildOptions`, FC.CONFIG.buildOptions);
|
||||
self.cloudBuildOptions = FC.CONFIG.buildOptions || [];
|
||||
console.log(`${self.logHead} generalOptions`, self.cloudBuildOptions);
|
||||
data.osdProtocols = data.generalOptions
|
||||
.filter((option) => option.group === "OSD")
|
||||
.map((option) => {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue