mirror of
https://github.com/betaflight/betaflight-configurator.git
synced 2025-07-27 02:05:21 +03:00
Make port_handler work with PWA (#3958)
* Make port_handler work with PWA * Modify the port_handler more the Vue way * Fixes after review * Fix request permission option not being deselected * Hide baud selection in port picker if virtual port * Added port override option for manual * Fix virtual port state when loading the page * Fix request permission adds the same device several times * Fix automatic selection of device under Linux
This commit is contained in:
parent
b91698d0f4
commit
fce0c8305b
12 changed files with 246 additions and 166 deletions
|
@ -62,6 +62,10 @@
|
||||||
"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."
|
||||||
},
|
},
|
||||||
|
"portsSelectPermission": {
|
||||||
|
"message": "--- I can't find my device ---",
|
||||||
|
"description": "Option in the port selection dropdown to allow the user to give permissions to the system to access the device."
|
||||||
|
},
|
||||||
"virtualMSPVersion": {
|
"virtualMSPVersion": {
|
||||||
"message": "Virtual Firmware Version"
|
"message": "Virtual Firmware Version"
|
||||||
},
|
},
|
||||||
|
|
|
@ -13,6 +13,7 @@ import StatusBar from "./status-bar/StatusBar.vue";
|
||||||
import BatteryIcon from "./quad-status/BatteryIcon.vue";
|
import BatteryIcon from "./quad-status/BatteryIcon.vue";
|
||||||
import FC from '../js/fc.js';
|
import FC from '../js/fc.js';
|
||||||
import MSP from '../js/msp.js';
|
import MSP from '../js/msp.js';
|
||||||
|
import PortHandler from '../js/port_handler.js';
|
||||||
import PortUsage from '../js/port_usage.js';
|
import PortUsage from '../js/port_usage.js';
|
||||||
import PortPicker from './port-picker/PortPicker.vue';
|
import PortPicker from './port-picker/PortPicker.vue';
|
||||||
import CONFIGURATOR from '../js/data_storage.js';
|
import CONFIGURATOR from '../js/data_storage.js';
|
||||||
|
@ -26,6 +27,7 @@ const betaflightModel = {
|
||||||
FC,
|
FC,
|
||||||
MSP,
|
MSP,
|
||||||
PortUsage,
|
PortUsage,
|
||||||
|
PortHandler,
|
||||||
};
|
};
|
||||||
|
|
||||||
i18next.on('initialized', function() {
|
i18next.on('initialized', function() {
|
||||||
|
|
|
@ -1,13 +1,13 @@
|
||||||
<template>
|
<template>
|
||||||
<div
|
<div
|
||||||
id="firmware-virtual-option"
|
id="firmware-virtual-option"
|
||||||
:style="{ display: isVirtual ? 'block' : 'none' }"
|
|
||||||
>
|
>
|
||||||
<div class="dropdown dropdown-dark">
|
<div class="dropdown dropdown-dark">
|
||||||
<select
|
<select
|
||||||
id="firmware-version-dropdown"
|
id="firmware-version-dropdown"
|
||||||
class="dropdown-select"
|
class="dropdown-select"
|
||||||
:title="$t('virtualMSPVersion')"
|
:title="$t('virtualMSPVersion')"
|
||||||
|
v-model="value"
|
||||||
>
|
>
|
||||||
<option
|
<option
|
||||||
v-for="(version, index) in firmwareVersions"
|
v-for="(version, index) in firmwareVersions"
|
||||||
|
@ -24,9 +24,9 @@
|
||||||
<script>
|
<script>
|
||||||
export default {
|
export default {
|
||||||
props: {
|
props: {
|
||||||
isVirtual: {
|
value: {
|
||||||
type: Boolean,
|
type: String,
|
||||||
default: true,
|
default: "1.46.0",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
|
@ -50,7 +50,7 @@ export default {
|
||||||
width: 180px;
|
width: 180px;
|
||||||
margin-right: 15px;
|
margin-right: 15px;
|
||||||
margin-top: 0px;
|
margin-top: 0px;
|
||||||
display: none;
|
display: block;
|
||||||
}
|
}
|
||||||
.dropdown {
|
.dropdown {
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
|
|
|
@ -1,42 +1,43 @@
|
||||||
<template>
|
<template>
|
||||||
<div
|
<div id="port-override-option">
|
||||||
id="port-override-option"
|
|
||||||
style="display: none"
|
|
||||||
:style="{ display: isManual ? 'flex' : 'none' }"
|
|
||||||
>
|
|
||||||
<label
|
<label
|
||||||
for="port-override"
|
for="port-override"
|
||||||
><span>{{ $t("portOverrideText") }}</span>
|
><span>{{ $t("portOverrideText") }}</span>
|
||||||
<input
|
<input
|
||||||
id="port-override"
|
id="port-override"
|
||||||
type="text"
|
type="text"
|
||||||
value="/dev/rfcomm0"
|
v-model="value"
|
||||||
></label>
|
></label>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
export default {
|
export default {
|
||||||
props: {
|
props: {
|
||||||
isManual: {
|
value: {
|
||||||
type: Boolean,
|
type: String,
|
||||||
default: true,
|
default: "/dev/rfcomm0",
|
||||||
},
|
|
||||||
},
|
},
|
||||||
|
isManual: {
|
||||||
|
type: Boolean,
|
||||||
|
default: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
<style scoped>
|
<style scoped>
|
||||||
#port-override-option {
|
#port-override-option {
|
||||||
font-family: "Open Sans", "Segoe UI", Tahoma, sans-serif;
|
font-family: "Open Sans", "Segoe UI", Tahoma, sans-serif;
|
||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
margin-top: 16px;
|
margin-top: 4px;
|
||||||
margin-right: 15px;
|
margin-right: 15px;
|
||||||
label {
|
label {
|
||||||
background-color: #2b2b2b;
|
background-color: #2b2b2b;
|
||||||
border-radius: 3px;
|
border-radius: 3px;
|
||||||
padding: 3px;
|
padding: 3px;
|
||||||
color: var(--subtleAccent);
|
color: var(--subtleAccent);
|
||||||
}
|
};
|
||||||
|
display: block;
|
||||||
}
|
}
|
||||||
|
|
||||||
#port-override-option label {
|
#port-override-option label {
|
||||||
|
|
|
@ -1,24 +1,50 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="web-port-picker">
|
<div class="web-port-picker">
|
||||||
<FirmwareVirtualOption :is-virtual="port === 'virtual'" />
|
<PortOverrideOption
|
||||||
<PortsInput v-model="port" />
|
v-if="value.selectedPort === 'manual'"
|
||||||
|
v-model="value.portOverride"
|
||||||
|
/>
|
||||||
|
<FirmwareVirtualOption
|
||||||
|
v-if="value.selectedPort === 'virtual'"
|
||||||
|
v-model="value.virtualMspVersion"
|
||||||
|
/>
|
||||||
|
<PortsInput
|
||||||
|
v-model="value"
|
||||||
|
:connected-devices="connectedDevices"
|
||||||
|
:disabled="disabled" />
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
import PortOverrideOption from "./PortOverrideOption.vue";
|
||||||
import FirmwareVirtualOption from "./FirmwareVirtualOption.vue";
|
import FirmwareVirtualOption from "./FirmwareVirtualOption.vue";
|
||||||
import PortsInput from "./PortsInput.vue";
|
import PortsInput from "./PortsInput.vue";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
|
props: {
|
||||||
|
value: {
|
||||||
|
type: Object,
|
||||||
|
default: {
|
||||||
|
selectedPort: "manual",
|
||||||
|
selectedBaud: 115200,
|
||||||
|
portOverride: "/dev/rfcomm0",
|
||||||
|
virtualMspVersion: "1.46.0",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
connectedDevices: {
|
||||||
|
type: Array,
|
||||||
|
default: () => [],
|
||||||
|
},
|
||||||
|
disabled: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
components: {
|
components: {
|
||||||
|
PortOverrideOption,
|
||||||
FirmwareVirtualOption,
|
FirmwareVirtualOption,
|
||||||
PortsInput,
|
PortsInput,
|
||||||
},
|
},
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
port: 'manual',
|
|
||||||
};
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
|
@ -8,8 +8,9 @@
|
||||||
id="port"
|
id="port"
|
||||||
class="dropdown-select"
|
class="dropdown-select"
|
||||||
:title="$t('firmwareFlasherManualPort')"
|
:title="$t('firmwareFlasherManualPort')"
|
||||||
@value="value"
|
:disabled="disabled"
|
||||||
@input="$emit('input', $event.target.value)"
|
v-model="value.selectedPort"
|
||||||
|
@change="onChange"
|
||||||
>
|
>
|
||||||
<option value="manual">
|
<option value="manual">
|
||||||
{{ $t("portsSelectManual") }}
|
{{ $t("portsSelectManual") }}
|
||||||
|
@ -20,16 +21,29 @@
|
||||||
>
|
>
|
||||||
{{ $t("portsSelectVirtual") }}
|
{{ $t("portsSelectVirtual") }}
|
||||||
</option>
|
</option>
|
||||||
|
<option
|
||||||
|
v-for="connectedDevice in connectedDevices"
|
||||||
|
:key="connectedDevice.path"
|
||||||
|
:value="connectedDevice.path"
|
||||||
|
>
|
||||||
|
{{ connectedDevice.displayName }}
|
||||||
|
</option>
|
||||||
|
<option value="requestpermission">
|
||||||
|
{{ $t("portsSelectPermission") }}
|
||||||
|
</option>
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
<div id="auto-connect-and-baud">
|
<div id="auto-connect-and-baud">
|
||||||
<div id="baudselect">
|
<div id="baudselect"
|
||||||
|
v-if="value.selectedPort !== 'virtual'"
|
||||||
|
>
|
||||||
<div class="dropdown dropdown-dark">
|
<div class="dropdown dropdown-dark">
|
||||||
<select
|
<select
|
||||||
id="baud"
|
id="baud"
|
||||||
v-model="selectedBaudRate"
|
v-model="value.selectedBauds"
|
||||||
class="dropdown-select"
|
class="dropdown-select"
|
||||||
:title="$t('firmwareFlasherBaudRate')"
|
:title="$t('firmwareFlasherBaudRate')"
|
||||||
|
:disabled="disabled"
|
||||||
>
|
>
|
||||||
<option
|
<option
|
||||||
v-for="baudRate in baudRates"
|
v-for="baudRate in baudRates"
|
||||||
|
@ -52,15 +66,25 @@ import { EventBus } from '../eventBus';
|
||||||
export default {
|
export default {
|
||||||
props: {
|
props: {
|
||||||
value: {
|
value: {
|
||||||
type: String,
|
type: Object,
|
||||||
default: 'manual',
|
default: {
|
||||||
|
selectedPort: 'manual',
|
||||||
|
selectedBaud: 115200,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
connectedDevices: {
|
||||||
|
type: Array,
|
||||||
|
default: () => [],
|
||||||
|
},
|
||||||
|
disabled: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
showVirtual: false,
|
showVirtual: false,
|
||||||
selectedBaudRate: "115200",
|
|
||||||
baudRates: [
|
baudRates: [
|
||||||
{ value: "1000000", label: "1000000" },
|
{ value: "1000000", label: "1000000" },
|
||||||
{ value: "500000", label: "500000" },
|
{ value: "500000", label: "500000" },
|
||||||
|
@ -80,13 +104,23 @@ export default {
|
||||||
},
|
},
|
||||||
mounted() {
|
mounted() {
|
||||||
EventBus.$on('config-storage:set', this.setShowVirtual);
|
EventBus.$on('config-storage:set', this.setShowVirtual);
|
||||||
|
this.setShowVirtual('showVirtualMode');
|
||||||
},
|
},
|
||||||
destroyed() {
|
destroyed() {
|
||||||
EventBus.$off('config-storage:set', this.setShowVirtual);
|
EventBus.$off('config-storage:set', this.setShowVirtual);
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
setShowVirtual() {
|
setShowVirtual(element) {
|
||||||
this.showVirtual = getConfig('showVirtualMode').showVirtualMode;
|
if (element === 'showVirtualMode') {
|
||||||
|
this.showVirtual = getConfig('showVirtualMode').showVirtualMode;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onChange(event) {
|
||||||
|
if (event.target.value === 'requestpermission') {
|
||||||
|
EventBus.$emit('ports-input:request-permission');
|
||||||
|
} else {
|
||||||
|
EventBus.$emit('ports-input:change', event.target.value);
|
||||||
|
}
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
|
@ -26,7 +26,11 @@
|
||||||
:firmware-id="FC.CONFIG.flightControllerIdentifier"
|
:firmware-id="FC.CONFIG.flightControllerIdentifier"
|
||||||
:hardware-id="FC.CONFIG.hardwareName"
|
:hardware-id="FC.CONFIG.hardwareName"
|
||||||
></betaflight-logo>
|
></betaflight-logo>
|
||||||
<port-picker></port-picker>
|
<port-picker
|
||||||
|
v-model="PortHandler.portPicker"
|
||||||
|
:connected-devices="PortHandler.currentPorts"
|
||||||
|
:disabled="PortHandler.portPickerDisabled"
|
||||||
|
></port-picker>
|
||||||
<div class="header-wrapper">
|
<div class="header-wrapper">
|
||||||
<div id="quad-status_wrapper">
|
<div id="quad-status_wrapper">
|
||||||
<battery-icon
|
<battery-icon
|
||||||
|
|
|
@ -45,7 +45,7 @@ export function set(input) {
|
||||||
tmpObj[element] = input[element];
|
tmpObj[element] = input[element];
|
||||||
try {
|
try {
|
||||||
localStorage.setItem(element, JSON.stringify(tmpObj));
|
localStorage.setItem(element, JSON.stringify(tmpObj));
|
||||||
EventBus.$emit(`config-storage:set`, 'element');
|
EventBus.$emit('config-storage:set', element);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error(e);
|
console.error(e);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,18 +1,28 @@
|
||||||
import GUI, { TABS } from "./gui";
|
import GUI, { TABS } from "./gui";
|
||||||
import FC from "./fc";
|
import FC from "./fc";
|
||||||
import { i18n } from "./localization";
|
import { i18n } from "./localization";
|
||||||
import { generateVirtualApiVersions, getTextWidth } from './utils/common';
|
|
||||||
import { get as getConfig } from "./ConfigStorage";
|
import { get as getConfig } from "./ConfigStorage";
|
||||||
import serial from "./serial";
|
|
||||||
import MdnsDiscovery from "./mdns_discovery";
|
import MdnsDiscovery from "./mdns_discovery";
|
||||||
import { isWeb } from "./utils/isWeb";
|
import { isWeb } from "./utils/isWeb";
|
||||||
import { usbDevices } from "./usb_devices";
|
import { usbDevices } from "./usb_devices";
|
||||||
|
import { serialShim } from "./serial_shim.js";
|
||||||
|
import { EventBus } from "../components/eventBus";
|
||||||
|
|
||||||
const TIMEOUT_CHECK = 500 ; // With 250 it seems that it produces a memory leak and slowdown in some versions, reason unknown
|
const serial = serialShim();
|
||||||
|
|
||||||
|
const DEFAULT_PORT = 'manual';
|
||||||
|
const DEFAULT_BAUDS = 115200;
|
||||||
|
|
||||||
const PortHandler = new function () {
|
const PortHandler = new function () {
|
||||||
this.currentPorts = [];
|
this.currentPorts = [];
|
||||||
this.initialPorts = false;
|
this.initialPorts = false;
|
||||||
|
this.portPicker = {
|
||||||
|
selectedPort: DEFAULT_PORT,
|
||||||
|
selectedBauds: DEFAULT_BAUDS,
|
||||||
|
portOverride: "/dev/rfcomm0",
|
||||||
|
virtualMspVersion: "1.46.0",
|
||||||
|
};
|
||||||
|
this.portPickerDisabled = false;
|
||||||
this.port_detected_callbacks = [];
|
this.port_detected_callbacks = [];
|
||||||
this.port_removed_callbacks = [];
|
this.port_removed_callbacks = [];
|
||||||
this.dfu_available = false;
|
this.dfu_available = false;
|
||||||
|
@ -24,21 +34,13 @@ const PortHandler = new function () {
|
||||||
};
|
};
|
||||||
|
|
||||||
PortHandler.initialize = function () {
|
PortHandler.initialize = function () {
|
||||||
const self = this;
|
|
||||||
|
|
||||||
// currently web build doesn't need port handler,
|
EventBus.$on('ports-input:request-permission', this.askPermissionPort.bind(this));
|
||||||
// so just bail out.
|
EventBus.$on('ports-input:change', this.onChangeSelectedPort.bind(this));
|
||||||
if (isWeb()) {
|
|
||||||
return 'not implemented';
|
|
||||||
}
|
|
||||||
|
|
||||||
const portPickerElementSelector = "div#port-picker #port";
|
serial.addEventListener("addedDevice", this.check_serial_devices.bind(this));
|
||||||
self.portPickerElement = $(portPickerElementSelector);
|
|
||||||
self.selectList = document.querySelector(portPickerElementSelector);
|
|
||||||
self.initialWidth = self.selectList.offsetWidth + 12;
|
|
||||||
|
|
||||||
// fill dropdown with version numbers
|
serial.addEventListener("removedDevice", this.check_serial_devices.bind(this));
|
||||||
generateVirtualApiVersions();
|
|
||||||
|
|
||||||
this.reinitialize(); // just to prevent code redundancy
|
this.reinitialize(); // just to prevent code redundancy
|
||||||
};
|
};
|
||||||
|
@ -73,15 +75,15 @@ PortHandler.check = function () {
|
||||||
self.check_serial_devices();
|
self.check_serial_devices();
|
||||||
}
|
}
|
||||||
|
|
||||||
self.usbCheckLoop = setTimeout(() => {
|
|
||||||
self.check();
|
this.check_serial_devices();
|
||||||
}, TIMEOUT_CHECK);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
PortHandler.check_serial_devices = function () {
|
PortHandler.check_serial_devices = function () {
|
||||||
const self = this;
|
const self = this;
|
||||||
|
|
||||||
serial.getDevices(function(cp) {
|
const updatePorts = function(cp) {
|
||||||
self.currentPorts = [];
|
self.currentPorts = [];
|
||||||
|
|
||||||
if (self.useMdnsBrowser) {
|
if (self.useMdnsBrowser) {
|
||||||
|
@ -109,11 +111,25 @@ PortHandler.check_serial_devices = function () {
|
||||||
} else {
|
} else {
|
||||||
self.removePort();
|
self.removePort();
|
||||||
self.detectPort();
|
self.detectPort();
|
||||||
|
self.selectActivePort();
|
||||||
}
|
}
|
||||||
});
|
};
|
||||||
|
|
||||||
|
|
||||||
|
serial.getDevices().then(updatePorts);
|
||||||
|
};
|
||||||
|
|
||||||
|
PortHandler.onChangeSelectedPort = function(port) {
|
||||||
|
this.portPicker.selectedPort = port;
|
||||||
};
|
};
|
||||||
|
|
||||||
PortHandler.check_usb_devices = function (callback) {
|
PortHandler.check_usb_devices = function (callback) {
|
||||||
|
|
||||||
|
// TODO needs USB code refactor for web
|
||||||
|
if (isWeb()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const self = this;
|
const self = this;
|
||||||
|
|
||||||
chrome.usb.getDevices(usbDevices, function (result) {
|
chrome.usb.getDevices(usbDevices, function (result) {
|
||||||
|
@ -137,7 +153,7 @@ PortHandler.check_usb_devices = function (callback) {
|
||||||
}));
|
}));
|
||||||
|
|
||||||
self.portPickerElement.append($('<option/>', {
|
self.portPickerElement.append($('<option/>', {
|
||||||
value: 'manual',
|
value: DEFAULT_PORT,
|
||||||
text: i18n.getMessage('portsSelectManual'),
|
text: i18n.getMessage('portsSelectManual'),
|
||||||
/**
|
/**
|
||||||
* @deprecated please avoid using `isDFU` and friends for new code.
|
* @deprecated please avoid using `isDFU` and friends for new code.
|
||||||
|
@ -203,7 +219,6 @@ PortHandler.removePort = function() {
|
||||||
self.initialPorts.splice(self.initialPorts.indexOf(port, 1));
|
self.initialPorts.splice(self.initialPorts.indexOf(port, 1));
|
||||||
}
|
}
|
||||||
self.updatePortSelect(self.initialPorts);
|
self.updatePortSelect(self.initialPorts);
|
||||||
self.portPickerElement.trigger('change');
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -216,8 +231,8 @@ PortHandler.detectPort = function() {
|
||||||
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);
|
this.portPicker.selectedPort = newPorts[0].path;
|
||||||
} else if (newPorts.length > 1) {
|
} else {
|
||||||
self.selectActivePort();
|
self.selectActivePort();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -227,8 +242,6 @@ PortHandler.detectPort = function() {
|
||||||
TABS.firmware_flasher.boardNeedsVerification = true;
|
TABS.firmware_flasher.boardNeedsVerification = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
self.portPickerElement.trigger('change');
|
|
||||||
|
|
||||||
// auto-connect if enabled
|
// auto-connect if enabled
|
||||||
if (GUI.auto_connect && !GUI.connecting_to && !GUI.connected_to && GUI.active_tab !== 'firmware_flasher') {
|
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
|
||||||
|
@ -263,105 +276,39 @@ 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();
|
|
||||||
|
|
||||||
for (const port of ports) {
|
|
||||||
const portText = port.displayName ? `${port.path} - ${port.displayName}` : port.path;
|
|
||||||
|
|
||||||
this.portPickerElement.append($("<option/>", {
|
|
||||||
value: port.path,
|
|
||||||
text: portText,
|
|
||||||
/**
|
|
||||||
* @deprecated please avoid using `isDFU` and friends for new code.
|
|
||||||
*/
|
|
||||||
data: {isManual: false},
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.showVirtualMode) {
|
|
||||||
this.portPickerElement.append($("<option/>", {
|
|
||||||
value: 'virtual',
|
|
||||||
text: i18n.getMessage('portsSelectVirtual'),
|
|
||||||
/**
|
|
||||||
* @deprecated please avoid using `isDFU` and friends for new code.
|
|
||||||
*/
|
|
||||||
data: {isVirtual: true},
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.showManualMode) {
|
|
||||||
this.portPickerElement.append($("<option/>", {
|
|
||||||
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();
|
|
||||||
this.currentPorts = 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() {
|
PortHandler.selectActivePort = function() {
|
||||||
const ports = this.currentPorts;
|
const ports = this.currentPorts;
|
||||||
const OS = GUI.operating_system;
|
const OS = GUI.operating_system;
|
||||||
|
let selectedPort;
|
||||||
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;
|
||||||
if (portName) {
|
if (portName) {
|
||||||
const pathSelect = ports[i].path;
|
const pathSelect = ports[i].path;
|
||||||
const isWindows = (OS === 'Windows');
|
const deviceFilter = ['AT32', 'CP210', 'SPR', 'STM'];
|
||||||
const isTty = pathSelect.includes('tty');
|
|
||||||
const deviceFilter = ['AT32', 'CP210', 'SPR', 'STM32'];
|
|
||||||
const deviceRecognized = deviceFilter.some(device => portName.includes(device));
|
const deviceRecognized = deviceFilter.some(device => portName.includes(device));
|
||||||
const legacyDeviceRecognized = portName.includes('usb');
|
const legacyDeviceRecognized = portName.includes('usb');
|
||||||
if (isWindows && deviceRecognized || isTty && (deviceRecognized || legacyDeviceRecognized)) {
|
if (deviceRecognized || legacyDeviceRecognized) {
|
||||||
this.portPickerElement.val(pathSelect);
|
selectedPort = pathSelect;
|
||||||
this.port_available = true;
|
this.port_available = true;
|
||||||
console.log(`Porthandler detected device ${portName} on port: ${pathSelect}`);
|
console.log(`Porthandler detected device ${portName} on port: ${pathSelect}`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
this.portPicker.selectedPort = selectedPort || DEFAULT_PORT;
|
||||||
|
|
||||||
PortHandler.setPortsInputWidth = function() {
|
|
||||||
|
|
||||||
function findMaxLengthOption(selectEl) {
|
|
||||||
let max = 0;
|
|
||||||
|
|
||||||
$(selectEl.options).each(function () {
|
|
||||||
const textSize = getTextWidth(this.textContent);
|
|
||||||
if (textSize > max) {
|
|
||||||
max = textSize;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
return max;
|
|
||||||
}
|
|
||||||
|
|
||||||
const correction = 32; // account for up/down button and spacing
|
|
||||||
let width = findMaxLengthOption(this.selectList) + correction;
|
|
||||||
|
|
||||||
width = (width > this.initialWidth) ? width : this.initialWidth;
|
|
||||||
|
|
||||||
const portsInput = document.querySelector("div#port-picker #portsinput");
|
|
||||||
portsInput.style.width = `${width}px`;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
PortHandler.port_detected = function(name, code, timeout, ignore_timeout) {
|
PortHandler.port_detected = function(name, code, timeout, ignore_timeout) {
|
||||||
|
|
|
@ -26,6 +26,7 @@ import BuildApi from "./BuildApi";
|
||||||
|
|
||||||
import { isWeb } from "./utils/isWeb";
|
import { isWeb } from "./utils/isWeb";
|
||||||
import { serialShim } from "./serial_shim.js";
|
import { serialShim } from "./serial_shim.js";
|
||||||
|
import { EventBus } from "../components/eventBus";
|
||||||
|
|
||||||
let serial = serialShim();
|
let serial = serialShim();
|
||||||
|
|
||||||
|
@ -73,19 +74,16 @@ export function initializeSerialBackend() {
|
||||||
$('#port-override').val(data.portOverride);
|
$('#port-override').val(data.portOverride);
|
||||||
}
|
}
|
||||||
|
|
||||||
$('div#port-picker #port').change(function (target) {
|
EventBus.$on('ports-input:change', () => GUI.updateManualPortVisibility());
|
||||||
GUI.updateManualPortVisibility();
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
$("div.connect_controls a.connect").on('click', function () {
|
$("div.connect_controls a.connect").on('click', function () {
|
||||||
|
|
||||||
const selectedPort = $('#port').val();
|
const selectedPort = PortHandler.portPicker.selectedPort;
|
||||||
let portName;
|
let portName;
|
||||||
if (selectedPort === 'manual') {
|
if (selectedPort === 'manual') {
|
||||||
portName = $('#port-override').val();
|
portName = $('#port-override').val();
|
||||||
} else {
|
} else {
|
||||||
portName = String($('div#port-picker #port').val());
|
portName = selectedPort;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!GUI.connect_lock && selectedPort !== 'none') {
|
if (!GUI.connect_lock && selectedPort !== 'none') {
|
||||||
|
@ -93,8 +91,8 @@ export function initializeSerialBackend() {
|
||||||
|
|
||||||
GUI.configuration_loaded = false;
|
GUI.configuration_loaded = false;
|
||||||
|
|
||||||
const selected_baud = parseInt($('div#port-picker #baud').val());
|
const selected_baud = PortHandler.portPicker.selectedBauds;
|
||||||
const selectedPort = $('#port').val();
|
const selectedPort = portName;
|
||||||
|
|
||||||
if (selectedPort === 'DFU') {
|
if (selectedPort === 'DFU') {
|
||||||
$('select#baud').hide();
|
$('select#baud').hide();
|
||||||
|
@ -106,10 +104,10 @@ export function initializeSerialBackend() {
|
||||||
GUI.connecting_to = portName;
|
GUI.connecting_to = portName;
|
||||||
|
|
||||||
// lock port select & baud while we are connecting / connected
|
// lock port select & baud while we are connecting / connected
|
||||||
$('div#port-picker #port, div#port-picker #baud, div#port-picker #delay').prop('disabled', true);
|
PortHandler.portPickerDisabled = true;
|
||||||
$('div.connect_controls div.connect_state').text(i18n.getMessage('connecting'));
|
$('div.connect_controls div.connect_state').text(i18n.getMessage('connecting'));
|
||||||
|
|
||||||
const baudRate = parseInt($('#baud').val());
|
const baudRate = selected_baud;
|
||||||
if (selectedPort === 'virtual') {
|
if (selectedPort === 'virtual') {
|
||||||
CONFIGURATOR.virtualMode = true;
|
CONFIGURATOR.virtualMode = true;
|
||||||
CONFIGURATOR.virtualApiVersion = $('#firmware-version-dropdown').val();
|
CONFIGURATOR.virtualApiVersion = $('#firmware-version-dropdown').val();
|
||||||
|
@ -117,7 +115,7 @@ export function initializeSerialBackend() {
|
||||||
// Hack to get virtual working on the web
|
// Hack to get virtual working on the web
|
||||||
serial = serialShim();
|
serial = serialShim();
|
||||||
serial.connect('virtual', {}, onOpenVirtual);
|
serial.connect('virtual', {}, onOpenVirtual);
|
||||||
} else if (isWeb()) {
|
} else {
|
||||||
CONFIGURATOR.virtualMode = false;
|
CONFIGURATOR.virtualMode = false;
|
||||||
serial = serialShim();
|
serial = serialShim();
|
||||||
// Explicitly disconnect the event listeners before attaching the new ones.
|
// Explicitly disconnect the event listeners before attaching the new ones.
|
||||||
|
@ -127,10 +125,7 @@ export function initializeSerialBackend() {
|
||||||
serial.removeEventListener('disconnect', disconnectHandler);
|
serial.removeEventListener('disconnect', disconnectHandler);
|
||||||
serial.addEventListener('disconnect', disconnectHandler);
|
serial.addEventListener('disconnect', disconnectHandler);
|
||||||
|
|
||||||
serial.connect({ baudRate });
|
serial.connect(portName, { baudRate });
|
||||||
} else {
|
|
||||||
serial.connect(portName, { bitrate: selected_baud }, onOpen);
|
|
||||||
toggleStatus();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
|
@ -230,8 +225,7 @@ function finishClose(finishedCallback) {
|
||||||
$('#dialogReportProblems-closebtn').click();
|
$('#dialogReportProblems-closebtn').click();
|
||||||
|
|
||||||
// unlock port select & baud
|
// unlock port select & baud
|
||||||
$('div#port-picker #port').prop('disabled', false);
|
PortHandler.portPickerDisabled = false;
|
||||||
if (!GUI.auto_connect) $('div#port-picker #baud').prop('disabled', false);
|
|
||||||
|
|
||||||
// reset connect / disconnect button
|
// reset connect / disconnect button
|
||||||
$('div.connect_controls a.connect').removeClass('active');
|
$('div.connect_controls a.connect').removeClass('active');
|
||||||
|
@ -275,7 +269,7 @@ function abortConnection() {
|
||||||
$('div#connectbutton a.connect').removeClass('active');
|
$('div#connectbutton a.connect').removeClass('active');
|
||||||
|
|
||||||
// unlock port select & baud
|
// unlock port select & baud
|
||||||
$('div#port-picker #port, div#port-picker #baud, div#port-picker #delay').prop('disabled', false);
|
PortHandler.portPickerDisabled = false;
|
||||||
|
|
||||||
// reset data
|
// reset data
|
||||||
isConnected = false;
|
isConnected = false;
|
||||||
|
|
|
@ -1,3 +1,10 @@
|
||||||
|
export const vendorIdNames = {
|
||||||
|
1027: "FTDI",
|
||||||
|
1155: "STM Electronics",
|
||||||
|
4292: "Silicon Labs",
|
||||||
|
0x2e3c: "AT32",
|
||||||
|
};
|
||||||
|
|
||||||
export const serialDevices = [
|
export const serialDevices = [
|
||||||
{ vendorId: 1027, productId: 24577 }, // FT232R USB UART
|
{ vendorId: 1027, productId: 24577 }, // FT232R USB UART
|
||||||
{ vendorId: 1155, productId: 22336 }, // STM Electronics Virtual COM Port
|
{ vendorId: 1155, productId: 22336 }, // STM Electronics Virtual COM Port
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import { webSerialDevices } from "./serial_devices";
|
import { webSerialDevices, vendorIdNames } from "./serial_devices";
|
||||||
|
|
||||||
async function* streamAsyncIterable(reader, keepReadingFlag) {
|
async function* streamAsyncIterable(reader, keepReadingFlag) {
|
||||||
try {
|
try {
|
||||||
|
@ -30,12 +30,34 @@ class WebSerial extends EventTarget {
|
||||||
|
|
||||||
this.logHead = "SERIAL: ";
|
this.logHead = "SERIAL: ";
|
||||||
|
|
||||||
|
this.port_counter = 0;
|
||||||
|
this.ports = [];
|
||||||
this.port = null;
|
this.port = null;
|
||||||
this.reader = null;
|
this.reader = null;
|
||||||
this.writer = null;
|
this.writer = null;
|
||||||
this.reading = false;
|
this.reading = false;
|
||||||
|
|
||||||
this.connect = this.connect.bind(this);
|
this.connect = this.connect.bind(this);
|
||||||
|
|
||||||
|
navigator.serial.addEventListener("connect", e => this.handleNewDevice(e.target));
|
||||||
|
navigator.serial.addEventListener("disconnect", e => this.handleRemovedDevice(e.target));
|
||||||
|
|
||||||
|
this.loadDevices();
|
||||||
|
}
|
||||||
|
|
||||||
|
handleNewDevice(device) {
|
||||||
|
|
||||||
|
const added = this.createPort(device);
|
||||||
|
this.ports.push(added);
|
||||||
|
this.dispatchEvent(new CustomEvent("addedDevice", { detail: added }));
|
||||||
|
|
||||||
|
return added;
|
||||||
|
}
|
||||||
|
|
||||||
|
handleRemovedDevice(device) {
|
||||||
|
const removed = this.ports.find(port => port.port === device);
|
||||||
|
this.ports = this.ports.filter(port => port.port !== device);
|
||||||
|
this.dispatchEvent(new CustomEvent("removedDevice", { detail: removed }));
|
||||||
}
|
}
|
||||||
|
|
||||||
handleReceiveBytes(info) {
|
handleReceiveBytes(info) {
|
||||||
|
@ -44,16 +66,56 @@ class WebSerial extends EventTarget {
|
||||||
|
|
||||||
handleDisconnect() {
|
handleDisconnect() {
|
||||||
this.removeEventListener('receive', this.handleReceiveBytes);
|
this.removeEventListener('receive', this.handleReceiveBytes);
|
||||||
this.removeEventListener('disconnect', this.handleDisconnect);
|
this.dispatchEvent(new CustomEvent("disconnect", { detail: false }));
|
||||||
}
|
}
|
||||||
|
|
||||||
async connect(options) {
|
getConnectedPort() {
|
||||||
this.openRequested = true;
|
return this.port;
|
||||||
this.port = await navigator.serial.requestPort({
|
}
|
||||||
|
|
||||||
|
createPort(port) {
|
||||||
|
return {
|
||||||
|
path: `D${this.port_counter}`,
|
||||||
|
displayName: `Betaflight ${vendorIdNames[port.getInfo().usbVendorId]}`,
|
||||||
|
vendorId: port.getInfo().usbVendorId,
|
||||||
|
productId: port.getInfo().usbProductId,
|
||||||
|
port: port,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
async loadDevices() {
|
||||||
|
const ports = await navigator.serial.getPorts({
|
||||||
filters: webSerialDevices,
|
filters: webSerialDevices,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
this.port_counter = 1;
|
||||||
|
this.ports = ports.map(function (port) {
|
||||||
|
return this.createPort(port);
|
||||||
|
}, this);
|
||||||
|
};
|
||||||
|
|
||||||
|
async requestPermissionDevice() {
|
||||||
|
const permissionPort = await navigator.serial.requestPort({
|
||||||
|
filters: webSerialDevices,
|
||||||
|
});
|
||||||
|
const found = this.ports.find(port => port.port === device);
|
||||||
|
if (!found) {
|
||||||
|
return this.handleNewDevice(permissionPort);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
};
|
||||||
|
|
||||||
|
async getDevices() {
|
||||||
|
return this.ports;
|
||||||
|
}
|
||||||
|
|
||||||
|
async connect(path, options) {
|
||||||
|
this.openRequested = true;
|
||||||
|
|
||||||
|
this.port = this.ports.find(device => device.path === path).port;
|
||||||
|
|
||||||
await this.port.open(options);
|
await this.port.open(options);
|
||||||
|
|
||||||
const connectionInfo = this.port.getInfo();
|
const connectionInfo = this.port.getInfo();
|
||||||
this.connectionInfo = connectionInfo;
|
this.connectionInfo = connectionInfo;
|
||||||
this.writer = this.port.writable.getWriter();
|
this.writer = this.port.writable.getWriter();
|
||||||
|
@ -69,7 +131,6 @@ class WebSerial extends EventTarget {
|
||||||
this.openRequested = false;
|
this.openRequested = false;
|
||||||
|
|
||||||
this.addEventListener("receive", this.handleReceiveBytes);
|
this.addEventListener("receive", this.handleReceiveBytes);
|
||||||
this.addEventListener('disconnect', this.handleDisconnect);
|
|
||||||
|
|
||||||
console.log(
|
console.log(
|
||||||
`${this.logHead} Connection opened with ID: ${connectionInfo.connectionId}, Baud: ${options.baudRate}`,
|
`${this.logHead} Connection opened with ID: ${connectionInfo.connectionId}, Baud: ${options.baudRate}`,
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue