From 8971dc4c5f45d4f4293846a4dda77970722d144d Mon Sep 17 00:00:00 2001 From: Akos Kitta Date: Tue, 8 Oct 2019 10:55:50 +0200 Subject: [PATCH] Implemented naive reconnecting. Signed-off-by: Akos Kitta --- .../browser/arduino-frontend-contribution.tsx | 4 +- .../boards/boards-service-client-impl.ts | 72 ++++++++++++++++--- arduino-ide-extension/src/common/types.ts | 3 + 3 files changed, 69 insertions(+), 10 deletions(-) create mode 100644 arduino-ide-extension/src/common/types.ts diff --git a/arduino-ide-extension/src/browser/arduino-frontend-contribution.tsx b/arduino-ide-extension/src/browser/arduino-frontend-contribution.tsx index ef84eb41..7a790c4b 100644 --- a/arduino-ide-extension/src/browser/arduino-frontend-contribution.tsx +++ b/arduino-ide-extension/src/browser/arduino-frontend-contribution.tsx @@ -137,7 +137,7 @@ export class ArduinoFrontendContribution implements TabBarToolbarContribution, C @inject(QuickOpenService) protected readonly quickOpenService: QuickOpenService; - @inject(ArduinoWorkspaceService) + @inject(ArduinoWorkspaceService) protected readonly workspaceService: ArduinoWorkspaceService; @inject(ConfigService) @@ -164,6 +164,8 @@ export class ArduinoFrontendContribution implements TabBarToolbarContribution, C updateStatusBar(this.boardsServiceClient.boardsConfig); this.registerSketchesInMenu(this.menuRegistry); + + this.boardsService.getAttachedBoards().then(({ boards }) => this.boardsServiceClient.tryReconnect(boards)); } registerToolbarItems(registry: TabBarToolbarRegistry): void { diff --git a/arduino-ide-extension/src/browser/boards/boards-service-client-impl.ts b/arduino-ide-extension/src/browser/boards/boards-service-client-impl.ts index 3ceb5d8f..b11476b3 100644 --- a/arduino-ide-extension/src/browser/boards/boards-service-client-impl.ts +++ b/arduino-ide-extension/src/browser/boards/boards-service-client-impl.ts @@ -1,8 +1,11 @@ import { injectable, inject, postConstruct } from 'inversify'; -import { Emitter, ILogger } from '@theia/core'; -import { BoardsServiceClient, AttachedBoardsChangeEvent, BoardInstalledEvent, AttachedSerialBoard } from '../../common/protocol/boards-service'; +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 { BoardsConfig } from './boards-config'; -import { LocalStorageService } from '@theia/core/lib/browser'; +import { MaybePromise } from '@theia/core'; @injectable() export class BoardsServiceClientImpl implements BoardsServiceClient { @@ -13,10 +16,18 @@ export class BoardsServiceClientImpl implements BoardsServiceClient { @inject(LocalStorageService) protected storageService: LocalStorageService; - protected readonly onAttachedBoardsChangedEmitter = new Emitter(); protected readonly onBoardInstalledEmitter = new Emitter(); + protected readonly onAttachedBoardsChangedEmitter = new Emitter(); protected readonly onSelectedBoardsConfigChangedEmitter = new Emitter(); + /** + * Used for the auto-reconnecting. Sometimes, the attached board gets disconnected after uploading something to it. + * It happens with certain boards on Windows. For example, the `MKR1000` boards is selected on post `COM5` on Windows, + * perform an upload, the board automatically disconnects and reconnects, but on another port, `COM10`. + * We have to listen on such changes and auto-reconnect the same board on another port. + * See: https://arduino.slack.com/archives/CJJHJCJSJ/p1568645417013000?thread_ts=1568640504.009400&cid=CJJHJCJSJ + */ + protected latestValidBoardsConfig: RecursiveRequired | undefined = undefined; protected _boardsConfig: BoardsConfig.Config = {}; readonly onBoardsChanged = this.onAttachedBoardsChangedEmitter.event; @@ -30,7 +41,8 @@ export class BoardsServiceClientImpl implements BoardsServiceClient { notifyAttachedBoardsChanged(event: AttachedBoardsChangeEvent): void { this.logger.info('Attached boards changed: ', JSON.stringify(event)); - const detachedBoards = AttachedBoardsChangeEvent.diff(event).detached.filter(AttachedSerialBoard.is).map(({ port }) => port); + 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. @@ -40,6 +52,37 @@ export class BoardsServiceClientImpl implements BoardsServiceClient { selectedPort: undefined }; } + // Try to reconnect. + this.tryReconnect(attached); + } + + async tryReconnect(attachedBoards: MaybePromise>): Promise { + const boards = await attachedBoards; + if (this.latestValidBoardsConfig && !this.canUploadTo(this.boardsConfig)) { + for (const board of boards.filter(AttachedSerialBoard.is)) { + if (this.latestValidBoardsConfig.selectedBoard.fqbn === board.fqbn + && this.latestValidBoardsConfig.selectedBoard.name === board.name + && this.latestValidBoardsConfig.selectedPort === board.port) { + + this.boardsConfig = this.latestValidBoardsConfig; + 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`. + for (const board of boards.filter(AttachedSerialBoard.is)) { + if (this.latestValidBoardsConfig.selectedBoard.fqbn === board.fqbn + && this.latestValidBoardsConfig.selectedBoard.name === board.name) { + + this.boardsConfig = { + ...this.latestValidBoardsConfig, + selectedPort: board.port + }; + return true; + } + } + } + return false; } notifyBoardInstalled(event: BoardInstalledEvent): void { @@ -50,6 +93,9 @@ export class BoardsServiceClientImpl implements BoardsServiceClient { set boardsConfig(config: BoardsConfig.Config) { this.logger.info('Board config changed: ', JSON.stringify(config)); this._boardsConfig = config; + if (this.canUploadTo(this._boardsConfig)) { + this.latestValidBoardsConfig = this._boardsConfig; + } this.saveState().then(() => this.onSelectedBoardsConfigChangedEmitter.fire(this._boardsConfig)); } @@ -58,14 +104,22 @@ export class BoardsServiceClientImpl implements BoardsServiceClient { } protected saveState(): Promise { - return this.storageService.setData('boards-config', this.boardsConfig); + return this.storageService.setData('latest-valid-boards-config', this.latestValidBoardsConfig); } protected async loadState(): Promise { - const boardsConfig = await this.storageService.getData('boards-config'); - if (boardsConfig) { - this.boardsConfig = boardsConfig; + const storedValidBoardsConfig = await this.storageService.getData>('latest-valid-boards-config'); + if (storedValidBoardsConfig) { + this.latestValidBoardsConfig = storedValidBoardsConfig; } } + protected canVerify(config: BoardsConfig.Config | undefined): config is BoardsConfig.Config & { selectedBoard: Board } { + return !!config && !!config.selectedBoard; + } + + protected canUploadTo(config: BoardsConfig.Config | undefined): config is RecursiveRequired { + return this.canVerify(config) && !!config.selectedPort && !!config.selectedBoard.fqbn; + } + } diff --git a/arduino-ide-extension/src/common/types.ts b/arduino-ide-extension/src/common/types.ts new file mode 100644 index 00000000..100be9f1 --- /dev/null +++ b/arduino-ide-extension/src/common/types.ts @@ -0,0 +1,3 @@ +export type RecursiveRequired = { + [P in keyof T]-?: RecursiveRequired; +};