mirror of
https://github.com/arduino/arduino-ide.git
synced 2025-11-13 12:19:29 +00:00
Updated port discovery to support unknown boards
From now on, we do not retrieve the ports from the attached boards. A board can be unknown but the port is still relevant. Signed-off-by: Akos Kitta <kittaakos@typefox.io>
This commit is contained in:
@@ -165,7 +165,10 @@ export class ArduinoFrontendContribution implements TabBarToolbarContribution, C
|
||||
|
||||
this.registerSketchesInMenu(this.menuRegistry);
|
||||
|
||||
this.boardsService.getAttachedBoards().then(({ boards }) => this.boardsServiceClient.tryReconnect(boards));
|
||||
Promise.all([
|
||||
this.boardsService.getAttachedBoards(),
|
||||
this.boardsService.getAvailablePorts()
|
||||
]).then(([{ boards }, { ports }]) => this.boardsServiceClient.tryReconnect(boards, ports));
|
||||
}
|
||||
|
||||
registerToolbarItems(registry: TabBarToolbarRegistry): void {
|
||||
@@ -270,7 +273,7 @@ export class ArduinoFrontendContribution implements TabBarToolbarContribution, C
|
||||
if (!selectedPort) {
|
||||
throw new Error('No ports selected. Please select a port.');
|
||||
}
|
||||
await this.coreService.upload({ uri: uri.toString(), board: boardsConfig.selectedBoard, port: selectedPort });
|
||||
await this.coreService.upload({ uri: uri.toString(), board: boardsConfig.selectedBoard, port: selectedPort.address });
|
||||
} catch (e) {
|
||||
await this.messageService.error(e.toString());
|
||||
} finally {
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
import * as React from 'react';
|
||||
import { DisposableCollection } from '@theia/core';
|
||||
import { BoardsService, Board, AttachedSerialBoard, AttachedBoardsChangeEvent } from '../../common/protocol/boards-service';
|
||||
import { BoardsService, Board, Port, AttachedSerialBoard, AttachedBoardsChangeEvent } from '../../common/protocol/boards-service';
|
||||
import { BoardsServiceClientImpl } from './boards-service-client-impl';
|
||||
|
||||
export namespace BoardsConfig {
|
||||
|
||||
export interface Config {
|
||||
selectedBoard?: Board;
|
||||
selectedPort?: string;
|
||||
selectedPort?: Port;
|
||||
}
|
||||
|
||||
export interface Props {
|
||||
@@ -19,7 +19,8 @@ export namespace BoardsConfig {
|
||||
|
||||
export interface State extends Config {
|
||||
searchResults: Array<Board & { packageName: string }>;
|
||||
knownPorts: string[];
|
||||
knownPorts: Port[];
|
||||
showAllPorts: boolean;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -47,7 +48,7 @@ export abstract class Item<T> extends React.Component<{
|
||||
{label}
|
||||
</div>
|
||||
{!detail ? '' : <div className='detail'>{detail}</div>}
|
||||
{!selected ? '' : <div className='selected-icon'><i className='fa fa-check'/></div>}
|
||||
{!selected ? '' : <div className='selected-icon'><i className='fa fa-check' /></div>}
|
||||
</div>;
|
||||
}
|
||||
|
||||
@@ -68,16 +69,17 @@ export class BoardsConfig extends React.Component<BoardsConfig.Props, BoardsConf
|
||||
this.state = {
|
||||
searchResults: [],
|
||||
knownPorts: [],
|
||||
showAllPorts: false,
|
||||
...boardsConfig
|
||||
}
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
this.updateBoards();
|
||||
this.props.boardsService.getAttachedBoards().then(({ boards }) => this.updatePorts(boards));
|
||||
this.props.boardsService.getAvailablePorts().then(({ ports }) => this.updatePorts(ports));
|
||||
const { boardsServiceClient: client } = this.props;
|
||||
this.toDispose.pushAll([
|
||||
client.onBoardsChanged(event => this.updatePorts(event.newState.boards, AttachedBoardsChangeEvent.diff(event).detached)),
|
||||
client.onBoardsChanged(event => this.updatePorts(event.newState.ports, AttachedBoardsChangeEvent.diff(event).detached.ports)),
|
||||
client.onBoardsConfigChanged(({ selectedBoard, selectedPort }) => {
|
||||
this.setState({ selectedBoard, selectedPort }, () => this.fireConfigChanged());
|
||||
})
|
||||
@@ -101,11 +103,11 @@ export class BoardsConfig extends React.Component<BoardsConfig.Props, BoardsConf
|
||||
this.queryBoards({ query }).then(({ searchResults }) => this.setState({ searchResults }));
|
||||
}
|
||||
|
||||
protected updatePorts = (boards: Board[] = [], detachedBoards: Board[] = []) => {
|
||||
this.queryPorts(Promise.resolve({ boards })).then(({ knownPorts }) => {
|
||||
protected updatePorts = (ports: Port[] = [], removedPorts: Port[] = []) => {
|
||||
this.queryPorts(Promise.resolve({ ports })).then(({ knownPorts }) => {
|
||||
let { selectedPort } = this.state;
|
||||
const removedPorts = detachedBoards.filter(AttachedSerialBoard.is).map(({ port }) => port);
|
||||
if (!!selectedPort && removedPorts.indexOf(selectedPort) !== -1) {
|
||||
// If the currently selected port is not available anymore, unset the selected port.
|
||||
if (removedPorts.some(port => Port.equals(port, selectedPort))) {
|
||||
selectedPort = undefined;
|
||||
}
|
||||
this.setState({ knownPorts, selectedPort }, () => this.fireConfigChanged());
|
||||
@@ -130,18 +132,24 @@ export class BoardsConfig extends React.Component<BoardsConfig.Props, BoardsConf
|
||||
return this.props.boardsService.getAttachedBoards();
|
||||
}
|
||||
|
||||
protected queryPorts = (attachedBoards: Promise<{ boards: Board[] }> = this.attachedBoards) => {
|
||||
return new Promise<{ knownPorts: string[] }>(resolve => {
|
||||
attachedBoards
|
||||
.then(({ boards }) => boards
|
||||
.filter(AttachedSerialBoard.is)
|
||||
.map(({ port }) => port)
|
||||
.sort())
|
||||
protected get availablePorts(): Promise<{ ports: Port[] }> {
|
||||
return this.props.boardsService.getAvailablePorts();
|
||||
}
|
||||
|
||||
protected queryPorts = (availablePorts: Promise<{ ports: Port[] }> = this.availablePorts) => {
|
||||
return new Promise<{ knownPorts: Port[] }>(resolve => {
|
||||
availablePorts
|
||||
.then(({ ports }) => ports
|
||||
.sort(Port.compare))
|
||||
.then(knownPorts => resolve({ knownPorts }));
|
||||
});
|
||||
}
|
||||
|
||||
protected selectPort = (selectedPort: string | undefined) => {
|
||||
protected toggleFilterPorts = () => {
|
||||
this.setState({ showAllPorts: !this.state.showAllPorts });
|
||||
}
|
||||
|
||||
protected selectPort = (selectedPort: Port | undefined) => {
|
||||
this.setState({ selectedPort }, () => this.fireConfigChanged());
|
||||
}
|
||||
|
||||
@@ -156,17 +164,20 @@ export class BoardsConfig extends React.Component<BoardsConfig.Props, BoardsConf
|
||||
render(): React.ReactNode {
|
||||
return <div className='body'>
|
||||
{this.renderContainer('boards', this.renderBoards.bind(this))}
|
||||
{this.renderContainer('ports', this.renderPorts.bind(this))}
|
||||
{this.renderContainer('ports', this.renderPorts.bind(this), this.renderPortsFooter.bind(this))}
|
||||
</div>;
|
||||
}
|
||||
|
||||
protected renderContainer(title: string, contentRenderer: () => React.ReactNode): React.ReactNode {
|
||||
protected renderContainer(title: string, contentRenderer: () => React.ReactNode, footerRenderer?: () => React.ReactNode): React.ReactNode {
|
||||
return <div className='container'>
|
||||
<div className='content'>
|
||||
<div className='title'>
|
||||
{title}
|
||||
</div>
|
||||
{contentRenderer()}
|
||||
<div className='footer'>
|
||||
{(footerRenderer ? footerRenderer() : '')}
|
||||
</div>
|
||||
</div>
|
||||
</div>;
|
||||
}
|
||||
@@ -214,7 +225,9 @@ export class BoardsConfig extends React.Component<BoardsConfig.Props, BoardsConf
|
||||
}
|
||||
|
||||
protected renderPorts(): React.ReactNode {
|
||||
return !this.state.knownPorts.length ?
|
||||
const filter = this.state.showAllPorts ? () => true : Port.isBoardPort;
|
||||
const ports = this.state.knownPorts.filter(filter);
|
||||
return !ports.length ?
|
||||
(
|
||||
<div className='loading noselect'>
|
||||
No ports discovered
|
||||
@@ -222,17 +235,31 @@ export class BoardsConfig extends React.Component<BoardsConfig.Props, BoardsConf
|
||||
) :
|
||||
(
|
||||
<div className='ports list'>
|
||||
{this.state.knownPorts.map(port => <Item<string>
|
||||
key={port}
|
||||
{ports.map(port => <Item<Port>
|
||||
key={Port.toString(port)}
|
||||
item={port}
|
||||
label={port}
|
||||
selected={this.state.selectedPort === port}
|
||||
label={Port.toString(port)}
|
||||
selected={Port.equals(this.state.selectedPort, port)}
|
||||
onClick={this.selectPort}
|
||||
/>)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
protected renderPortsFooter(): React.ReactNode {
|
||||
return <div className='noselect'>
|
||||
<label
|
||||
title='Shows all available ports when enabled'>
|
||||
<input
|
||||
type='checkbox'
|
||||
defaultChecked={this.state.showAllPorts}
|
||||
onChange={this.toggleFilterPorts}
|
||||
/>
|
||||
<span>Show all ports</span>
|
||||
</label>
|
||||
</div>;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export namespace BoardsConfig {
|
||||
@@ -244,7 +271,7 @@ export namespace BoardsConfig {
|
||||
if (AttachedSerialBoard.is(other)) {
|
||||
return !!selectedBoard
|
||||
&& Board.equals(other, selectedBoard)
|
||||
&& selectedPort === other.port;
|
||||
&& Port.sameAs(selectedPort, other.port);
|
||||
}
|
||||
return sameAs(config, other);
|
||||
}
|
||||
@@ -260,7 +287,7 @@ export namespace BoardsConfig {
|
||||
return options.default;
|
||||
}
|
||||
const { name } = selectedBoard;
|
||||
return `${name}${port ? ' at ' + port : ''}`;
|
||||
return `${name}${port ? ' at ' + Port.toString(port) : ''}`;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -3,9 +3,8 @@ import { Emitter } from '@theia/core/lib/common/event';
|
||||
import { ILogger } from '@theia/core/lib/common/logger';
|
||||
import { LocalStorageService } from '@theia/core/lib/browser/storage-service';
|
||||
import { RecursiveRequired } from '../../common/types';
|
||||
import { BoardsServiceClient, AttachedBoardsChangeEvent, BoardInstalledEvent, AttachedSerialBoard, Board } from '../../common/protocol/boards-service';
|
||||
import { BoardsServiceClient, AttachedBoardsChangeEvent, BoardInstalledEvent, AttachedSerialBoard, Board, Port } from '../../common/protocol/boards-service';
|
||||
import { BoardsConfig } from './boards-config';
|
||||
import { MaybePromise } from '@theia/core';
|
||||
|
||||
@injectable()
|
||||
export class BoardsServiceClientImpl implements BoardsServiceClient {
|
||||
@@ -40,29 +39,27 @@ export class BoardsServiceClientImpl implements BoardsServiceClient {
|
||||
}
|
||||
|
||||
notifyAttachedBoardsChanged(event: AttachedBoardsChangeEvent): void {
|
||||
this.logger.info('Attached boards changed: ', JSON.stringify(event));
|
||||
this.logger.info('Attached boards and available ports changed: ', JSON.stringify(event));
|
||||
const { detached, attached } = AttachedBoardsChangeEvent.diff(event);
|
||||
const detachedBoards = detached.filter(AttachedSerialBoard.is).map(({ port }) => port);
|
||||
const { selectedPort, selectedBoard } = this.boardsConfig;
|
||||
this.onAttachedBoardsChangedEmitter.fire(event);
|
||||
// Dynamically unset the port if the selected board was an attached one and we detached it.
|
||||
if (!!selectedPort && detachedBoards.indexOf(selectedPort) !== -1) {
|
||||
// Dynamically unset the port if is not available anymore. A port can be "detached" when removing a board.
|
||||
if (detached.ports.some(port => Port.equals(selectedPort, port))) {
|
||||
this.boardsConfig = {
|
||||
selectedBoard,
|
||||
selectedPort: undefined
|
||||
};
|
||||
}
|
||||
// Try to reconnect.
|
||||
this.tryReconnect(attached);
|
||||
this.tryReconnect(attached.boards, attached.ports);
|
||||
}
|
||||
|
||||
async tryReconnect(attachedBoards: MaybePromise<Array<Board>>): Promise<boolean> {
|
||||
const boards = await attachedBoards;
|
||||
async tryReconnect(attachedBoards: Board[], availablePorts: Port[]): Promise<boolean> {
|
||||
if (this.latestValidBoardsConfig && !this.canUploadTo(this.boardsConfig)) {
|
||||
for (const board of boards.filter(AttachedSerialBoard.is)) {
|
||||
for (const board of attachedBoards.filter(AttachedSerialBoard.is)) {
|
||||
if (this.latestValidBoardsConfig.selectedBoard.fqbn === board.fqbn
|
||||
&& this.latestValidBoardsConfig.selectedBoard.name === board.name
|
||||
&& this.latestValidBoardsConfig.selectedPort === board.port) {
|
||||
&& Port.sameAs(this.latestValidBoardsConfig.selectedPort, board.port)) {
|
||||
|
||||
this.boardsConfig = this.latestValidBoardsConfig;
|
||||
return true;
|
||||
@@ -70,13 +67,13 @@ export class BoardsServiceClientImpl implements BoardsServiceClient {
|
||||
}
|
||||
// If we could not find an exact match, we compare the board FQBN-name pairs and ignore the port, as it might have changed.
|
||||
// See documentation on `latestValidBoardsConfig`.
|
||||
for (const board of boards.filter(AttachedSerialBoard.is)) {
|
||||
for (const board of attachedBoards.filter(AttachedSerialBoard.is)) {
|
||||
if (this.latestValidBoardsConfig.selectedBoard.fqbn === board.fqbn
|
||||
&& this.latestValidBoardsConfig.selectedBoard.name === board.name) {
|
||||
|
||||
this.boardsConfig = {
|
||||
...this.latestValidBoardsConfig,
|
||||
selectedPort: board.port
|
||||
selectedPort: availablePorts.find(port => Port.sameAs(port, board.port))
|
||||
};
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import * as React from 'react';
|
||||
import * as ReactDOM from 'react-dom';
|
||||
import { CommandRegistry, DisposableCollection } from '@theia/core';
|
||||
import { BoardsService, Board, AttachedSerialBoard } from '../../common/protocol/boards-service';
|
||||
import { BoardsService, Board, AttachedSerialBoard, Port } from '../../common/protocol/boards-service';
|
||||
import { ArduinoCommands } from '../arduino-commands';
|
||||
import { BoardsServiceClientImpl } from './boards-service-client-impl';
|
||||
import { BoardsConfig } from './boards-config';
|
||||
@@ -88,6 +88,7 @@ export namespace BoardsToolBarItem {
|
||||
export interface State {
|
||||
boardsConfig: BoardsConfig.Config;
|
||||
attachedBoards: Board[];
|
||||
availablePorts: Port[];
|
||||
coords: BoardsDropDownListCoords | 'hidden';
|
||||
}
|
||||
}
|
||||
@@ -104,6 +105,7 @@ export class BoardsToolBarItem extends React.Component<BoardsToolBarItem.Props,
|
||||
this.state = {
|
||||
boardsConfig: this.props.boardsServiceClient.boardsConfig,
|
||||
attachedBoards: [],
|
||||
availablePorts: [],
|
||||
coords: 'hidden'
|
||||
};
|
||||
|
||||
@@ -116,10 +118,13 @@ export class BoardsToolBarItem extends React.Component<BoardsToolBarItem.Props,
|
||||
const { boardsServiceClient: client, boardService } = this.props;
|
||||
this.toDispose.pushAll([
|
||||
client.onBoardsConfigChanged(boardsConfig => this.setState({ boardsConfig })),
|
||||
client.onBoardsChanged(({ newState }) => this.setState({ attachedBoards: newState.boards }))
|
||||
client.onBoardsChanged(({ newState }) => this.setState({ attachedBoards: newState.boards, availablePorts: newState.ports }))
|
||||
]);
|
||||
boardService.getAttachedBoards().then(({ boards: attachedBoards }) => {
|
||||
this.setState({ attachedBoards })
|
||||
Promise.all([
|
||||
boardService.getAttachedBoards(),
|
||||
boardService.getAvailablePorts()
|
||||
]).then(([{boards: attachedBoards}, { ports: availablePorts }]) => {
|
||||
this.setState({ attachedBoards, availablePorts })
|
||||
});
|
||||
}
|
||||
|
||||
@@ -149,29 +154,32 @@ export class BoardsToolBarItem extends React.Component<BoardsToolBarItem.Props,
|
||||
};
|
||||
|
||||
render(): React.ReactNode {
|
||||
const { boardsConfig, coords, attachedBoards } = this.state;
|
||||
const boardsConfigText = BoardsConfig.Config.toString(boardsConfig, { default: 'no board selected' });
|
||||
const { boardsConfig, coords, attachedBoards, availablePorts } = this.state;
|
||||
const title = BoardsConfig.Config.toString(boardsConfig, { default: 'no board selected' });
|
||||
const configuredBoard = attachedBoards
|
||||
.filter(AttachedSerialBoard.is)
|
||||
.filter(board => availablePorts.some(port => Port.sameAs(port, board.port)))
|
||||
.filter(board => BoardsConfig.Config.sameAs(boardsConfig, board)).shift();
|
||||
|
||||
const items = attachedBoards.filter(AttachedSerialBoard.is).map(board => ({
|
||||
label: `${board.name} at ${board.port}`,
|
||||
selected: configuredBoard === board,
|
||||
onClick: () => this.props.boardsServiceClient.boardsConfig = {
|
||||
selectedBoard: board,
|
||||
selectedPort: board.port
|
||||
onClick: () => {
|
||||
this.props.boardsServiceClient.boardsConfig = {
|
||||
selectedBoard: board,
|
||||
selectedPort: availablePorts.find(port => Port.sameAs(port, board.port))
|
||||
}
|
||||
}
|
||||
}));
|
||||
|
||||
return <React.Fragment>
|
||||
<div className='arduino-boards-toolbar-item-container'>
|
||||
<div className='arduino-boards-toolbar-item' title={boardsConfigText}>
|
||||
<div className='arduino-boards-toolbar-item' title={title}>
|
||||
<div className='inner-container' onClick={this.show}>
|
||||
<span className={!configuredBoard ? 'fa fa-times notAttached' : ''}/>
|
||||
<div className='label noWrapInfo'>
|
||||
<div className='noWrapInfo noselect'>
|
||||
{boardsConfigText}
|
||||
{title}
|
||||
</div>
|
||||
</div>
|
||||
<span className='fa fa-caret-down caret'/>
|
||||
|
||||
@@ -268,7 +268,7 @@ export class MonitorWidget extends ReactWidget implements StatefulWidget {
|
||||
return {
|
||||
baudRate,
|
||||
board: selectedBoard,
|
||||
port: selectedPort
|
||||
port: selectedPort.address
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -73,11 +73,18 @@ div#select-board-dialog .selectBoardContainer .body .list .item.selected i{
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
#select-board-dialog .selectBoardContainer .body .container .content .footer {
|
||||
padding: 10px 5px 10px 0px;
|
||||
}
|
||||
|
||||
#select-board-dialog .selectBoardContainer .body .container .content .loading {
|
||||
font-size: var(--theia-ui-font-size1);
|
||||
color: #7f8c8d;
|
||||
padding: 10px 5px 10px 10px;
|
||||
text-transform: uppercase;
|
||||
/* The max, min-height comes from `.body .list` 265px + 47px top padding - 2 * 10px top padding */
|
||||
max-height: 292px;
|
||||
min-height: 292px;
|
||||
}
|
||||
|
||||
#select-board-dialog .selectBoardContainer .body .list .item {
|
||||
|
||||
Reference in New Issue
Block a user