Skip to content

Commit

Permalink
fix: propagate monitor errors to the frontend
Browse files Browse the repository at this point in the history
Closes #1508

Signed-off-by: Akos Kitta <[email protected]>
  • Loading branch information
Akos Kitta committed Mar 17, 2023
1 parent 9b49712 commit 9cced4b
Show file tree
Hide file tree
Showing 7 changed files with 272 additions and 135 deletions.
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import {
ApplicationError,
CommandRegistry,
Disposable,
Emitter,
Expand Down Expand Up @@ -167,7 +168,13 @@ export class MonitorManagerProxyClientImpl
const { selectedBoard, selectedPort } =
this.boardsServiceProvider.boardsConfig;
if (!selectedBoard || !selectedBoard.fqbn || !selectedPort) return;
await this.server().startMonitor(selectedBoard, selectedPort, settings);
try {
await this.server().startMonitor(selectedBoard, selectedPort, settings);
} catch (err) {
this.messageService.error(
ApplicationError.is(err) ? err.message : String(err)
);
}
}

getCurrentSettings(board: Board, port: Port): Promise<MonitorSettings> {
Expand Down
176 changes: 155 additions & 21 deletions arduino-ide-extension/src/common/protocol/monitor-service.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Event, JsonRpcServer } from '@theia/core';
import { ApplicationError, Event, JsonRpcServer, nls } from '@theia/core';
import {
PluggableMonitorSettings,
MonitorSettings,
Expand Down Expand Up @@ -46,7 +46,7 @@ export interface PluggableMonitorSetting {
readonly id: string;
// A human-readable label of the setting (to be displayed on the GUI)
readonly label: string;
// The setting type (at the moment only "enum" is avaiable)
// The setting type (at the moment only "enum" is available)
readonly type: string;
// The values allowed on "enum" types
readonly values: string[];
Expand All @@ -72,24 +72,158 @@ export namespace Monitor {
};
}

export interface Status {}
export type OK = Status;
export interface ErrorStatus extends Status {
readonly message: string;
export const MonitorErrorCodes = {
ConnectionFailed: 6001,
NotConnected: 6002,
AlreadyConnected: 6003,
MissingConfiguration: 6004,
UploadInProgress: 6005,
} as const;

export const ConnectionFailedError = createMonitorError(
MonitorErrorCodes.ConnectionFailed
);
export const NotConnectedError = createMonitorError(
MonitorErrorCodes.NotConnected
);
export const AlreadyConnectedError = createMonitorError(
MonitorErrorCodes.AlreadyConnected
);
export const MissingConfigurationError = createMonitorError(
MonitorErrorCodes.MissingConfiguration
);
export const UploadInProgressError = createMonitorError(
MonitorErrorCodes.UploadInProgress
);

export function createConnectionFailedError(
port: Port,
details?: string
): ApplicationError<number, PortDescriptor> {
const { protocol, protocolLabel, address, addressLabel } = port;
return ConnectionFailedError(
connectionFailedErrorLabel(addressLabel, protocolLabel, details),
{ protocol, address }
);
}
export namespace Status {
export function isOK(status: Status & { message?: string }): status is OK {
return !!status && typeof status.message !== 'string';
}
export const OK: OK = {};
export const NOT_CONNECTED: ErrorStatus = { message: 'Not connected.' };
export const ALREADY_CONNECTED: ErrorStatus = {
message: 'Already connected.',
};
export const CONFIG_MISSING: ErrorStatus = {
message: 'Serial Config missing.',
};
export const UPLOAD_IN_PROGRESS: ErrorStatus = {
message: 'Upload in progress.',
};

export function createNotConnectedError(
port: Port
): ApplicationError<number, PortDescriptor> {
const { protocol, protocolLabel, address, addressLabel } = port;
return NotConnectedError(
notConnectedErrorLabel(addressLabel, protocolLabel),
{ protocol, address }
);
}

export function createAlreadyConnectedError(
port: Port
): ApplicationError<number, PortDescriptor> {
const { protocol, protocolLabel, address, addressLabel } = port;
return AlreadyConnectedError(
alreadyConnectedErrorLabel(addressLabel, protocolLabel),
{ protocol, address }
);
}

export function createMissingConfigurationError(
port: Port
): ApplicationError<number, PortDescriptor> {
const { protocol, protocolLabel, address, addressLabel } = port;
return MissingConfigurationError(
missingConfigurationErrorLabel(addressLabel, protocolLabel),
{ protocol, address }
);
}

export function createUploadInProgressError(
port: Port
): ApplicationError<number, PortDescriptor> {
const { protocol, protocolLabel, address, addressLabel } = port;
return UploadInProgressError(
uploadInProgressErrorLabel(addressLabel, protocolLabel),
{ protocol, address }
);
}

/**
* Bare minimum representation of a port. Supports neither UI labels nor properties.
*/
export interface PortDescriptor {
readonly protocol: string;
readonly address: string;
}

export function createPortDescriptor(port: Port): PortDescriptor {
const { protocol, address } = port;
return { protocol, address };
}

function createMonitorError(
code: number
): ApplicationError.Constructor<number, PortDescriptor> {
return ApplicationError.declare(
code,
(message: string, data: PortDescriptor) => ({ data, message })
);
}

export function connectionFailedErrorLabel(
addressLabel: string,
protocolLabel: string,
details?: string
): string {
const formattedDetails = details ? `: ${details}.` : '.';
return nls.localize(
'arduino/monitor/connectionFailedError',
'Could not connect to {0} {1} port{2}',
addressLabel,
protocolLabel,
formattedDetails
);
}
export function notConnectedErrorLabel(
addressLabel: string,
protocolLabel: string
): string {
return nls.localize(
'arduino/monitor/notConnectedError',
'Not connected to {0} {1} port.',
addressLabel,
protocolLabel
);
}
export function missingConfigurationErrorLabel(
addressLabel: string,
protocolLabel: string
): string {
return nls.localize(
'arduino/monitor/missingConfigurationError',
'Could not connect to {0} {1} port. The monitor configuration is missing.',
addressLabel,
protocolLabel
);
}
export function uploadInProgressErrorLabel(
addressLabel: string,
protocolLabel: string
): string {
return nls.localize(
'arduino/monitor/uploadInProgressError',
'Could not connect to {0} {1} port. An upload is in progress.',
addressLabel,
protocolLabel
);
}
export function alreadyConnectedErrorLabel(
addressLabel: string,
protocolLabel: string
): string {
return nls.localize(
'arduino/monitor/alreadyConnectedError',
'Could not connect to {0} {1} port. Already connected.',
addressLabel,
protocolLabel
);
}
4 changes: 2 additions & 2 deletions arduino-ide-extension/src/node/core-service-impl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ import {
UploadUsingProgrammerResponse,
} from './cli-protocol/cc/arduino/cli/commands/v1/upload_pb';
import { ResponseService } from '../common/protocol/response-service';
import { OutputMessage, Port, Status } from '../common/protocol';
import { OutputMessage, Port } from '../common/protocol';
import { ArduinoCoreServiceClient } from './cli-protocol/cc/arduino/cli/commands/v1/commands_grpc_pb';
import { Port as RpcPort } from './cli-protocol/cc/arduino/cli/commands/v1/port_pb';
import { ApplicationError, CommandService, Disposable, nls } from '@theia/core';
Expand Down Expand Up @@ -392,7 +392,7 @@ export class CoreServiceImpl extends CoreClientAware implements CoreService {
}: {
fqbn?: string | undefined;
port?: Port | undefined;
}): Promise<Status> {
}): Promise<void> {
this.boardDiscovery.setUploadInProgress(false);
return this.monitorManager.notifyUploadFinished(fqbn, port);
}
Expand Down
8 changes: 2 additions & 6 deletions arduino-ide-extension/src/node/monitor-manager-proxy-impl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ import { inject, injectable } from '@theia/core/shared/inversify';
import {
MonitorManagerProxy,
MonitorManagerProxyClient,
Status,
} from '../common/protocol';
import { Board, Port } from '../common/protocol';
import { MonitorManager } from './monitor-manager';
Expand Down Expand Up @@ -41,11 +40,8 @@ export class MonitorManagerProxyImpl implements MonitorManagerProxy {
await this.changeMonitorSettings(board, port, settings);
}

const connectToClient = (status: Status) => {
if (status === Status.ALREADY_CONNECTED || status === Status.OK) {
// Monitor started correctly, connect it with the frontend
this.client.connect(this.manager.getWebsocketAddressPort(board, port));
}
const connectToClient = async () => {
this.client.connect(this.manager.getWebsocketAddressPort(board, port));
};
return this.manager.startMonitor(board, port, connectToClient);
}
Expand Down
20 changes: 9 additions & 11 deletions arduino-ide-extension/src/node/monitor-manager.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { ILogger } from '@theia/core';
import { inject, injectable, named } from '@theia/core/shared/inversify';
import { Board, BoardsService, Port, Status } from '../common/protocol';
import { Board, BoardsService, Port } from '../common/protocol';
import { CoreClientAware } from './core-client-provider';
import { MonitorService } from './monitor-service';
import { MonitorServiceFactory } from './monitor-service-factory';
Expand Down Expand Up @@ -36,7 +36,7 @@ export class MonitorManager extends CoreClientAware {
private monitorServiceStartQueue: {
monitorID: string;
serviceStartParams: [Board, Port];
connectToClient: (status: Status) => void;
connectToClient: () => Promise<void>;
}[] = [];

@inject(MonitorServiceFactory)
Expand Down Expand Up @@ -104,7 +104,7 @@ export class MonitorManager extends CoreClientAware {
async startMonitor(
board: Board,
port: Port,
connectToClient: (status: Status) => void
connectToClient: () => Promise<void>
): Promise<void> {
const monitorID = this.monitorID(board.fqbn, port);

Expand All @@ -127,8 +127,8 @@ export class MonitorManager extends CoreClientAware {
return;
}

const result = await monitor.start();
connectToClient(result);
await monitor.start();
await connectToClient();
}

/**
Expand Down Expand Up @@ -202,8 +202,7 @@ export class MonitorManager extends CoreClientAware {
async notifyUploadFinished(
fqbn?: string | undefined,
port?: Port
): Promise<Status> {
let status: Status = Status.NOT_CONNECTED;
): Promise<void> {
let portDidChangeOnUpload = false;

// We have no way of knowing which monitor
Expand All @@ -214,7 +213,7 @@ export class MonitorManager extends CoreClientAware {

const monitor = this.monitorServices.get(monitorID);
if (monitor) {
status = await monitor.start();
await monitor.start();
}

// this monitorID will only be present in "disposedForUpload"
Expand All @@ -232,7 +231,6 @@ export class MonitorManager extends CoreClientAware {
}

await this.startQueuedServices(portDidChangeOnUpload);
return status;
}

async startQueuedServices(portDidChangeOnUpload: boolean): Promise<void> {
Expand Down Expand Up @@ -261,8 +259,8 @@ export class MonitorManager extends CoreClientAware {
const monitorService = this.monitorServices.get(monitorID);

if (monitorService) {
const result = await monitorService.start();
connectToClient(result);
await monitorService.start();
await connectToClient();
}
}
}
Expand Down
Loading

0 comments on commit 9cced4b

Please sign in to comment.