ATL-61: Implemented burn bootloader.

Signed-off-by: Akos Kitta <kittaakos@typefox.io>
This commit is contained in:
Akos Kitta 2020-08-13 10:35:37 +02:00 committed by Akos Kitta
parent 525e688d70
commit b5d7c3b45d
6 changed files with 145 additions and 6 deletions

View File

@ -117,6 +117,7 @@ import { EditorWidgetFactory as TheiaEditorWidgetFactory } from '@theia/editor/l
import { EditorWidgetFactory } from './theia/editor/editor-widget-factory';
import { OutputWidget as TheiaOutputWidget } from '@theia/output/lib/browser/output-widget';
import { OutputWidget } from './theia/output/output-widget';
import { BurnBootloader } from './contributions/burn-bootloader';
const ElementQueries = require('css-element-queries/src/ElementQueries');
@ -358,4 +359,5 @@ export default new ContainerModule((bind, unbind, isBound, rebind) => {
Contribution.configure(bind, QuitApp);
Contribution.configure(bind, SketchControl);
Contribution.configure(bind, Settings);
Contribution.configure(bind, BurnBootloader);
});

View File

@ -0,0 +1,94 @@
import { inject, injectable } from 'inversify';
import { OutputChannelManager } from '@theia/output/lib/common/output-channel';
import { CoreService } from '../../common/protocol';
import { ArduinoMenus } from '../menu/arduino-menus';
import { BoardsDataStore } from '../boards/boards-data-store';
import { MonitorConnection } from '../monitor/monitor-connection';
import { BoardsServiceClientImpl } from '../boards/boards-service-client-impl';
import { SketchContribution, Command, CommandRegistry, MenuModelRegistry } from './contribution';
@injectable()
export class BurnBootloader extends SketchContribution {
@inject(CoreService)
protected readonly coreService: CoreService;
@inject(MonitorConnection)
protected readonly monitorConnection: MonitorConnection;
@inject(BoardsDataStore)
protected readonly boardsDataStore: BoardsDataStore;
@inject(BoardsServiceClientImpl)
protected readonly boardsServiceClientImpl: BoardsServiceClientImpl;
@inject(OutputChannelManager)
protected readonly outputChannelManager: OutputChannelManager;
registerCommands(registry: CommandRegistry): void {
registry.registerCommand(BurnBootloader.Commands.BURN_BOOTLOADER, {
execute: () => this.burnBootloader()
});
}
registerMenus(registry: MenuModelRegistry): void {
registry.registerMenuAction(ArduinoMenus.TOOLS__BOARD_SETTINGS_GROUP, {
commandId: BurnBootloader.Commands.BURN_BOOTLOADER.id,
label: 'Burn Bootloader',
order: 'z99'
});
}
async burnBootloader(): Promise<void> {
const monitorConfig = this.monitorConnection.monitorConfig;
if (monitorConfig) {
await this.monitorConnection.disconnect();
}
try {
const { boardsConfig } = this.boardsServiceClientImpl;
if (!boardsConfig || !boardsConfig.selectedBoard) {
throw new Error('No boards selected. Please select a board.');
}
if (!boardsConfig.selectedBoard.fqbn) {
throw new Error(`No core is installed for the '${boardsConfig.selectedBoard.name}' board. Please install the core.`);
}
const { selectedPort } = boardsConfig;
if (!selectedPort) {
throw new Error('No ports selected. Please select a port.');
}
const port = selectedPort.address;
const [fqbn, { selectedProgrammer: programmer }] = await Promise.all([
this.boardsDataStore.appendConfigToFqbn(boardsConfig.selectedBoard.fqbn),
this.boardsDataStore.getData(boardsConfig.selectedBoard.fqbn)
]);
if (!programmer) {
throw new Error('Programmer is not selected. Please select a programmer from the `Tools` > `Programmer` menu.');
}
this.outputChannelManager.getChannel('Arduino: bootloader').clear();
await this.coreService.burnBootloader({
fqbn,
programmer,
port
});
this.messageService.info('Done burning bootloader.', { timeout: 1000 });
} catch (e) {
this.messageService.error(e.toString());
} finally {
if (monitorConfig) {
await this.monitorConnection.connect(monitorConfig);
}
}
}
}
export namespace BurnBootloader {
export namespace Commands {
export const BURN_BOOTLOADER: Command = {
id: 'arduino-burn-bootloader'
};
}
}

View File

