Skip to content

Commit 2914379

Browse files
authored
Merge pull request #46 from bcmi-labs/poll-boards
Implemented the board discovery with polling.
2 parents 6b25659 + b78ddbe commit 2914379

23 files changed

+858
-877
lines changed

.vscode/settings.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
{
2+
"typescript.tsdk": "node_modules/typescript/lib"
3+
}

arduino-ide-extension/src/browser/arduino-commands.ts

Lines changed: 1 addition & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -35,20 +35,11 @@ export namespace ArduinoCommands {
3535
category: 'File'
3636
}
3737

38-
export const REFRESH_BOARDS: Command = {
39-
id: "arduino-refresh-attached-boards",
40-
label: "Refresh attached boards"
41-
}
42-
43-
export const SELECT_BOARD: Command = {
44-
id: "arduino-select-board"
45-
}
46-
4738
export const OPEN_BOARDS_DIALOG: Command = {
4839
id: "arduino-open-boards-dialog"
4940
}
5041

51-
export const TOGGLE_PROMODE: Command = {
42+
export const TOGGLE_PRO_MODE: Command = {
5243
id: "arduino-toggle-pro-mode"
5344
}
5445

arduino-ide-extension/src/browser/arduino-frontend-contribution.tsx

Lines changed: 45 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -5,22 +5,21 @@ import { EditorWidget } from '@theia/editor/lib/browser/editor-widget';
55
import { MessageService } from '@theia/core/lib/common/message-service';
66
import { CommandContribution, CommandRegistry, Command } from '@theia/core/lib/common/command';
77
import { TabBarToolbarContribution, TabBarToolbarRegistry } from '@theia/core/lib/browser/shell/tab-bar-toolbar';
8-
import { BoardsService, Board } from '../common/protocol/boards-service';
8+
import { BoardsService } from '../common/protocol/boards-service';
99
import { ArduinoCommands } from './arduino-commands';
10-
import { ConnectedBoards } from './components/connected-boards';
1110
import { CoreService } from '../common/protocol/core-service';
1211
import { WorkspaceServiceExt } from './workspace-service-ext';
1312
import { ToolOutputServiceClient } from '../common/protocol/tool-output-service';
1413
import { QuickPickService } from '@theia/core/lib/common/quick-pick-service';
1514
import { BoardsListWidgetFrontendContribution } from './boards/boards-widget-frontend-contribution';
16-
import { BoardsNotificationService } from './boards-notification-service';
15+
import { BoardsServiceClientImpl } from './boards/boards-service-client-impl';
1716
import { WorkspaceRootUriAwareCommandHandler, WorkspaceCommands } from '@theia/workspace/lib/browser/workspace-commands';
1817
import { SelectionService, MenuContribution, MenuModelRegistry, MAIN_MENU_BAR } from '@theia/core';
1918
import { WorkspaceService } from '@theia/workspace/lib/browser/workspace-service';
2019
import { SketchFactory } from './sketch-factory';
2120
import { ArduinoToolbar } from './toolbar/arduino-toolbar';
2221
import { EditorManager, EditorMainMenu } from '@theia/editor/lib/browser';
23-
import { ContextMenuRenderer, OpenerService, Widget, StatusBar, ShellLayoutRestorer } from '@theia/core/lib/browser';
22+
import { ContextMenuRenderer, OpenerService, Widget, StatusBar, ShellLayoutRestorer, StatusBarAlignment } from '@theia/core/lib/browser';
2423
import { OpenFileDialogProps, FileDialogService } from '@theia/filesystem/lib/browser/file-dialog';
2524
import { FileSystem, FileStat } from '@theia/filesystem/lib/common';
2625
import { ArduinoToolbarContextMenu } from './arduino-file-menu';
@@ -32,17 +31,17 @@ import { FileDownloadCommands } from '@theia/filesystem/lib/browser/download/fil
3231
import { MonacoMenus } from '@theia/monaco/lib/browser/monaco-menu';
3332
import { TerminalMenus } from '@theia/terminal/lib/browser/terminal-frontend-contribution';
3433
import { MaybePromise } from '@theia/core/lib/common/types';
35-
import { SelectBoardDialog } from './boards/select-board-dialog';
34+
import { BoardsConfigDialog } from './boards/boards-config-dialog';
3635
import { BoardsToolBarItem } from './boards/boards-toolbar-item';
36+
import { BoardsConfig } from './boards/boards-config';
3737

3838
export namespace ArduinoMenus {
3939
export const SKETCH = [...MAIN_MENU_BAR, '3_sketch'];
4040
export const TOOLS = [...MAIN_MENU_BAR, '4_tools'];
4141
}
4242

4343
export const ARDUINO_PRO_MODE: boolean = (() => {
44-
const proModeStr = window.localStorage.getItem('arduino-pro-mode');
45-
return proModeStr === 'true';
44+
return window.localStorage.getItem('arduino-pro-mode') === 'true';
4645
})();
4746

4847
@injectable()
@@ -52,7 +51,7 @@ export class ArduinoFrontendContribution implements TabBarToolbarContribution, C
5251
protected readonly messageService: MessageService;
5352

5453
@inject(BoardsService)
55-
protected readonly boardService: BoardsService;
54+
protected readonly boardsService: BoardsService;
5655

5756
@inject(CoreService)
5857
protected readonly coreService: CoreService;
@@ -69,8 +68,8 @@ export class ArduinoFrontendContribution implements TabBarToolbarContribution, C
6968
@inject(BoardsListWidgetFrontendContribution)
7069
protected readonly boardsListWidgetFrontendContribution: BoardsListWidgetFrontendContribution;
7170

72-
@inject(BoardsNotificationService)
73-
protected readonly boardsNotificationService: BoardsNotificationService;
71+
@inject(BoardsServiceClientImpl)
72+
protected readonly boardsServiceClient: BoardsServiceClientImpl;
7473

7574
@inject(SelectionService)
7675
protected readonly selectionService: SelectionService;
@@ -99,8 +98,8 @@ export class ArduinoFrontendContribution implements TabBarToolbarContribution, C
9998
@inject(SketchesService)
10099
protected readonly sketches: SketchesService;
101100

102-
@inject(SelectBoardDialog)
103-
protected readonly selectBoardsDialog: SelectBoardDialog;
101+
@inject(BoardsConfigDialog)
102+
protected readonly boardsConfigDialog: BoardsConfigDialog;
104103

105104
@inject(MenuModelRegistry)
106105
protected readonly menuRegistry: MenuModelRegistry;
@@ -129,6 +128,15 @@ export class ArduinoFrontendContribution implements TabBarToolbarContribution, C
129128
protected async init(): Promise<void> {
130129
// This is a hack. Otherwise, the backend services won't bind.
131130
await this.workspaceServiceExt.roots();
131+
132+
const updateStatusBar = (config: BoardsConfig.Config) => {
133+
this.statusBar.setElement('arduino-selected-board', {
134+
alignment: StatusBarAlignment.RIGHT,
135+
text: BoardsConfig.Config.toString(config)
136+
});
137+
}
138+
this.boardsServiceClient.onBoardsConfigChanged(updateStatusBar);
139+
updateStatusBar(this.boardsServiceClient.boardsConfig);
132140
}
133141

134142
registerToolbarItems(registry: TabBarToolbarRegistry): void {
@@ -157,15 +165,13 @@ export class ArduinoFrontendContribution implements TabBarToolbarContribution, C
157165
text: '$(arrow-down)'
158166
});
159167
registry.registerItem({
160-
id: ConnectedBoards.TOOLBAR_ID,
168+
id: BoardsToolBarItem.TOOLBAR_ID,
161169
render: () => <BoardsToolBarItem
162170
key='boardsToolbarItem'
163171
ref={ref => this.boardsToolbarItem = ref}
164172
commands={this.commands}
165-
statusBar={this.statusBar}
166-
contextMenuRenderer={this.contextMenuRenderer}
167-
boardsNotificationService={this.boardsNotificationService}
168-
boardService={this.boardService} />,
173+
boardsServiceClient={this.boardsServiceClient}
174+
boardService={this.boardsService} />,
169175
isVisible: widget => this.isArduinoToolbar(widget)
170176
})
171177
}
@@ -186,7 +192,14 @@ export class ArduinoFrontendContribution implements TabBarToolbarContribution, C
186192
}
187193

