Skip to content

Commit 2831acc

Browse files
Akos Kittakittaakos
Akos Kitta
authored andcommitted
ATL-530: No checks before upload/verify/burn
Made the port/fqbn/programmer optional for upload, verify, and burn bootloader. From now on, the IDE does not warn the user before performing the desired CLI command. Closes arduino/arduino-pro-ide#364 Signed-off-by: Akos Kitta <[email protected]>
1 parent acbb7d3 commit 2831acc

File tree

8 files changed

+86
-115
lines changed

8 files changed

+86
-115
lines changed

Diff for: arduino-ide-extension/src/browser/boards/boards-data-store.ts

+13-8
Original file line numberDiff line numberDiff line change
@@ -58,28 +58,33 @@ export class BoardsDataStore implements FrontendApplicationContribution {
5858
}
5959

6060
async appendConfigToFqbn(
61-
fqbn: string,
62-
boardsPackageVersion: MaybePromise<Installable.Version | undefined> = this.getBoardsPackageVersion(fqbn)): Promise<string> {
61+
fqbn: string | undefined,
62+
boardsPackageVersion: MaybePromise<Installable.Version | undefined> = this.getBoardsPackageVersion(fqbn)): Promise<string | undefined> {
63+
64+
if (!fqbn) {
65+
return undefined;
66+
}
6367

6468
const { configOptions } = await this.getData(fqbn, boardsPackageVersion);
6569
return ConfigOption.decorate(fqbn, configOptions);
6670
}
6771