@ -103,7 +103,7 @@ export class UploadSketch extends SketchContribution {
if (usingProgrammer) {
const programmer = selectedProgrammer;
if (!programmer) {
throw new Error('Programmer is not selected. Please select a programmer.');
throw new Error('Programmer is not selected. Please select a programmer from the `Tools` > `Programmer` menu.');
}
let port: undefined | string = undefined;
// If the port is set by the user, we pass it to the CLI as it might be required.

View File

@ -30,7 +30,7 @@ export namespace ArduinoMenus {
export const TOOLS = [...MAIN_MENU_BAR, '4_tools'];
// `Auto Format`, `Library Manager...`, `Boards Manager...`
export const TOOLS__MAIN_GROUP = [...TOOLS, '0_main'];
// Core settings, such as `Processor` and `Programmers` for the board.
// Core settings, such as `Processor` and `Programmers` for the board and `Burn Bootloader`
export const TOOLS__BOARD_SETTINGS_GROUP = [...TOOLS, '1_board_settings'];
// Context menu

View File

@ -11,6 +11,7 @@ export const CoreService = Symbol('CoreService');
export interface CoreService extends JsonRpcServer<CoreServiceClient> {
compile(options: CoreService.Compile.Options): Promise<void>;
upload(options: CoreService.Upload.Options): Promise<void>;
burnBootloader(options: CoreService.Bootloader.Options): Promise<void>;
}
export namespace CoreService {
@ -29,4 +30,12 @@ export namespace CoreService {
Compile.Options & Readonly<{ programmer: Programmer, port?: string }>;
}
export namespace Bootloader {
export interface Options {
readonly fqbn: string;
readonly programmer: Programmer;
readonly port: string;
}
}
}

View File

@ -6,7 +6,7 @@ import { BoardsService } from '../common/protocol/boards-service';
import { CoreClientProvider } from './core-client-provider';
import * as path from 'path';
import { ToolOutputServiceServer } from '../common/protocol/tool-output-service';
import { UploadReq, UploadResp } from './cli-protocol/commands/upload_pb';
import { UploadReq, UploadResp, BurnBootloaderReq, BurnBootloaderResp } from './cli-protocol/commands/upload_pb';
@injectable()
export class CoreServiceImpl implements CoreService {
@ -113,9 +113,9 @@ export class CoreServiceImpl implements CoreService {
try {
await new Promise<void>((resolve, reject) => {
result.on('data', (cr: UploadResp) => {
this.toolOutputService.append({ tool: 'upload', chunk: Buffer.from(cr.getOutStream_asU8()).toString() });
this.toolOutputService.append({ tool: 'upload', chunk: Buffer.from(cr.getErrStream_asU8()).toString() });
result.on('data', (resp: UploadResp) => {
this.toolOutputService.append({ tool: 'upload', chunk: Buffer.from(resp.getOutStream_asU8()).toString() });
this.toolOutputService.append({ tool: 'upload', chunk: Buffer.from(resp.getErrStream_asU8()).toString() });
});
result.on('error', error => reject(error));
result.on('end', () => resolve());
@ -127,6 +127,40 @@ export class CoreServiceImpl implements CoreService {
}
}
async burnBootloader(options: CoreService.Bootloader.Options): Promise<void> {
const coreClient = await this.coreClientProvider.client();
if (!coreClient) {
return;
}
const { fqbn, port, programmer } = options;
if (!fqbn) {
throw new Error('The selected board has no FQBN.');
}
if (!port) {
throw new Error('Port must be specified.');
}
const { client, instance } = coreClient;
const req = new BurnBootloaderReq();
req.setFqbn(fqbn);
req.setPort(port);
req.setProgrammer(programmer.id);
req.setInstance(instance);
const result = client.burnBootloader(req);
try {
await new Promise<void>((resolve, reject) => {
result.on('data', (resp: BurnBootloaderResp) => {
this.toolOutputService.append({ tool: 'bootloader', chunk: Buffer.from(resp.getOutStream_asU8()).toString() });
this.toolOutputService.append({ tool: 'bootloader', chunk: Buffer.from(resp.getErrStream_asU8()).toString() });
});
result.on('error', error => reject(error));
result.on('end', () => resolve());
});
} catch (e) {
this.toolOutputService.append({ tool: 'bootloader', chunk: `Error while burning the bootloader: ${e}\n`, severity: 'error' });
throw e;
}
}
setClient(client: CoreServiceClient | undefined): void {
this.client = client;
}