mirror of
https://github.com/arduino/arduino-ide.git
synced 2025-07-18 08:46:33 +00:00
fix 180: prevent erroneous "auto-reconnect"(s) in board selector (#1328)
* ensure auto-select doesn't select wrong port prevent auto-select of unattached boards Co-authored-by: Francesco Spissu <francescospissu@users.noreply.github.com> * clean up * add "uploadInProgress" prop to boards change event * remove unused methods and deps * [WIP]: leverage upload event to derived new port * remove timeout * refine port selection logic * clean up naming * refine port selection logic & add delayed clean up * renaming & refactoring * method syntax & remove unnecessary methods
This commit is contained in:
parent
125bd64c91
commit
cc5cf3b165
@ -65,6 +65,10 @@ export class BoardsServiceProvider implements FrontendApplicationContribution {
|
|||||||
protected _availablePorts: Port[] = [];
|
protected _availablePorts: Port[] = [];
|
||||||
protected _availableBoards: AvailableBoard[] = [];
|
protected _availableBoards: AvailableBoard[] = [];
|
||||||
|
|
||||||
|
private lastBoardsConfigOnUpload: BoardsConfig.Config | undefined;
|
||||||
|
private lastAvailablePortsOnUpload: Port[] | undefined;
|
||||||
|
private boardConfigToAutoSelect: BoardsConfig.Config | undefined;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Unlike `onAttachedBoardsChanged` this event fires when the user modifies the selected board in the IDE.\
|
* Unlike `onAttachedBoardsChanged` this event fires when the user modifies the selected board in the IDE.\
|
||||||
* This event also fires, when the boards package was not available for the currently selected board,
|
* This event also fires, when the boards package was not available for the currently selected board,
|
||||||
@ -112,6 +116,84 @@ export class BoardsServiceProvider implements FrontendApplicationContribution {
|
|||||||
return this._reconciled.promise;
|
return this._reconciled.promise;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
snapshotBoardDiscoveryOnUpload(): void {
|
||||||
|
this.lastBoardsConfigOnUpload = this._boardsConfig;
|
||||||
|
this.lastAvailablePortsOnUpload = this._availablePorts;
|
||||||
|
}
|
||||||
|
|
||||||
|
clearBoardDiscoverySnapshot(): void {
|
||||||
|
this.lastBoardsConfigOnUpload = undefined;
|
||||||
|
this.lastAvailablePortsOnUpload = undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
private portToAutoSelectCanBeDerived(): boolean {
|
||||||
|
return Boolean(
|
||||||
|
this.lastBoardsConfigOnUpload && this.lastAvailablePortsOnUpload
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
attemptPostUploadAutoSelect(): void {
|
||||||
|
setTimeout(() => {
|
||||||
|
if (this.portToAutoSelectCanBeDerived()) {
|
||||||
|
this.attemptAutoSelect({
|
||||||
|
ports: this._availablePorts,
|
||||||
|
boards: this._availableBoards,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}, 2000); // 2 second delay same as IDE 1.8
|
||||||
|
}
|
||||||
|
|
||||||
|
private attemptAutoSelect(
|
||||||
|
newState: AttachedBoardsChangeEvent['newState']
|
||||||
|
): void {
|
||||||
|
this.deriveBoardConfigToAutoSelect(newState);
|
||||||
|
this.tryReconnect();
|
||||||
|
}
|
||||||
|
|
||||||
|
private deriveBoardConfigToAutoSelect(
|
||||||
|
newState: AttachedBoardsChangeEvent['newState']
|
||||||
|
): void {
|
||||||
|
if (!this.portToAutoSelectCanBeDerived()) {
|
||||||
|
this.boardConfigToAutoSelect = undefined;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const oldPorts = this.lastAvailablePortsOnUpload!;
|
||||||
|
const { ports: newPorts, boards: newBoards } = newState;
|
||||||
|
|
||||||
|
const appearedPorts =
|
||||||
|
oldPorts.length > 0
|
||||||
|
? newPorts.filter((newPort: Port) =>
|
||||||
|
oldPorts.every((oldPort: Port) => !Port.sameAs(newPort, oldPort))
|
||||||
|
)
|
||||||
|
: newPorts;
|
||||||
|
|
||||||
|
for (const port of appearedPorts) {
|
||||||
|
const boardOnAppearedPort = newBoards.find((board: Board) =>
|
||||||
|
Port.sameAs(board.port, port)
|
||||||
|
);
|
||||||
|
|
||||||
|
const lastBoardsConfigOnUpload = this.lastBoardsConfigOnUpload!;
|
||||||
|
|
||||||
|
if (
|
||||||
|
boardOnAppearedPort &&
|
||||||
|
lastBoardsConfigOnUpload.selectedBoard &&
|
||||||
|
Board.sameAs(
|
||||||
|
boardOnAppearedPort,
|
||||||
|
lastBoardsConfigOnUpload.selectedBoard
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
this.clearBoardDiscoverySnapshot();
|
||||||
|
|
||||||
|
this.boardConfigToAutoSelect = {
|
||||||
|
selectedBoard: boardOnAppearedPort,
|
||||||
|
selectedPort: port,
|
||||||
|
};
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
protected notifyAttachedBoardsChanged(
|
protected notifyAttachedBoardsChanged(
|
||||||
event: AttachedBoardsChangeEvent
|
event: AttachedBoardsChangeEvent
|
||||||
): void {
|
): void {
|
||||||
@ -120,10 +202,18 @@ export class BoardsServiceProvider implements FrontendApplicationContribution {
|
|||||||
this.logger.info(AttachedBoardsChangeEvent.toString(event));
|
this.logger.info(AttachedBoardsChangeEvent.toString(event));
|
||||||
this.logger.info('------------------------------------------');
|
this.logger.info('------------------------------------------');
|
||||||
}
|
}
|
||||||
|
|
||||||
this._attachedBoards = event.newState.boards;
|
this._attachedBoards = event.newState.boards;
|
||||||
this._availablePorts = event.newState.ports;
|
this._availablePorts = event.newState.ports;
|
||||||
this.onAvailablePortsChangedEmitter.fire(this._availablePorts);
|
this.onAvailablePortsChangedEmitter.fire(this._availablePorts);
|
||||||
this.reconcileAvailableBoards().then(() => this.tryReconnect());
|
this.reconcileAvailableBoards().then(() => {
|
||||||
|
const { uploadInProgress } = event;
|
||||||
|
// avoid attempting "auto-selection" while an
|
||||||
|
// upload is in progress
|
||||||
|
if (!uploadInProgress) {
|
||||||
|
this.attemptAutoSelect(event.newState);
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
protected notifyPlatformInstalled(event: { item: BoardsPackage }): void {
|
protected notifyPlatformInstalled(event: { item: BoardsPackage }): void {
|
||||||
@ -239,24 +329,12 @@ export class BoardsServiceProvider implements FrontendApplicationContribution {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// 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`.
|
if (!this.boardConfigToAutoSelect) return false;
|
||||||
for (const board of this.availableBoards.filter(
|
|
||||||
({ state }) => state !== AvailableBoard.State.incomplete
|
this.boardsConfig = this.boardConfigToAutoSelect;
|
||||||
)) {
|
this.boardConfigToAutoSelect = undefined;
|
||||||
if (
|
return true;
|
||||||
this.latestValidBoardsConfig.selectedBoard.fqbn === board.fqbn &&
|
|
||||||
this.latestValidBoardsConfig.selectedBoard.name === board.name &&
|
|
||||||
this.latestValidBoardsConfig.selectedPort.protocol ===
|
|
||||||
board.port?.protocol
|
|
||||||
) {
|
|
||||||
this.boardsConfig = {
|
|
||||||
...this.latestValidBoardsConfig,
|
|
||||||
selectedPort: board.port,
|
|
||||||
};
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -450,6 +528,11 @@ export class BoardsServiceProvider implements FrontendApplicationContribution {
|
|||||||
const board = attachedBoards.find(({ port }) =>
|
const board = attachedBoards.find(({ port }) =>
|
||||||
Port.sameAs(boardPort, port)
|
Port.sameAs(boardPort, port)
|
||||||
);
|
);
|
||||||
|
// "board" will always be falsey for
|
||||||
|
// port that was originally mapped
|
||||||
|
// to unknown board and then selected
|
||||||
|
// manually by user
|
||||||
|
|
||||||
const lastSelectedBoard = await this.getLastSelectedBoardOnPort(
|
const lastSelectedBoard = await this.getLastSelectedBoardOnPort(
|
||||||
boardPort
|
boardPort
|
||||||
);
|
);
|
||||||
@ -468,7 +551,9 @@ export class BoardsServiceProvider implements FrontendApplicationContribution {
|
|||||||
availableBoard = {
|
availableBoard = {
|
||||||
...lastSelectedBoard,
|
...lastSelectedBoard,
|
||||||
state: AvailableBoard.State.guessed,
|
state: AvailableBoard.State.guessed,
|
||||||
selected: BoardsConfig.Config.sameAs(boardsConfig, lastSelectedBoard),
|
selected:
|
||||||
|
BoardsConfig.Config.sameAs(boardsConfig, lastSelectedBoard) &&
|
||||||
|
Port.sameAs(boardPort, boardsConfig.selectedPort), // to avoid double selection
|
||||||
port: boardPort,
|
port: boardPort,
|
||||||
};
|
};
|
||||||
} else {
|
} else {
|
||||||
@ -483,7 +568,7 @@ export class BoardsServiceProvider implements FrontendApplicationContribution {
|
|||||||
|
|
||||||
if (
|
if (
|
||||||
boardsConfig.selectedBoard &&
|
boardsConfig.selectedBoard &&
|
||||||
!availableBoards.some(({ selected }) => selected)
|
availableBoards.every(({ selected }) => !selected)
|
||||||
) {
|
) {
|
||||||
// If the selected board has the same port of an unknown board
|
// If the selected board has the same port of an unknown board
|
||||||
// that is already in availableBoards we might get a duplicate port.
|
// that is already in availableBoards we might get a duplicate port.
|
||||||
|
@ -190,6 +190,7 @@ export class UploadSketch extends CoreServiceContribution {
|
|||||||
// toggle the toolbar button and menu item state.
|
// toggle the toolbar button and menu item state.
|
||||||
// uploadInProgress will be set to false whether the upload fails or not
|
// uploadInProgress will be set to false whether the upload fails or not
|
||||||
this.uploadInProgress = true;
|
this.uploadInProgress = true;
|
||||||
|
this.boardsServiceProvider.snapshotBoardDiscoveryOnUpload();
|
||||||
this.onDidChangeEmitter.fire();
|
this.onDidChangeEmitter.fire();
|
||||||
this.clearVisibleNotification();
|
this.clearVisibleNotification();
|
||||||
|
|
||||||
@ -243,6 +244,7 @@ export class UploadSketch extends CoreServiceContribution {
|
|||||||
this.handleError(e);
|
this.handleError(e);
|
||||||
} finally {
|
} finally {
|
||||||
this.uploadInProgress = false;
|
this.uploadInProgress = false;
|
||||||
|
this.boardsServiceProvider.attemptPostUploadAutoSelect();
|
||||||
this.onDidChangeEmitter.fire();
|
this.onDidChangeEmitter.fire();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -25,6 +25,7 @@ export namespace AvailablePorts {
|
|||||||
export interface AttachedBoardsChangeEvent {
|
export interface AttachedBoardsChangeEvent {
|
||||||
readonly oldState: Readonly<{ boards: Board[]; ports: Port[] }>;
|
readonly oldState: Readonly<{ boards: Board[]; ports: Port[] }>;
|
||||||
readonly newState: Readonly<{ boards: Board[]; ports: Port[] }>;
|
readonly newState: Readonly<{ boards: Board[]; ports: Port[] }>;
|
||||||
|
readonly uploadInProgress: boolean;
|
||||||
}
|
}
|
||||||
export namespace AttachedBoardsChangeEvent {
|
export namespace AttachedBoardsChangeEvent {
|
||||||
export function isEmpty(event: AttachedBoardsChangeEvent): boolean {
|
export function isEmpty(event: AttachedBoardsChangeEvent): boolean {
|
||||||
|
@ -54,6 +54,8 @@ export class BoardDiscovery
|
|||||||
private readonly onStreamDidCancelEmitter = new Emitter<void>(); // when the watcher is canceled by the IDE2
|
private readonly onStreamDidCancelEmitter = new Emitter<void>(); // when the watcher is canceled by the IDE2
|
||||||
private readonly toDisposeOnStopWatch = new DisposableCollection();
|
private readonly toDisposeOnStopWatch = new DisposableCollection();
|
||||||
|
|
||||||
|
private uploadInProgress = false;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Keys are the `address` of the ports.
|
* Keys are the `address` of the ports.
|
||||||
*
|
*
|
||||||
@ -123,6 +125,10 @@ export class BoardDiscovery
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
setUploadInProgress(uploadAttemptInProgress: boolean): void {
|
||||||
|
this.uploadInProgress = uploadAttemptInProgress;
|
||||||
|
}
|
||||||
|
|
||||||
private createTimeout(
|
private createTimeout(
|
||||||
after: number,
|
after: number,
|
||||||
onTimeout: (error: Error) => void
|
onTimeout: (error: Error) => void
|
||||||
@ -318,6 +324,7 @@ export class BoardDiscovery
|
|||||||
ports: newAvailablePorts,
|
ports: newAvailablePorts,
|
||||||
boards: newAttachedBoards,
|
boards: newAttachedBoards,
|
||||||
},
|
},
|
||||||
|
uploadInProgress: this.uploadInProgress,
|
||||||
};
|
};
|
||||||
|
|
||||||
this._availablePorts = newState;
|
this._availablePorts = newState;
|
||||||
|
@ -34,6 +34,7 @@ import { Instance } from './cli-protocol/cc/arduino/cli/commands/v1/common_pb';
|
|||||||
import { firstToUpperCase, notEmpty } from '../common/utils';
|
import { firstToUpperCase, notEmpty } from '../common/utils';
|
||||||
import { ServiceError } from './service-error';
|
import { ServiceError } from './service-error';
|
||||||
import { ExecuteWithProgress, ProgressResponse } from './grpc-progressible';
|
import { ExecuteWithProgress, ProgressResponse } from './grpc-progressible';
|
||||||
|
import { BoardDiscovery } from './board-discovery';
|
||||||
|
|
||||||
namespace Uploadable {
|
namespace Uploadable {
|
||||||
export type Request = UploadRequest | UploadUsingProgrammerRequest;
|
export type Request = UploadRequest | UploadUsingProgrammerRequest;
|
||||||
@ -51,6 +52,9 @@ export class CoreServiceImpl extends CoreClientAware implements CoreService {
|
|||||||
@inject(CommandService)
|
@inject(CommandService)
|
||||||
private readonly commandService: CommandService;
|
private readonly commandService: CommandService;
|
||||||
|
|
||||||
|
@inject(BoardDiscovery)
|
||||||
|
private readonly boardDiscovery: BoardDiscovery;
|
||||||
|
|
||||||
async compile(options: CoreService.Options.Compile): Promise<void> {
|
async compile(options: CoreService.Options.Compile): Promise<void> {
|
||||||
const coreClient = await this.coreClient;
|
const coreClient = await this.coreClient;
|
||||||
const { client, instance } = coreClient;
|
const { client, instance } = coreClient;
|
||||||
@ -378,6 +382,7 @@ export class CoreServiceImpl extends CoreClientAware implements CoreService {
|
|||||||
fqbn?: string | undefined;
|
fqbn?: string | undefined;
|
||||||
port?: Port | undefined;
|
port?: Port | undefined;
|
||||||
}): Promise<void> {
|
}): Promise<void> {
|
||||||
|
this.boardDiscovery.setUploadInProgress(true);
|
||||||
return this.monitorManager.notifyUploadStarted(fqbn, port);
|
return this.monitorManager.notifyUploadStarted(fqbn, port);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -388,6 +393,7 @@ export class CoreServiceImpl extends CoreClientAware implements CoreService {
|
|||||||
fqbn?: string | undefined;
|
fqbn?: string | undefined;
|
||||||
port?: Port | undefined;
|
port?: Port | undefined;
|
||||||
}): Promise<Status> {
|
}): Promise<Status> {
|
||||||
|
this.boardDiscovery.setUploadInProgress(false);
|
||||||
return this.monitorManager.notifyUploadFinished(fqbn, port);
|
return this.monitorManager.notifyUploadFinished(fqbn, port);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user