6872
async getData(
69-
fqbn: string,
73+
fqbn: string | undefined,
7074
boardsPackageVersion: MaybePromise<Installable.Version | undefined> = this.getBoardsPackageVersion(fqbn)): Promise<BoardsDataStore.Data> {
7175

76+
if (!fqbn) {
77+
return BoardsDataStore.Data.EMPTY;
78+
}
79+
7280
const version = await boardsPackageVersion;
7381
if (!version) {
7482
return BoardsDataStore.Data.EMPTY;
7583
}
7684
const key = this.getStorageKey(fqbn, version);
7785
let data = await this.storageService.getData<BoardsDataStore.Data | undefined>(key, undefined);
7886
if (data) {
79-
// If `configOptions` is empty we rather reload the data. See arduino/arduino-cli#954 and arduino/arduino-cli#955.
80-
if (data.configOptions.length && data.programmers !== undefined) { // to be backward compatible. We did not save the `programmers` into the `localStorage`.
81-
return data;
82-
}
87+
return data;
8388
}
8489

8590
const boardDetails = await this.getBoardDetailsSafe(fqbn);
@@ -173,7 +178,7 @@ export class BoardsDataStore implements FrontendApplicationContribution {
173178
this.onChangedEmitter.fire();
174179
}
175180

176-
protected async getBoardsPackageVersion(fqbn: string): Promise<Installable.Version | undefined> {
181+
protected async getBoardsPackageVersion(fqbn: string | undefined): Promise<Installable.Version | undefined> {
177182
if (!fqbn) {
178183
return undefined;
179184
}

Diff for: arduino-ide-extension/src/browser/contributions/burn-bootloader.ts

+3-19
Original file line numberDiff line numberDiff line change
@@ -46,27 +46,11 @@ export class BurnBootloader extends SketchContribution {
4646
}
4747
try {
4848
const { boardsConfig } = this.boardsServiceClientImpl;
49-
if (!boardsConfig || !boardsConfig.selectedBoard) {
50-
throw new Error('No boards selected. Please select a board.');
51-
}
52-
if (!boardsConfig.selectedBoard.fqbn) {
53-
throw new Error(`No core is installed for the '${boardsConfig.selectedBoard.name}' board. Please install the core.`);
54-
}
55-
const { selectedPort } = boardsConfig;
56-
if (!selectedPort) {
57-
throw new Error('No ports selected. Please select a port.');
58-
}
59-
60-
const port = selectedPort.address;
49+
const port = boardsConfig.selectedPort?.address;
6150
const [fqbn, { selectedProgrammer: programmer }] = await Promise.all([
62-
this.boardsDataStore.appendConfigToFqbn(boardsConfig.selectedBoard.fqbn),
63-
this.boardsDataStore.getData(boardsConfig.selectedBoard.fqbn)
51+
this.boardsDataStore.appendConfigToFqbn(boardsConfig.selectedBoard?.fqbn),
52+
this.boardsDataStore.getData(boardsConfig.selectedBoard?.fqbn)
6453
]);
65-
66-
if (!programmer) {
67-
throw new Error('Programmer is not selected. Please select a programmer from the `Tools` > `Programmer` menu.');
68-
}
69-
7054
this.outputChannelManager.getChannel('Arduino: bootloader').clear();
7155
await this.coreService.burnBootloader({
7256
fqbn,

Diff for: arduino-ide-extension/src/browser/contributions/upload-sketch.ts

+8-23
Original file line numberDiff line numberDiff line change
@@ -83,34 +83,19 @@ export class UploadSketch extends SketchContribution {
8383
}
8484
try {
8585
const { boardsConfig } = this.boardsServiceClientImpl;
86-
if (!boardsConfig || !boardsConfig.selectedBoard) {
87-
throw new Error('No boards selected. Please select a board.');
88-
}
89-
if (!boardsConfig.selectedBoard.fqbn) {
90-
throw new Error(`No core is installed for the '${boardsConfig.selectedBoard.name}' board. Please install the core.`);
91-
}
92-
9386
const [fqbn, { selectedProgrammer }] = await Promise.all([
94-
this.boardsDataStore.appendConfigToFqbn(boardsConfig.selectedBoard.fqbn),
95-
this.boardsDataStore.getData(boardsConfig.selectedBoard.fqbn)
87+
this.boardsDataStore.appendConfigToFqbn(boardsConfig.selectedBoard?.fqbn),
88+
this.boardsDataStore.getData(boardsConfig.selectedBoard?.fqbn)
9689
]);
9790

9891
let options: CoreService.Upload.Options | undefined = undefined;
9992
const sketchUri = uri;
10093
const optimizeForDebug = this.editorMode.compileForDebug;
10194
const { selectedPort } = boardsConfig;
95+
const port = selectedPort?.address;
10296

10397
if (usingProgrammer) {
10498
const programmer = selectedProgrammer;
105-
if (!programmer) {
106-
throw new Error('Programmer is not selected. Please select a programmer from the `Tools` > `Programmer` menu.');
107-
}
108-
let port: undefined | string = undefined;
109-
// If the port is set by the user, we pass it to the CLI as it might be required.
110-
// If it is not set but the CLI requires it, we let the CLI to complain.
111-
if (selectedPort) {
112-
port = selectedPort.address;
113-
}
11499
options = {
115100
sketchUri,
116101
fqbn,
@@ -119,10 +104,6 @@ export class UploadSketch extends SketchContribution {
119104
port
120105
};
121106
} else {
122-
if (!selectedPort) {
123-
throw new Error('No ports selected. Please select a port.');
124-
}
125-
const port = selectedPort.address;
126107
options = {
127108
sketchUri,
128109
fqbn,
@@ -131,7 +112,11 @@ export class UploadSketch extends SketchContribution {
131112
};
132113
}
133114
this.outputChannelManager.getChannel('Arduino: upload').clear();
134-
await this.coreService.upload(options);
115+
if (usingProgrammer) {
116+
await this.coreService.uploadUsingProgrammer(options);
117+
} else {
118+
await this.coreService.upload(options);
119+
}
135120
this.messageService.info('Done uploading.', { timeout: 1000 });
136121
} catch (e) {
137122
this.messageService.error(e.toString());

Diff for: arduino-ide-extension/src/browser/contributions/verify-sketch.ts

+1-7
Original file line numberDiff line numberDiff line change
@@ -63,13 +63,7 @@ export class VerifySketch extends SketchContribution {
6363
}
6464
try {
6565
const { boardsConfig } = this.boardsServiceClientImpl;
66-
if (!boardsConfig || !boardsConfig.selectedBoard) {
67-
throw new Error('No boards selected. Please select a board.');
68-
}
69-
if (!boardsConfig.selectedBoard.fqbn) {
70-
throw new Error(`No core is installed for the '${boardsConfig.selectedBoard.name}' board. Please install the core.`);
71-
}
72-
const fqbn = await this.boardsDataStore.appendConfigToFqbn(boardsConfig.selectedBoard.fqbn);
66+
const fqbn = await this.boardsDataStore.appendConfigToFqbn(boardsConfig.selectedBoard?.fqbn);
7367
this.outputChannelManager.getChannel('Arduino: compile').clear();
7468
await this.coreService.compile({
7569
sketchUri: uri,
+2-14
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { inject, injectable } from 'inversify';
22
import { OutputContribution } from '@theia/output/lib/browser/output-contribution';
3-
import { OutputChannelManager, OutputChannelSeverity } from '@theia/output/lib/common/output-channel';
3+
import { OutputChannelManager } from '@theia/output/lib/common/output-channel';
44
import { OutputService, OutputMessage } from '../common/protocol/output-service';
55

66
@injectable()
@@ -22,19 +22,7 @@ export class OutputServiceImpl implements OutputService {
2222
// This will open, reveal but do not activate the Output view.
2323
: Promise.resolve(channel.show({ preserveFocus: true }));
2424

25-
show.then(() => channel.append(chunk, this.toOutputSeverity(message)));
26-
}
27-
28-
protected toOutputSeverity(message: OutputMessage): OutputChannelSeverity {
29-
if (message.severity) {
30-
switch (message.severity) {
31-
case 'error': return OutputChannelSeverity.Error
32-
case 'warning': return OutputChannelSeverity.Warning
33-
case 'info': return OutputChannelSeverity.Info
34-
default: return OutputChannelSeverity.Info
35-
}
36-
}
37-
return OutputChannelSeverity.Info
25+
show.then(() => channel.append(chunk));
3826
}
3927

4028
}

Diff for: arduino-ide-extension/src/common/protocol/core-service.ts

+9-7
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ export const CoreService = Symbol('CoreService');
55
export interface CoreService {
66
compile(options: CoreService.Compile.Options): Promise<void>;
77
upload(options: CoreService.Upload.Options): Promise<void>;
8+
uploadUsingProgrammer(options: CoreService.Upload.Options): Promise<void>;
89
burnBootloader(options: CoreService.Bootloader.Options): Promise<void>;
910
}
1011

@@ -13,22 +14,23 @@ export namespace CoreService {
1314
export namespace Compile {
1415
export interface Options {
1516
readonly sketchUri: string;
16-
readonly fqbn: string;
17+
readonly fqbn?: string | undefined;
1718
readonly optimizeForDebug: boolean;
1819
}
1920
}
2021

2122
export namespace Upload {
22-
export type Options =
23-
Compile.Options & Readonly<{ port: string }> |
24-
Compile.Options & Readonly<{ programmer: Programmer, port?: string }>;
23+
export interface Options extends Compile.Options {
24+
readonly port?: string | undefined;
25+
readonly programmer?: Programmer | undefined;
26+
}
2527
}
2628

2729
export namespace Bootloader {
2830
export interface Options {
29-
readonly fqbn: string;
30-
readonly programmer: Programmer;
31-
readonly port: string;
31+
readonly fqbn?: string | undefined;
32+
readonly port?: string | undefined;
33+
readonly programmer?: Programmer | undefined;
3234
}
3335
}
3436

Diff for: arduino-ide-extension/src/common/protocol/output-service.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
export interface OutputMessage {
22
readonly name: string;
33
readonly chunk: string;
4-
readonly severity?: 'error' | 'warning' | 'info';
4+
readonly severity?: 'error' | 'warning' | 'info'; // Currently not used!
55
}
66

77
export const OutputServicePath = '/services/output-service';

Diff for: arduino-ide-extension/src/node/core-service-impl.ts

+49-36
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,12 @@ import { dirname } from 'path';
44
import { CoreService } from '../common/protocol/core-service';
55
import { CompileReq, CompileResp } from './cli-protocol/commands/compile_pb';
66
import { CoreClientProvider } from './core-client-provider';
7-
import { UploadReq, UploadResp, BurnBootloaderReq, BurnBootloaderResp } from './cli-protocol/commands/upload_pb';
7+
import { UploadReq, UploadResp, BurnBootloaderReq, BurnBootloaderResp, UploadUsingProgrammerReq, UploadUsingProgrammerResp } from './cli-protocol/commands/upload_pb';
88
import { OutputService } from '../common/protocol/output-service';
99
import { NotificationServiceServer } from '../common/protocol';
10+
import { ClientReadableStream } from '@grpc/grpc-js';
11+
import { ArduinoCoreClient } from './cli-protocol/commands/commands_grpc_pb';
12+
import { firstToUpperCase, firstToLowerCase } from '../common/utils';
1013

1114
@injectable()
1215
export class CoreServiceImpl implements CoreService {
@@ -21,7 +24,7 @@ export class CoreServiceImpl implements CoreService {
2124
protected readonly notificationService: NotificationServiceServer;
2225

2326
async compile(options: CoreService.Compile.Options): Promise<void> {
24-
this.outputService.append({ name: 'compile', chunk: 'Compiling...\n' + JSON.stringify(options, null, 2) + '\n--------------------------\n' });
27+
this.outputService.append({ name: 'compile', chunk: 'Compile...\n' + JSON.stringify(options, null, 2) + '\n--------------------------\n' });
2528
const { sketchUri, fqbn } = options;
2629
const sketchFilePath = FileUri.fsPath(sketchUri);
2730
const sketchpath = dirname(sketchFilePath);
@@ -32,14 +35,12 @@ export class CoreServiceImpl implements CoreService {
3235
}
3336
const { client, instance } = coreClient;
3437

35-
if (!fqbn) {
36-
throw new Error('The selected board has no FQBN.');
37-
}
38-
3938
const compilerReq = new CompileReq();
4039
compilerReq.setInstance(instance);
4140
compilerReq.setSketchpath(sketchpath);
42-
compilerReq.setFqbn(fqbn);
41+
if (fqbn) {
42+
compilerReq.setFqbn(fqbn);
43+
}
4344
compilerReq.setOptimizefordebug(options.optimizeForDebug);
4445
compilerReq.setPreprocess(false);
4546
compilerReq.setVerbose(true);
@@ -63,9 +64,23 @@ export class CoreServiceImpl implements CoreService {
6364
}
6465

6566
async upload(options: CoreService.Upload.Options): Promise<void> {
67+
await this.doUpload(options, () => new UploadReq(), (client, req) => client.upload(req));
68+
}
69+
70+
async uploadUsingProgrammer(options: CoreService.Upload.Options): Promise<void> {
71+
await this.doUpload(options, () => new UploadUsingProgrammerReq(), (client, req) => client.uploadUsingProgrammer(req), 'upload using programmer');
72+
}
73+
74+
protected async doUpload(
75+
options: CoreService.Upload.Options,
76+
requestProvider: () => UploadReq | UploadUsingProgrammerReq,
77+
responseHandler: (client: ArduinoCoreClient, req: UploadReq | UploadUsingProgrammerReq) => ClientReadableStream<UploadResp | UploadUsingProgrammerResp>,
78+
task: string = 'upload'): Promise<void> {
79+
6680
await this.compile(options);
67-
this.outputService.append({ name: 'upload', chunk: 'Uploading...\n' + JSON.stringify(options, null, 2) + '\n--------------------------\n' });
68-
const { sketchUri, fqbn } = options;
81+
const chunk = firstToUpperCase(task) + '...\n';
82+
this.outputService.append({ name: 'upload', chunk: chunk + JSON.stringify(options, null, 2) + '\n--------------------------\n' });
83+
const { sketchUri, fqbn, port, programmer } = options;
6984
const sketchFilePath = FileUri.fsPath(sketchUri);
7085
const sketchpath = dirname(sketchFilePath);
7186

@@ -75,34 +90,32 @@ export class CoreServiceImpl implements CoreService {
7590
}
7691
const { client, instance } = coreClient;
7792

78-
if (!fqbn) {
79-
throw new Error('The selected board has no FQBN.');
93+
const req = requestProvider();
94+
req.setInstance(instance);
95+
req.setSketchPath(sketchpath);
96+
if (fqbn) {
97+
req.setFqbn(fqbn);
8098
}
81-
82-
const uploadReq = new UploadReq();
83-
uploadReq.setInstance(instance);
84-
uploadReq.setSketchPath(sketchpath);
85-
uploadReq.setFqbn(fqbn);
86-
if ('programmer' in options) {
87-
uploadReq.setProgrammer(options.programmer.id);
99+
if (port) {
100+
req.setPort(port);
88101
}
89-
if (options.port) {
90-
uploadReq.setPort(options.port);
102+
if (programmer) {
103+
req.setProgrammer(programmer.id);
91104
}
92-
const result = client.upload(uploadReq);
105+
const result = responseHandler(client, req);
93106

94107
try {
95108
await new Promise<void>((resolve, reject) => {
96109
result.on('data', (resp: UploadResp) => {
97-
this.outputService.append({ name: 'upload', chunk: Buffer.from(resp.getOutStream_asU8()).toString() });
98-
this.outputService.append({ name: 'upload', chunk: Buffer.from(resp.getErrStream_asU8()).toString() });
110+
this.outputService.append({ name: task, chunk: Buffer.from(resp.getOutStream_asU8()).toString() });
111+
this.outputService.append({ name: task, chunk: Buffer.from(resp.getErrStream_asU8()).toString() });
99112
});
100113
result.on('error', error => reject(error));
101114
result.on('end', () => resolve());
102115
});
103-
this.outputService.append({ name: 'upload', chunk: '\n--------------------------\nUpload complete.\n' });
116+
this.outputService.append({ name: 'upload', chunk: '\n--------------------------\n' + firstToLowerCase(task) + ' complete.\n' });
104117
} catch (e) {
105-
this.outputService.append({ name: 'upload', chunk: `Upload error: ${e}\n`, severity: 'error' });
118+
this.outputService.append({ name: 'upload', chunk: `${firstToUpperCase(task)} error: ${e}\n`, severity: 'error' });
106119
throw e;
107120
}
108121
}
@@ -113,19 +126,19 @@ export class CoreServiceImpl implements CoreService {
113126
return;
114127
}
115128
const { fqbn, port, programmer } = options;
116-
if (!fqbn) {
117-
throw new Error('The selected board has no FQBN.');
129+
const { client, instance } = coreClient;
130+
const burnReq = new BurnBootloaderReq();
131+
burnReq.setInstance(instance);
132+
if (fqbn) {
133+
burnReq.setFqbn(fqbn);
118134
}
119-
if (!port) {
120-
throw new Error('Port must be specified.');
135+
if (port) {
136+
burnReq.setPort(port);
121137
}
122-
const { client, instance } = coreClient;
123-
const req = new BurnBootloaderReq();
124-
req.setFqbn(fqbn);
125-
req.setPort(port);
126-
req.setProgrammer(programmer.id);
127-
req.setInstance(instance);
128-
const result = client.burnBootloader(req);
138+
if (programmer) {
139+
burnReq.setProgrammer(programmer.id);
140+
}
141+
const result = client.burnBootloader(burnReq);
129142
try {
130143
await new Promise<void>((resolve, reject) => {
131144
result.on('data', (resp: BurnBootloaderResp) => {

0 commit comments

Comments
 (0)