diff --git a/arduino-ide-extension/src/browser/boards/boards-config.tsx b/arduino-ide-extension/src/browser/boards/boards-config.tsx
index ce5d9eda1..e3d36fb68 100644
--- a/arduino-ide-extension/src/browser/boards/boards-config.tsx
+++ b/arduino-ide-extension/src/browser/boards/boards-config.tsx
@@ -354,7 +354,7 @@ export class BoardsConfig extends React.Component<
{ports.map((port) => (
-
- key={`${port.id}`}
+ key={`${Port.keyOf(port)}`}
item={port}
label={Port.toString(port)}
selected={Port.sameAs(this.state.selectedPort, port)}
diff --git a/arduino-ide-extension/src/browser/boards/boards-service-provider.ts b/arduino-ide-extension/src/browser/boards/boards-service-provider.ts
index e84e91e56..872e7d44b 100644
--- a/arduino-ide-extension/src/browser/boards/boards-service-provider.ts
+++ b/arduino-ide-extension/src/browser/boards/boards-service-provider.ts
@@ -13,6 +13,7 @@ import {
AttachedBoardsChangeEvent,
BoardWithPackage,
BoardUserField,
+ AvailablePorts,
} from '../../common/protocol';
import { BoardsConfig } from './boards-config';
import { naturalCompare } from '../../common/utils';
@@ -21,6 +22,7 @@ import { StorageWrapper } from '../storage-wrapper';
import { nls } from '@theia/core/lib/common';
import { Deferred } from '@theia/core/lib/common/promise-util';
import { FrontendApplicationStateService } from '@theia/core/lib/browser/frontend-application-state';
+import { Unknown } from '../../common/nls';
@injectable()
export class BoardsServiceProvider implements FrontendApplicationContribution {
@@ -96,11 +98,12 @@ export class BoardsServiceProvider implements FrontendApplicationContribution {
);
this.appStateService.reachedState('ready').then(async () => {
- const [attachedBoards, availablePorts] = await Promise.all([
- this.boardsService.getAttachedBoards(),
- this.boardsService.getAvailablePorts(),
+ const [state] = await Promise.all([
+ this.boardsService.getState(),
this.loadState(),
]);
+ const { boards: attachedBoards, ports: availablePorts } =
+ AvailablePorts.split(state);
this._attachedBoards = attachedBoards;
this._availablePorts = availablePorts;
this.onAvailablePortsChangedEmitter.fire(this._availablePorts);
@@ -459,6 +462,16 @@ export class BoardsServiceProvider implements FrontendApplicationContribution {
return this._availableBoards;
}
+ /**
+ * @deprecated Do not use this API, it will be removed. This is a hack to be able to set the missing port `properties` before an upload.
+ *
+ * See: https://github.com/arduino/arduino-ide/pull/1335#issuecomment-1224355236.
+ */
+ // TODO: remove this API and fix the selected board config store/restore correctly.
+ get availablePorts(): Port[] {
+ return this._availablePorts.slice();
+ }
+
async waitUntilAvailable(
what: Board & { port: Port },
timeout?: number
@@ -558,7 +571,7 @@ export class BoardsServiceProvider implements FrontendApplicationContribution {
};
} else {
availableBoard = {
- name: nls.localize('arduino/common/unknown', 'Unknown'),
+ name: Unknown,
port: boardPort,
state: AvailableBoard.State.incomplete,
};
diff --git a/arduino-ide-extension/src/browser/boards/boards-toolbar-item.tsx b/arduino-ide-extension/src/browser/boards/boards-toolbar-item.tsx
index a1ddb7749..93a15a3c5 100644
--- a/arduino-ide-extension/src/browser/boards/boards-toolbar-item.tsx
+++ b/arduino-ide-extension/src/browser/boards/boards-toolbar-item.tsx
@@ -138,7 +138,7 @@ export class BoardsDropDown extends React.Component {
{boardLabel}
- {port.address}
+ {port.addressLabel}
{selected ? : ''}
diff --git a/arduino-ide-extension/src/browser/contributions/board-selection.ts b/arduino-ide-extension/src/browser/contributions/board-selection.ts
index 24527ca79..21acc221b 100644
--- a/arduino-ide-extension/src/browser/contributions/board-selection.ts
+++ b/arduino-ide-extension/src/browser/contributions/board-selection.ts
@@ -288,7 +288,7 @@ PID: ${PID}`;
for (let i = 0; i < sortedIDs.length; i++) {
const portID = sortedIDs[i];
const [port, boards] = ports[portID];
- let label = `${port.address}`;
+ let label = `${port.addressLabel}`;
if (boards.length) {
const boardsList = boards.map((board) => board.name).join(', ');
label = `${label} (${boardsList})`;
@@ -331,7 +331,7 @@ PID: ${PID}`;
}
};
- const grouped = AvailablePorts.byProtocol(availablePorts);
+ const grouped = AvailablePorts.groupByProtocol(availablePorts);
let protocolOrder = 100;
// We first show serial and network ports, then all the rest
['serial', 'network'].forEach((protocol) => {
diff --git a/arduino-ide-extension/src/browser/contributions/upload-sketch.ts b/arduino-ide-extension/src/browser/contributions/upload-sketch.ts
index d3278f0ec..606647219 100644
--- a/arduino-ide-extension/src/browser/contributions/upload-sketch.ts
+++ b/arduino-ide-extension/src/browser/contributions/upload-sketch.ts
@@ -1,6 +1,6 @@
import { inject, injectable } from '@theia/core/shared/inversify';
import { Emitter } from '@theia/core/lib/common/event';
-import { BoardUserField, CoreService } from '../../common/protocol';
+import { BoardUserField, CoreService, Port } from '../../common/protocol';
import { ArduinoMenus, PlaceholderMenuNode } from '../menu/arduino-menus';
import { ArduinoToolbar } from '../toolbar/arduino-toolbar';
import {
@@ -12,7 +12,7 @@ import {
CoreServiceContribution,
} from './contribution';
import { UserFieldsDialog } from '../dialogs/user-fields/user-fields-dialog';
-import { DisposableCollection, nls } from '@theia/core/lib/common';
+import { deepClone, DisposableCollection, nls } from '@theia/core/lib/common';
import { CurrentSketch } from '../../common/protocol/sketches-service-client-impl';
import type { VerifySketchParams } from './verify-sketch';
@@ -266,7 +266,7 @@ export class UploadSketch extends CoreServiceContribution {
this.preferences.get('arduino.upload.verify'),
this.preferences.get('arduino.upload.verbose'),
]);
- const port = boardsConfig.selectedPort;
+ const port = this.maybeUpdatePortProperties(boardsConfig.selectedPort);
return {
sketch,
fqbn,
@@ -278,7 +278,29 @@ export class UploadSketch extends CoreServiceContribution {
};
}
- private userFields() {
+ /**
+ * This is a hack to ensure that the port object has the `properties` when uploading.(https://github.com/arduino/arduino-ide/issues/740)
+ * This method works around a bug when restoring a `port` persisted by an older version of IDE2. See the bug [here](https://github.com/arduino/arduino-ide/pull/1335#issuecomment-1224355236).
+ *
+ * Before the upload, this method checks the available ports and makes sure that the `properties` of an available port, and the port selected by the user have the same `properties`.
+ * This method does not update any state (for example, the `BoardsConfig.Config`) but uses the correct `properties` for the `upload`.
+ */
+ private maybeUpdatePortProperties(port: Port | undefined): Port | undefined {
+ if (port) {
+ const key = Port.keyOf(port);
+ for (const candidate of this.boardsServiceProvider.availablePorts) {
+ if (key === Port.keyOf(candidate) && candidate.properties) {
+ return {
+ ...port,
+ properties: deepClone(candidate.properties),
+ };
+ }
+ }
+ }
+ return port;
+ }
+
+ private userFields(): BoardUserField[] {
return this.cachedUserFields.get(this.selectedFqbnAddress()) ?? [];
}
diff --git a/arduino-ide-extension/src/browser/monitor-manager-proxy-client-impl.ts b/arduino-ide-extension/src/browser/monitor-manager-proxy-client-impl.ts
index 5519057aa..94ab4d0f5 100644
--- a/arduino-ide-extension/src/browser/monitor-manager-proxy-client-impl.ts
+++ b/arduino-ide-extension/src/browser/monitor-manager-proxy-client-impl.ts
@@ -145,7 +145,10 @@ export class MonitorManagerProxyClientImpl
if (
selectedBoard?.fqbn !==
this.lastConnectedBoard?.selectedBoard?.fqbn ||
- selectedPort?.id !== this.lastConnectedBoard?.selectedPort?.id
+ Port.keyOf(selectedPort) !==
+ (this.lastConnectedBoard.selectedPort
+ ? Port.keyOf(this.lastConnectedBoard.selectedPort)
+ : undefined)
) {
this.onMonitorShouldResetEmitter.fire(null);
this.lastConnectedBoard = {
diff --git a/arduino-ide-extension/src/browser/serial/monitor/serial-monitor-send-input.tsx b/arduino-ide-extension/src/browser/serial/monitor/serial-monitor-send-input.tsx
index 19f06eea1..180949726 100644
--- a/arduino-ide-extension/src/browser/serial/monitor/serial-monitor-send-input.tsx
+++ b/arduino-ide-extension/src/browser/serial/monitor/serial-monitor-send-input.tsx
@@ -5,6 +5,7 @@ import { isOSX } from '@theia/core/lib/common/os';
import { DisposableCollection, nls } from '@theia/core/lib/common';
import { BoardsServiceProvider } from '../../boards/boards-service-provider';
import { MonitorModel } from '../../monitor-model';
+import { Unknown } from '../../../common/nls';
export namespace SerialMonitorSendInput {
export interface Props {
@@ -86,8 +87,8 @@ export class SerialMonitorSendInput extends React.Component<
? Board.toString(board, {
useFqbn: false,
})
- : 'unknown',
- port ? port.address : 'unknown'
+ : Unknown,
+ port ? port.address : Unknown
);
}
diff --git a/arduino-ide-extension/src/browser/widgets/component-list/list-item-renderer.tsx b/arduino-ide-extension/src/browser/widgets/component-list/list-item-renderer.tsx
index b11ea2a8e..62e1f3e1c 100644
--- a/arduino-ide-extension/src/browser/widgets/component-list/list-item-renderer.tsx
+++ b/arduino-ide-extension/src/browser/widgets/component-list/list-item-renderer.tsx
@@ -5,6 +5,7 @@ import { Installable } from '../../../common/protocol/installable';
import { ArduinoComponent } from '../../../common/protocol/arduino-component';
import { ComponentListItem } from './component-list-item';
import { nls } from '@theia/core/lib/common';
+import { Unknown } from '../../../common/nls';
@injectable()
export class ListItemRenderer {
@@ -42,11 +43,7 @@ export class ListItemRenderer {
} else if ((item as any).id) {
nameAndAuthor = {(item as any).id};
} else {
- nameAndAuthor = (
-
- {nls.localize('arduino/common/unknown', 'Unknown')}
-
- );
+ nameAndAuthor = {Unknown};
}
const onClickUninstall = () => uninstall(item);
const installedVersion = !!item.installedVersion && (
diff --git a/arduino-ide-extension/src/common/nls.ts b/arduino-ide-extension/src/common/nls.ts
new file mode 100644
index 000000000..2435ff3b4
--- /dev/null
+++ b/arduino-ide-extension/src/common/nls.ts
@@ -0,0 +1,3 @@
+import { nls } from '@theia/core/lib/common/nls';
+
+export const Unknown = nls.localize('arduino/common/unknown', 'Unknown');
diff --git a/arduino-ide-extension/src/common/protocol/boards-service.ts b/arduino-ide-extension/src/common/protocol/boards-service.ts
index c21773cf2..6ff6e440f 100644
--- a/arduino-ide-extension/src/common/protocol/boards-service.ts
+++ b/arduino-ide-extension/src/common/protocol/boards-service.ts
@@ -5,7 +5,7 @@ import { ArduinoComponent } from './arduino-component';
export type AvailablePorts = Record]>;
export namespace AvailablePorts {
- export function byProtocol(
+ export function groupByProtocol(
availablePorts: AvailablePorts
): Map {
const grouped = new Map();
@@ -20,6 +20,21 @@ export namespace AvailablePorts {
}
return grouped;
}
+ export function split(
+ state: AvailablePorts
+ ): Readonly<{ boards: Board[]; ports: Port[] }> {
+ const availablePorts: Port[] = [];
+ const attachedBoards: Board[] = [];
+ for (const key of Object.keys(state)) {
+ const [port, boards] = state[key];
+ availablePorts.push(port);
+ attachedBoards.push(...boards);
+ }
+ return {
+ boards: attachedBoards,
+ ports: availablePorts,
+ };
+ }
}
export interface AttachedBoardsChangeEvent {
@@ -117,16 +132,6 @@ export const BoardsService = Symbol('BoardsService');
export interface BoardsService
extends Installable,
Searchable {
- /**
- * Deprecated. `getState` should be used to correctly map a board with a port.
- * @deprecated
- */
- getAttachedBoards(): Promise;
- /**
- * Deprecated. `getState` should be used to correctly map a board with a port.
- * @deprecated
- */
- getAvailablePorts(): Promise;
getState(): Promise;
getBoardDetails(options: { fqbn: string }): Promise;
getBoardPackage(options: { id: string }): Promise;
@@ -141,28 +146,55 @@ export interface BoardsService
}
export interface Port {
- // id is the combination of address and protocol
- // formatted like "|" used
- // to uniquely recognize a port
- readonly id: string;
readonly address: string;
readonly addressLabel: string;
readonly protocol: string;
readonly protocolLabel: string;
+ readonly properties?: Record;
}
export namespace Port {
- export function is(arg: any): arg is Port {
- return (
- !!arg &&
- 'address' in arg &&
- typeof arg['address'] === 'string' &&
- 'protocol' in arg &&
- typeof arg['protocol'] === 'string'
- );
+ export type Properties = Record;
+ export namespace Properties {
+ export function create(
+ properties: [string, string][] | undefined
+ ): Properties {
+ if (!properties) {
+ return {};
+ }
+ return properties.reduce((acc, curr) => {
+ const [key, value] = curr;
+ acc[key] = value;
+ return acc;
+ }, {} as Record);
+ }
+ }
+ export function is(arg: unknown): arg is Port {
+ if (typeof arg === 'object') {
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
+ const object = arg as any;
+ return (
+ 'address' in object &&
+ typeof object['address'] === 'string' &&
+ 'addressLabel' in object &&
+ typeof object['addressLabel'] === 'string' &&
+ 'protocol' in object &&
+ typeof object['protocol'] === 'string' &&
+ 'protocolLabel' in object &&
+ typeof object['protocolLabel'] === 'string'
+ );
+ }
+ return false;
+ }
+
+ /**
+ * Key is the combination of address and protocol formatted like `'${address}|${protocol}'` used to uniquely identify a port.
+ */
+ export function keyOf({ address, protocol }: Port): string {
+ return `${address}|${protocol}`;
}
- export function toString(port: Port): string {
- return `${port.addressLabel} ${port.protocolLabel}`;
+ export function toString({ addressLabel, protocolLabel }: Port): string {
+ return `${addressLabel} ${protocolLabel}`;
}
export function compare(left: Port, right: Port): number {
diff --git a/arduino-ide-extension/src/node/auth/utils.ts b/arduino-ide-extension/src/node/auth/utils.ts
index a7bc36110..f04e938b3 100644
--- a/arduino-ide-extension/src/node/auth/utils.ts
+++ b/arduino-ide-extension/src/node/auth/utils.ts
@@ -3,6 +3,7 @@ import { sha256 } from 'hash.js';
import { randomBytes } from 'crypto';
import btoa = require('btoa'); // TODO: check why we cannot
import { AuthenticationSession } from './types';
+import { Unknown } from '../../common/nls';
export interface IToken {
accessToken: string; // When unable to refresh due to network problems, the access token becomes undefined
@@ -62,10 +63,10 @@ export function token2IToken(token: Token): IToken {
sessionId: parsedIdToken.sub,
scope: token.scope,
account: {
- id: parsedIdToken.sub || 'unknown',
- email: parsedIdToken.email || 'unknown',
- nickname: parsedIdToken.nickname || 'unknown',
- picture: parsedIdToken.picture || 'unknown',
+ id: parsedIdToken.sub || Unknown,
+ email: parsedIdToken.email || Unknown,
+ nickname: parsedIdToken.nickname || Unknown,
+ picture: parsedIdToken.picture || Unknown,
},
};
}
diff --git a/arduino-ide-extension/src/node/board-discovery.ts b/arduino-ide-extension/src/node/board-discovery.ts
index 65663ced2..4f4bf40ee 100644
--- a/arduino-ide-extension/src/node/board-discovery.ts
+++ b/arduino-ide-extension/src/node/board-discovery.ts
@@ -1,27 +1,30 @@
-import { injectable, inject, named } from '@theia/core/shared/inversify';
import { ClientDuplexStream } from '@grpc/grpc-js';
+import { DisposableCollection } from '@theia/core/lib/common/disposable';
+import { Emitter, Event } from '@theia/core/lib/common/event';
import { ILogger } from '@theia/core/lib/common/logger';
import { deepClone } from '@theia/core/lib/common/objects';
-import { CoreClientAware } from './core-client-provider';
-import {
- BoardListWatchRequest,
- BoardListWatchResponse,
-} from './cli-protocol/cc/arduino/cli/commands/v1/board_pb';
+import { Deferred } from '@theia/core/lib/common/promise-util';
+import { BackendApplicationContribution } from '@theia/core/lib/node';
+import { inject, injectable, named } from '@theia/core/shared/inversify';
+import { Disposable } from '@theia/core/shared/vscode-languageserver-protocol';
+import { v4 } from 'uuid';
+import { Unknown } from '../common/nls';
import {
+ AttachedBoardsChangeEvent,
+ AvailablePorts,
Board,
- Port,
NotificationServiceServer,
- AvailablePorts,
- AttachedBoardsChangeEvent,
+ Port,
} from '../common/protocol';
-import { Emitter, Event } from '@theia/core/lib/common/event';
-import { DisposableCollection } from '@theia/core/lib/common/disposable';
-import { Disposable } from '@theia/core/shared/vscode-languageserver-protocol';
+import {
+ BoardListWatchRequest,
+ BoardListWatchResponse,
+ DetectedPort as RpcDetectedPort,
+} from './cli-protocol/cc/arduino/cli/commands/v1/board_pb';
import { ArduinoCoreServiceClient } from './cli-protocol/cc/arduino/cli/commands/v1/commands_grpc_pb';
-import { v4 } from 'uuid';
+import { Port as RpcPort } from './cli-protocol/cc/arduino/cli/commands/v1/port_pb';
+import { CoreClientAware } from './core-client-provider';
import { ServiceError } from './service-error';
-import { BackendApplicationContribution } from '@theia/core/lib/node';
-import { Deferred } from '@theia/core/lib/common/promise-util';
type Duplex = ClientDuplexStream;
interface StreamWrapper extends Disposable {
@@ -125,8 +128,8 @@ export class BoardDiscovery
});
}
- setUploadInProgress(uploadAttemptInProgress: boolean): void {
- this.uploadInProgress = uploadAttemptInProgress;
+ setUploadInProgress(uploadInProgress: boolean): void {
+ this.uploadInProgress = uploadInProgress;
}
private createTimeout(
@@ -216,7 +219,7 @@ export class BoardDiscovery
} else {
throw new Error(`Unhandled object type: ${arg}`);
}
- return JSON.stringify(object);
+ return JSON.stringify(object, null, 2); // TODO: remove `space`?
}
async start(): Promise {
@@ -234,103 +237,7 @@ export class BoardDiscovery
this.logger.info('start new deferred');
const { client, instance } = await this.coreClient;
const wrapper = await this.createWrapper(client);
- wrapper.stream.on('data', async (resp: BoardListWatchResponse) => {
- this.logger.info('onData', this.toJson(resp));
- if (resp.getEventType() === 'quit') {
- this.logger.info('quit received');
- this.stop();
- return;
- }
-
- const detectedPort = resp.getPort();
- if (detectedPort) {
- let eventType: 'add' | 'remove' | 'unknown' = 'unknown';
- if (resp.getEventType() === 'add') {
- eventType = 'add';
- } else if (resp.getEventType() === 'remove') {
- eventType = 'remove';
- } else {
- eventType = 'unknown';
- }
-
- if (eventType === 'unknown') {
- throw new Error(`Unexpected event type: '${resp.getEventType()}'`);
- }
-
- const oldState = deepClone(this._availablePorts);
- const newState = deepClone(this._availablePorts);
-
- const address = (detectedPort as any).getPort().getAddress();
- const protocol = (detectedPort as any).getPort().getProtocol();
- // Different discoveries can detect the same port with different
- // protocols, so we consider the combination of address and protocol
- // to be the id of a certain port to distinguish it from others.
- // If we'd use only the address of a port to store it in a map
- // we can have conflicts the same port is found with multiple
- // protocols.
- const portID = `${address}|${protocol}`;
- const label = (detectedPort as any).getPort().getLabel();
- const protocolLabel = (detectedPort as any)
- .getPort()
- .getProtocolLabel();
- const port = {
- id: portID,
- address,
- addressLabel: label,
- protocol,
- protocolLabel,
- };
- const boards: Board[] = [];
- for (const item of detectedPort.getMatchingBoardsList()) {
- boards.push({
- fqbn: item.getFqbn(),
- name: item.getName() || 'unknown',
- port,
- });
- }
-
- if (eventType === 'add') {
- if (newState[portID]) {
- const [, knownBoards] = newState[portID];
- this.logger.warn(
- `Port '${Port.toString(
- port
- )}' was already available. Known boards before override: ${JSON.stringify(
- knownBoards
- )}`
- );
- }
- newState[portID] = [port, boards];
- } else if (eventType === 'remove') {
- if (!newState[portID]) {
- this.logger.warn(
- `Port '${Port.toString(port)}' was not available. Skipping`
- );
- return;
- }
- delete newState[portID];
- }
-
- const oldAvailablePorts = this.getAvailablePorts(oldState);
- const oldAttachedBoards = this.getAttachedBoards(oldState);
- const newAvailablePorts = this.getAvailablePorts(newState);
- const newAttachedBoards = this.getAttachedBoards(newState);
- const event: AttachedBoardsChangeEvent = {
- oldState: {
- ports: oldAvailablePorts,
- boards: oldAttachedBoards,
- },
- newState: {
- ports: newAvailablePorts,
- boards: newAttachedBoards,
- },
- uploadInProgress: this.uploadInProgress,
- };
-
- this._availablePorts = newState;
- this.notificationService.notifyAttachedBoardsDidChange(event);
- }
- });
+ wrapper.stream.on('data', (resp) => this.onBoardListWatchResponse(resp));
this.logger.info('start request start watch');
await this.requestStartWatch(
new BoardListWatchRequest().setInstance(instance),
@@ -341,21 +248,124 @@ export class BoardDiscovery
this.logger.info('start resolved watching');
}
- getAttachedBoards(state: AvailablePorts = this.availablePorts): Board[] {
- const attachedBoards: Board[] = [];
- for (const portID of Object.keys(state)) {
- const [, boards] = state[portID];
- attachedBoards.push(...boards);
+ // XXX: make this `protected` and override for tests if IDE2 wants to mock events from the CLI.
+ private onBoardListWatchResponse(resp: BoardListWatchResponse): void {
+ this.logger.info(this.toJson(resp));
+ const eventType = EventType.parse(resp.getEventType());
+
+ if (eventType === EventType.Quit) {
+ this.logger.info('quit received');
+ this.stop();
+ return;
}
- return attachedBoards;
+
+ const detectedPort = resp.getPort();
+ if (detectedPort) {
+ const { port, boards } = this.fromRpc(detectedPort);
+ if (!port) {
+ if (!!boards.length) {
+ console.warn(
+ `Could not detect the port, but unexpectedly received discovered boards. This is most likely a bug! Response was: ${this.toJson(
+ resp
+ )}`
+ );
+ }
+ return;
+ }
+ const oldState = deepClone(this._availablePorts);
+ const newState = deepClone(this._availablePorts);
+ const key = Port.keyOf(port);
+
+ if (eventType === EventType.Add) {
+ if (newState[key]) {
+ const [, knownBoards] = newState[key];
+ this.logger.warn(
+ `Port '${Port.toString(
+ port
+ )}' was already available. Known boards before override: ${JSON.stringify(
+ knownBoards
+ )}`
+ );
+ }
+ newState[key] = [port, boards];
+ } else if (eventType === EventType.Remove) {
+ if (!newState[key]) {
+ this.logger.warn(
+ `Port '${Port.toString(port)}' was not available. Skipping`
+ );
+ return;
+ }
+ delete newState[key];
+ }
+
+ const event: AttachedBoardsChangeEvent = {
+ oldState: {
+ ...AvailablePorts.split(oldState),
+ },
+ newState: {
+ ...AvailablePorts.split(newState),
+ },
+ uploadInProgress: this.uploadInProgress,
+ };
+
+ this._availablePorts = newState;
+ this.notificationService.notifyAttachedBoardsDidChange(event);
+ }
+ }
+
+ private fromRpc(detectedPort: RpcDetectedPort): DetectedPort {
+ const rpcPort = detectedPort.getPort();
+ const port = rpcPort && this.fromRpcPort(rpcPort);
+ const boards = detectedPort.getMatchingBoardsList().map(
+ (board) =>
+ ({
+ fqbn: board.getFqbn(),
+ name: board.getName() || Unknown,
+ port,
+ } as Board)
+ );
+ return {
+ boards,
+ port,
+ };
}
- getAvailablePorts(state: AvailablePorts = this.availablePorts): Port[] {
- const availablePorts: Port[] = [];
- for (const portID of Object.keys(state)) {
- const [port] = state[portID];
- availablePorts.push(port);
+ private fromRpcPort(rpcPort: RpcPort): Port {
+ const port = {
+ address: rpcPort.getAddress(),
+ addressLabel: rpcPort.getLabel(),
+ protocol: rpcPort.getProtocol(),
+ protocolLabel: rpcPort.getProtocolLabel(),
+ properties: Port.Properties.create(rpcPort.getPropertiesMap().toObject()),
+ };
+ return port;
+ }
+}
+
+enum EventType {
+ Add,
+ Remove,
+ Quit,
+}
+namespace EventType {
+ export function parse(type: string): EventType {
+ const normalizedType = type.toLowerCase();
+ switch (normalizedType) {
+ case 'add':
+ return EventType.Add;
+ case 'remove':
+ return EventType.Remove;
+ case 'quit':
+ return EventType.Quit;
+ default:
+ throw new Error(
+ `Unexpected 'BoardListWatchResponse' event type: '${type}.'`
+ );
}
- return availablePorts;
}
}
+
+interface DetectedPort {
+ port: Port | undefined;
+ boards: Board[];
+}
diff --git a/arduino-ide-extension/src/node/boards-service-impl.ts b/arduino-ide-extension/src/node/boards-service-impl.ts
index ccad3147d..927a10e46 100644
--- a/arduino-ide-extension/src/node/boards-service-impl.ts
+++ b/arduino-ide-extension/src/node/boards-service-impl.ts
@@ -6,7 +6,6 @@ import {
Installable,
BoardsPackage,
Board,
- Port,
BoardDetails,
Tool,
ConfigOption,
@@ -65,14 +64,6 @@ export class BoardsServiceImpl
return this.boardDiscovery.availablePorts;
}
- async getAttachedBoards(): Promise {
- return this.boardDiscovery.getAttachedBoards();
- }
-
- async getAvailablePorts(): Promise {
- return this.boardDiscovery.getAvailablePorts();
- }
-
async getBoardDetails(options: {
fqbn: string;
}): Promise {
diff --git a/arduino-ide-extension/src/node/core-service-impl.ts b/arduino-ide-extension/src/node/core-service-impl.ts
index c9997cea9..f438ca2e6 100644
--- a/arduino-ide-extension/src/node/core-service-impl.ts
+++ b/arduino-ide-extension/src/node/core-service-impl.ts
@@ -25,7 +25,7 @@ import {
import { ResponseService } from '../common/protocol/response-service';
import { OutputMessage, Port, Status } from '../common/protocol';
import { ArduinoCoreServiceClient } from './cli-protocol/cc/arduino/cli/commands/v1/commands_grpc_pb';
-import { Port as GrpcPort } from './cli-protocol/cc/arduino/cli/commands/v1/port_pb';
+import { Port as RpcPort } from './cli-protocol/cc/arduino/cli/commands/v1/port_pb';
import { ApplicationError, CommandService, Disposable, nls } from '@theia/core';
import { MonitorManager } from './monitor-manager';
import { AutoFlushingBuffer } from './utils/buffers';
@@ -411,15 +411,20 @@ export class CoreServiceImpl extends CoreClientAware implements CoreService {
}
}
- private createPort(port: Port | undefined): GrpcPort {
- const grpcPort = new GrpcPort();
+ private createPort(port: Port | undefined): RpcPort {
+ const rpcPort = new RpcPort();
if (port) {
- grpcPort.setAddress(port.address);
- grpcPort.setLabel(port.addressLabel);
- grpcPort.setProtocol(port.protocol);
- grpcPort.setProtocolLabel(port.protocolLabel);
+ rpcPort.setAddress(port.address);
+ rpcPort.setLabel(port.addressLabel);
+ rpcPort.setProtocol(port.protocol);
+ rpcPort.setProtocolLabel(port.protocolLabel);
+ if (port.properties) {
+ for (const [key, value] of Object.entries(port.properties)) {
+ rpcPort.getPropertiesMap().set(key, value);
+ }
+ }
}
- return grpcPort;
+ return rpcPort;
}
}
type StreamingResponse =
diff --git a/arduino-ide-extension/src/node/monitor-service.ts b/arduino-ide-extension/src/node/monitor-service.ts
index b9d844225..62d126f7f 100644
--- a/arduino-ide-extension/src/node/monitor-service.ts
+++ b/arduino-ide-extension/src/node/monitor-service.ts
@@ -12,7 +12,7 @@ import {
} from './cli-protocol/cc/arduino/cli/commands/v1/monitor_pb';
import { CoreClientAware } from './core-client-provider';
import { WebSocketProvider } from './web-socket/web-socket-provider';
-import { Port as gRPCPort } from 'arduino-ide-extension/src/node/cli-protocol/cc/arduino/cli/commands/v1/port_pb';
+import { Port as RpcPort } from 'arduino-ide-extension/src/node/cli-protocol/cc/arduino/cli/commands/v1/port_pb';
import {
MonitorSettings,
PluggableMonitorSettings,
@@ -193,10 +193,10 @@ export class MonitorService extends CoreClientAware implements Disposable {
monitorRequest.setFqbn(this.board.fqbn);
}
if (this.port?.address && this.port?.protocol) {
- const port = new gRPCPort();
- port.setAddress(this.port.address);
- port.setProtocol(this.port.protocol);
- monitorRequest.setPort(port);
+ const rpcPort = new RpcPort();
+ rpcPort.setAddress(this.port.address);
+ rpcPort.setProtocol(this.port.protocol);
+ monitorRequest.setPort(rpcPort);
}
const config = new MonitorPortConfiguration();
for (const id in this.settings.pluggableMonitorSettings) {
diff --git a/arduino-ide-extension/src/test/browser/fixtures/boards.ts b/arduino-ide-extension/src/test/browser/fixtures/boards.ts
index c00ded48a..d7ddc3126 100644
--- a/arduino-ide-extension/src/test/browser/fixtures/boards.ts
+++ b/arduino-ide-extension/src/test/browser/fixtures/boards.ts
@@ -5,7 +5,6 @@ export const aBoard: Board = {
fqbn: 'some:board:fqbn',
name: 'Some Arduino Board',
port: {
- id: '/lol/port1234|serial',
address: '/lol/port1234',
addressLabel: '/lol/port1234',
protocol: 'serial',
@@ -13,7 +12,6 @@ export const aBoard: Board = {
},
};
export const aPort: Port = {
- id: aBoard.port!.id,
address: aBoard.port!.address,
addressLabel: aBoard.port!.addressLabel,
protocol: aBoard.port!.protocol,
@@ -27,7 +25,6 @@ export const anotherBoard: Board = {
fqbn: 'another:board:fqbn',
name: 'Another Arduino Board',
port: {
- id: '/kek/port5678|serial',
address: '/kek/port5678',
addressLabel: '/kek/port5678',
protocol: 'serial',
@@ -35,7 +32,6 @@ export const anotherBoard: Board = {
},
};
export const anotherPort: Port = {
- id: anotherBoard.port!.id,
address: anotherBoard.port!.address,
addressLabel: anotherBoard.port!.addressLabel,
protocol: anotherBoard.port!.protocol,