188194
try {
189-
await this.coreService.compile({ uri: uri.toString() });
195+
const { boardsConfig } = this.boardsServiceClient;
196+
if (!boardsConfig || !boardsConfig.selectedBoard) {
197+
throw new Error('No boards selected. Please select a board.');
198+
}
199+
if (!boardsConfig.selectedBoard.fqbn) {
200+
throw new Error(`No core is installed for ${boardsConfig.selectedBoard.name}. Please install the board.`);
201+
}
202+
await this.coreService.compile({ uri: uri.toString(), board: boardsConfig.selectedBoard });
190203
} catch (e) {
191204
await this.messageService.error(e.toString());
192205
}
@@ -207,7 +220,15 @@ export class ArduinoFrontendContribution implements TabBarToolbarContribution, C
207220
}
208221

209222
try {
210-
await this.coreService.upload({ uri: uri.toString() });
223+
const { boardsConfig } = this.boardsServiceClient;
224+
if (!boardsConfig || !boardsConfig.selectedBoard) {
225+
throw new Error('No boards selected. Please select a board.');
226+
}
227+
const { selectedPort } = boardsConfig;
228+
if (!selectedPort) {
229+
throw new Error('No ports selected. Please select a port.');
230+
}
231+
await this.coreService.upload({ uri: uri.toString(), board: boardsConfig.selectedBoard, port: selectedPort });
211232
} catch (e) {
212233
await this.messageService.error(e.toString());
213234
}
@@ -261,26 +282,16 @@ export class ArduinoFrontendContribution implements TabBarToolbarContribution, C
261282
}
262283
}
263284
}));
264-
registry.registerCommand(ArduinoCommands.REFRESH_BOARDS, {
265-
isEnabled: () => true,
266-
execute: () => this.boardsNotificationService.notifyBoardsInstalled()
267-
});
268-
registry.registerCommand(ArduinoCommands.SELECT_BOARD, {
269-
isEnabled: () => true,
270-
execute: async (board: Board) => {
271-
this.selectBoard(board);
272-
}
273-
})
274285
registry.registerCommand(ArduinoCommands.OPEN_BOARDS_DIALOG, {
275286
isEnabled: () => true,
276287
execute: async () => {
277-
const boardAndPort = await this.selectBoardsDialog.open();
278-
if (boardAndPort && boardAndPort.board) {
279-
this.selectBoard(boardAndPort.board);
288+
const boardsConfig = await this.boardsConfigDialog.open();
289+
if (boardsConfig) {
290+
this.boardsServiceClient.boardsConfig = boardsConfig;
280291
}
281292
}
282293
})
283-
registry.registerCommand(ArduinoCommands.TOGGLE_PROMODE, {
294+
registry.registerCommand(ArduinoCommands.TOGGLE_PRO_MODE, {
284295
execute: () => {
285296
const oldModeState = ARDUINO_PRO_MODE;
286297
window.localStorage.setItem('arduino-pro-mode', oldModeState ? 'false' : 'true');
@@ -290,13 +301,6 @@ export class ArduinoFrontendContribution implements TabBarToolbarContribution, C
290301
})
291302
}
292303

293-
protected async selectBoard(board: Board) {
294-
await this.boardService.selectBoard(board);
295-
if (this.boardsToolbarItem) {
296-
this.boardsToolbarItem.setSelectedBoard(board);
297-
}
298-
}
299-
300304
registerMenus(registry: MenuModelRegistry) {
301305
if (!ARDUINO_PRO_MODE) {
302306
registry.unregisterMenuAction(FileSystemCommands.UPLOAD);
@@ -335,7 +339,7 @@ export class ArduinoFrontendContribution implements TabBarToolbarContribution, C
335339
registry.registerSubmenu(ArduinoMenus.TOOLS, 'Tools');
336340

337341
registry.registerMenuAction(CommonMenus.HELP, {
338-
commandId: ArduinoCommands.TOGGLE_PROMODE.id,
342+
commandId: ArduinoCommands.TOGGLE_PRO_MODE.id,
339343
label: 'Advanced Mode'
340344
})
341345
}

arduino-ide-extension/src/browser/arduino-frontend-module.ts

Lines changed: 19 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ import { LibraryListWidget } from './library/library-list-widget';
1111
import { ArduinoFrontendContribution, ARDUINO_PRO_MODE } from './arduino-frontend-contribution';
1212
import { ArduinoLanguageGrammarContribution } from './language/arduino-language-grammar-contribution';
1313
import { LibraryService, LibraryServicePath } from '../common/protocol/library-service';
14-
import { BoardsService, BoardsServicePath } from '../common/protocol/boards-service';
14+
import { BoardsService, BoardsServicePath, BoardsServiceClient } from '../common/protocol/boards-service';
1515
import { SketchesService, SketchesServicePath } from '../common/protocol/sketches-service';
1616
import { LibraryListWidgetFrontendContribution } from './library/list-widget-frontend-contribution';
1717
import { CoreService, CoreServicePath } from '../common/protocol/core-service';
@@ -22,7 +22,7 @@ import { WorkspaceServiceExtImpl } from './workspace-service-ext-impl';
2222
import { ToolOutputServiceClient } from '../common/protocol/tool-output-service';
2323
import { ToolOutputService } from '../common/protocol/tool-output-service';
2424
import { ToolOutputServiceClientImpl } from './tool-output/client-service-impl';
25-
import { BoardsNotificationService } from './boards-notification-service';
25+
import { BoardsServiceClientImpl } from './boards/boards-service-client-impl';
2626
import { WorkspaceService } from '@theia/workspace/lib/browser/workspace-service';
2727
import { AWorkspaceService } from './arduino-workspace-service';
2828
import { ThemeService } from '@theia/core/lib/browser/theming';
@@ -46,8 +46,8 @@ import { SilentMonacoStatusBarContribution } from './customization/silent-monaco
4646
import { ApplicationShell } from '@theia/core/lib/browser';
4747
import { CustomApplicationShell } from './customization/custom-application-shell';
4848
import { CustomFrontendApplication } from './customization/custom-frontend-application';
49-
import { SelectBoardDialog, SelectBoardDialogProps } from './boards/select-board-dialog';
50-
import { SelectBoardDialogWidget } from './boards/select-board-dialog-widget';
49+
import { BoardsConfigDialog, BoardsConfigDialogProps } from './boards/boards-config-dialog';
50+
import { BoardsConfigDialogWidget } from './boards/boards-config-dialog-widget';
5151
const ElementQueries = require('css-element-queries/src/ElementQueries');
5252

5353
if (!ARDUINO_PRO_MODE) {
@@ -87,12 +87,19 @@ export default new ContainerModule((bind: interfaces.Bind, unbind: interfaces.Un
8787
// Sketch list service
8888
bind(SketchesService).toDynamicValue(context => WebSocketConnectionProvider.createProxy(context.container, SketchesServicePath)).inSingletonScope();
8989

90-
// Boards Notification service for updating boards list
91-
// TODO (post-PoC): move this to boards service/backend
92-
bind(BoardsNotificationService).toSelf().inSingletonScope();
93-
9490
// Boards service
95-
bind(BoardsService).toDynamicValue(context => WebSocketConnectionProvider.createProxy(context.container, BoardsServicePath)).inSingletonScope();
91+
bind(BoardsService).toDynamicValue(context => {
92+
const connection = context.container.get(WebSocketConnectionProvider);
93+
const client = context.container.get(BoardsServiceClientImpl);
94+
return connection.createProxy(BoardsServicePath, client);
95+
}).inSingletonScope();
96+
// Boards service client to receive and delegate notifications from the backend.
97+
bind(BoardsServiceClientImpl).toSelf().inSingletonScope();
98+
bind(BoardsServiceClient).toDynamicValue(context => {
99+
const client = context.container.get(BoardsServiceClientImpl);
100+
WebSocketConnectionProvider.createProxy(context.container, BoardsServicePath, client);
101+
return client;
102+
}).inSingletonScope();
96103

97104
// Boards list widget
98105
bind(BoardsListWidget).toSelf();
@@ -104,9 +111,9 @@ export default new ContainerModule((bind: interfaces.Bind, unbind: interfaces.Un
104111
bind(FrontendApplicationContribution).toService(BoardsListWidgetFrontendContribution);
105112

106113
// Board select dialog
107-
bind(SelectBoardDialogWidget).toSelf().inSingletonScope();
108-
bind(SelectBoardDialog).toSelf().inSingletonScope();
109-
bind(SelectBoardDialogProps).toConstantValue({
114+
bind(BoardsConfigDialogWidget).toSelf().inSingletonScope();
115+
bind(BoardsConfigDialog).toSelf().inSingletonScope();
116+
bind(BoardsConfigDialogProps).toConstantValue({
110117
title: 'Select Board'
111118
})
112119

arduino-ide-extension/src/browser/boards-notification-service.ts

Lines changed: 0 additions & 19 deletions
This file was deleted.
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
import * as React from 'react';
2+
import { injectable, inject } from 'inversify';
3+
import { Emitter } from '@theia/core/lib/common/event';
4+
import { ReactWidget, Message } from '@theia/core/lib/browser';
5+
import { BoardsService } from '../../common/protocol/boards-service';
6+
import { BoardsConfig } from './boards-config';
7+
import { BoardsServiceClientImpl } from './boards-service-client-impl';
8+
9+
@injectable()
10+
export class BoardsConfigDialogWidget extends ReactWidget {
11+
12+
@inject(BoardsService)
13+
protected readonly boardsService: BoardsService;
14+
15+
@inject(BoardsServiceClientImpl)
16+
protected readonly boardsServiceClient: BoardsServiceClientImpl;
17+
18+
protected readonly onBoardConfigChangedEmitter = new Emitter<BoardsConfig.Config>();
19+
readonly onBoardConfigChanged = this.onBoardConfigChangedEmitter.event;
20+
21+
protected focusNode: HTMLElement | undefined;
22+
23+
constructor() {
24+
super();
25+
this.id = 'select-board-dialog';
26+
}
27+
28+
protected fireConfigChanged = (config: BoardsConfig.Config) => {
29+
this.onBoardConfigChangedEmitter.fire(config);
30+
}
31+
32+
protected setFocusNode = (element: HTMLElement | undefined) => {
33+
this.focusNode = element;
34+
}
35+
36+
protected render(): React.ReactNode {
37+
return <div className='selectBoardContainer'>
38+
<BoardsConfig
39+
boardsService={this.boardsService}
40+
boardsServiceClient={this.boardsServiceClient}
41+
onConfigChange={this.fireConfigChanged}
42+
onFocusNodeSet={this.setFocusNode} />
43+
</div>;
44+
}
45+
46+
protected onActivateRequest(msg: Message): void {
47+
super.onActivateRequest(msg);
48+
if (this.focusNode instanceof HTMLInputElement) {
49+
this.focusNode.select();
50+
}
51+
(this.focusNode || this.node).focus();
52+
}
53+
54+
55+
}

0 commit comments

Comments
 (0)