mirror of
https://github.com/arduino/arduino-ide.git
synced 2025-07-14 06:46:36 +00:00
Use port properties from the discovery.
Signed-off-by: Akos Kitta <a.kitta@arduino.cc> Closes #740
This commit is contained in:
parent
b5f9aa0f15
commit
f7f644cf36
@ -354,7 +354,7 @@ export class BoardsConfig extends React.Component<
|
|||||||
<div className="ports list">
|
<div className="ports list">
|
||||||
{ports.map((port) => (
|
{ports.map((port) => (
|
||||||
<Item<Port>
|
<Item<Port>
|
||||||
key={`${port.id}`}
|
key={`${Port.keyOf(port)}`}
|
||||||
item={port}
|
item={port}
|
||||||
label={Port.toString(port)}
|
label={Port.toString(port)}
|
||||||
selected={Port.sameAs(this.state.selectedPort, port)}
|
selected={Port.sameAs(this.state.selectedPort, port)}
|
||||||
|
@ -13,6 +13,7 @@ import {
|
|||||||
AttachedBoardsChangeEvent,
|
AttachedBoardsChangeEvent,
|
||||||
BoardWithPackage,
|
BoardWithPackage,
|
||||||
BoardUserField,
|
BoardUserField,
|
||||||
|
AvailablePorts,
|
||||||
} from '../../common/protocol';
|
} from '../../common/protocol';
|
||||||
import { BoardsConfig } from './boards-config';
|
import { BoardsConfig } from './boards-config';
|
||||||
import { naturalCompare } from '../../common/utils';
|
import { naturalCompare } from '../../common/utils';
|
||||||
@ -21,6 +22,7 @@ import { StorageWrapper } from '../storage-wrapper';
|
|||||||
import { nls } from '@theia/core/lib/common';
|
import { nls } from '@theia/core/lib/common';
|
||||||
import { Deferred } from '@theia/core/lib/common/promise-util';
|
import { Deferred } from '@theia/core/lib/common/promise-util';
|
||||||
import { FrontendApplicationStateService } from '@theia/core/lib/browser/frontend-application-state';
|
import { FrontendApplicationStateService } from '@theia/core/lib/browser/frontend-application-state';
|
||||||
|
import { Unknown } from '../../common/nls';
|
||||||
|
|
||||||
@injectable()
|
@injectable()
|
||||||
export class BoardsServiceProvider implements FrontendApplicationContribution {
|
export class BoardsServiceProvider implements FrontendApplicationContribution {
|
||||||
@ -96,11 +98,12 @@ export class BoardsServiceProvider implements FrontendApplicationContribution {
|
|||||||
);
|
);
|
||||||
|
|
||||||
this.appStateService.reachedState('ready').then(async () => {
|
this.appStateService.reachedState('ready').then(async () => {
|
||||||
const [attachedBoards, availablePorts] = await Promise.all([
|
const [state] = await Promise.all([
|
||||||
this.boardsService.getAttachedBoards(),
|
this.boardsService.getState(),
|
||||||
this.boardsService.getAvailablePorts(),
|
|
||||||
this.loadState(),
|
this.loadState(),
|
||||||
]);
|
]);
|
||||||
|
const { boards: attachedBoards, ports: availablePorts } =
|
||||||
|
AvailablePorts.split(state);
|
||||||
this._attachedBoards = attachedBoards;
|
this._attachedBoards = attachedBoards;
|
||||||
this._availablePorts = availablePorts;
|
this._availablePorts = availablePorts;
|
||||||
this.onAvailablePortsChangedEmitter.fire(this._availablePorts);
|
this.onAvailablePortsChangedEmitter.fire(this._availablePorts);
|
||||||
@ -558,7 +561,7 @@ export class BoardsServiceProvider implements FrontendApplicationContribution {
|
|||||||
};
|
};
|
||||||
} else {
|
} else {
|
||||||
availableBoard = {
|
availableBoard = {
|
||||||
name: nls.localize('arduino/common/unknown', 'Unknown'),
|
name: Unknown,
|
||||||
port: boardPort,
|
port: boardPort,
|
||||||
state: AvailableBoard.State.incomplete,
|
state: AvailableBoard.State.incomplete,
|
||||||
};
|
};
|
||||||
|
@ -331,7 +331,7 @@ PID: ${PID}`;
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const grouped = AvailablePorts.byProtocol(availablePorts);
|
const grouped = AvailablePorts.groupByProtocol(availablePorts);
|
||||||
let protocolOrder = 100;
|
let protocolOrder = 100;
|
||||||
// We first show serial and network ports, then all the rest
|
// We first show serial and network ports, then all the rest
|
||||||
['serial', 'network'].forEach((protocol) => {
|
['serial', 'network'].forEach((protocol) => {
|
||||||
|
@ -145,7 +145,10 @@ export class MonitorManagerProxyClientImpl
|
|||||||
if (
|
if (
|
||||||
selectedBoard?.fqbn !==
|
selectedBoard?.fqbn !==
|
||||||
this.lastConnectedBoard?.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.onMonitorShouldResetEmitter.fire(null);
|
||||||
this.lastConnectedBoard = {
|
this.lastConnectedBoard = {
|
||||||
|
@ -5,6 +5,7 @@ import { isOSX } from '@theia/core/lib/common/os';
|
|||||||
import { DisposableCollection, nls } from '@theia/core/lib/common';
|
import { DisposableCollection, nls } from '@theia/core/lib/common';
|
||||||
import { BoardsServiceProvider } from '../../boards/boards-service-provider';
|
import { BoardsServiceProvider } from '../../boards/boards-service-provider';
|
||||||
import { MonitorModel } from '../../monitor-model';
|
import { MonitorModel } from '../../monitor-model';
|
||||||
|
import { Unknown } from '../../../common/nls';
|
||||||
|
|
||||||
export namespace SerialMonitorSendInput {
|
export namespace SerialMonitorSendInput {
|
||||||
export interface Props {
|
export interface Props {
|
||||||
@ -86,8 +87,8 @@ export class SerialMonitorSendInput extends React.Component<
|
|||||||
? Board.toString(board, {
|
? Board.toString(board, {
|
||||||
useFqbn: false,
|
useFqbn: false,
|
||||||
})
|
})
|
||||||
: 'unknown',
|
: Unknown,
|
||||||
port ? port.address : 'unknown'
|
port ? port.address : Unknown
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -5,6 +5,7 @@ import { Installable } from '../../../common/protocol/installable';
|
|||||||
import { ArduinoComponent } from '../../../common/protocol/arduino-component';
|
import { ArduinoComponent } from '../../../common/protocol/arduino-component';
|
||||||
import { ComponentListItem } from './component-list-item';
|
import { ComponentListItem } from './component-list-item';
|
||||||
import { nls } from '@theia/core/lib/common';
|
import { nls } from '@theia/core/lib/common';
|
||||||
|
import { Unknown } from '../../../common/nls';
|
||||||
|
|
||||||
@injectable()
|
@injectable()
|
||||||
export class ListItemRenderer<T extends ArduinoComponent> {
|
export class ListItemRenderer<T extends ArduinoComponent> {
|
||||||
@ -42,11 +43,7 @@ export class ListItemRenderer<T extends ArduinoComponent> {
|
|||||||
} else if ((item as any).id) {
|
} else if ((item as any).id) {
|
||||||
nameAndAuthor = <span className="name">{(item as any).id}</span>;
|
nameAndAuthor = <span className="name">{(item as any).id}</span>;
|
||||||
} else {
|
} else {
|
||||||
nameAndAuthor = (
|
nameAndAuthor = <span className="name">{Unknown}</span>;
|
||||||
<span className="name">
|
|
||||||
{nls.localize('arduino/common/unknown', 'Unknown')}
|
|
||||||
</span>
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
const onClickUninstall = () => uninstall(item);
|
const onClickUninstall = () => uninstall(item);
|
||||||
const installedVersion = !!item.installedVersion && (
|
const installedVersion = !!item.installedVersion && (
|
||||||
|
3
arduino-ide-extension/src/common/nls.ts
Normal file
3
arduino-ide-extension/src/common/nls.ts
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
import { nls } from '@theia/core/lib/common/nls';
|
||||||
|
|
||||||
|
export const Unknown = nls.localize('arduino/common/unknown', 'Unknown');
|
@ -5,7 +5,7 @@ import { ArduinoComponent } from './arduino-component';
|
|||||||
|
|
||||||
export type AvailablePorts = Record<string, [Port, Array<Board>]>;
|
export type AvailablePorts = Record<string, [Port, Array<Board>]>;
|
||||||
export namespace AvailablePorts {
|
export namespace AvailablePorts {
|
||||||
export function byProtocol(
|
export function groupByProtocol(
|
||||||
availablePorts: AvailablePorts
|
availablePorts: AvailablePorts
|
||||||
): Map<string, AvailablePorts> {
|
): Map<string, AvailablePorts> {
|
||||||
const grouped = new Map<string, AvailablePorts>();
|
const grouped = new Map<string, AvailablePorts>();
|
||||||
@ -20,6 +20,21 @@ export namespace AvailablePorts {
|
|||||||
}
|
}
|
||||||
return grouped;
|
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 {
|
export interface AttachedBoardsChangeEvent {
|
||||||
@ -117,16 +132,6 @@ export const BoardsService = Symbol('BoardsService');
|
|||||||
export interface BoardsService
|
export interface BoardsService
|
||||||
extends Installable<BoardsPackage>,
|
extends Installable<BoardsPackage>,
|
||||||
Searchable<BoardsPackage> {
|
Searchable<BoardsPackage> {
|
||||||
/**
|
|
||||||
* Deprecated. `getState` should be used to correctly map a board with a port.
|
|
||||||
* @deprecated
|
|
||||||
*/
|
|
||||||
getAttachedBoards(): Promise<Board[]>;
|
|
||||||
/**
|
|
||||||
* Deprecated. `getState` should be used to correctly map a board with a port.
|
|
||||||
* @deprecated
|
|
||||||
*/
|
|
||||||
getAvailablePorts(): Promise<Port[]>;
|
|
||||||
getState(): Promise<AvailablePorts>;
|
getState(): Promise<AvailablePorts>;
|
||||||
getBoardDetails(options: { fqbn: string }): Promise<BoardDetails | undefined>;
|
getBoardDetails(options: { fqbn: string }): Promise<BoardDetails | undefined>;
|
||||||
getBoardPackage(options: { id: string }): Promise<BoardsPackage | undefined>;
|
getBoardPackage(options: { id: string }): Promise<BoardsPackage | undefined>;
|
||||||
@ -141,28 +146,55 @@ export interface BoardsService
|
|||||||
}
|
}
|
||||||
|
|
||||||
export interface Port {
|
export interface Port {
|
||||||
// id is the combination of address and protocol
|
|
||||||
// formatted like "<address>|<protocol>" used
|
|
||||||
// to uniquely recognize a port
|
|
||||||
readonly id: string;
|
|
||||||
readonly address: string;
|
readonly address: string;
|
||||||
readonly addressLabel: string;
|
readonly addressLabel: string;
|
||||||
readonly protocol: string;
|
readonly protocol: string;
|
||||||
readonly protocolLabel: string;
|
readonly protocolLabel: string;
|
||||||
|
readonly properties?: Record<string, string>;
|
||||||
}
|
}
|
||||||
export namespace Port {
|
export namespace Port {
|
||||||
export function is(arg: any): arg is Port {
|
export type Properties = Record<string, string>;
|
||||||
return (
|
export namespace Properties {
|
||||||
!!arg &&
|
export function create(
|
||||||
'address' in arg &&
|
properties: [string, string][] | undefined
|
||||||
typeof arg['address'] === 'string' &&
|
): Properties {
|
||||||
'protocol' in arg &&
|
if (!properties) {
|
||||||
typeof arg['protocol'] === 'string'
|
return {};
|
||||||
);
|
}
|
||||||
|
return properties.reduce((acc, curr) => {
|
||||||
|
const [key, value] = curr;
|
||||||
|
acc[key] = value;
|
||||||
|
return acc;
|
||||||
|
}, {} as Record<string, string>);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
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;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function toString(port: Port): string {
|
/**
|
||||||
return `${port.addressLabel} ${port.protocolLabel}`;
|
* 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({ addressLabel, protocolLabel }: Port): string {
|
||||||
|
return `${addressLabel} ${protocolLabel}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function compare(left: Port, right: Port): number {
|
export function compare(left: Port, right: Port): number {
|
||||||
|
@ -3,6 +3,7 @@ import { sha256 } from 'hash.js';
|
|||||||
import { randomBytes } from 'crypto';
|
import { randomBytes } from 'crypto';
|
||||||
import btoa = require('btoa'); // TODO: check why we cannot
|
import btoa = require('btoa'); // TODO: check why we cannot
|
||||||
import { AuthenticationSession } from './types';
|
import { AuthenticationSession } from './types';
|
||||||
|
import { Unknown } from '../../common/nls';
|
||||||
|
|
||||||
export interface IToken {
|
export interface IToken {
|
||||||
accessToken: string; // When unable to refresh due to network problems, the access token becomes undefined
|
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,
|
sessionId: parsedIdToken.sub,
|
||||||
scope: token.scope,
|
scope: token.scope,
|
||||||
account: {
|
account: {
|
||||||
id: parsedIdToken.sub || 'unknown',
|
id: parsedIdToken.sub || Unknown,
|
||||||
email: parsedIdToken.email || 'unknown',
|
email: parsedIdToken.email || Unknown,
|
||||||
nickname: parsedIdToken.nickname || 'unknown',
|
nickname: parsedIdToken.nickname || Unknown,
|
||||||
picture: parsedIdToken.picture || 'unknown',
|
picture: parsedIdToken.picture || Unknown,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -1,27 +1,30 @@
|
|||||||
import { injectable, inject, named } from '@theia/core/shared/inversify';
|
|
||||||
import { ClientDuplexStream } from '@grpc/grpc-js';
|
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 { ILogger } from '@theia/core/lib/common/logger';
|
||||||
import { deepClone } from '@theia/core/lib/common/objects';
|
import { deepClone } from '@theia/core/lib/common/objects';
|
||||||
import { CoreClientAware } from './core-client-provider';
|
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,
|
||||||
|
NotificationServiceServer,
|
||||||
|
Port,
|
||||||
|
} from '../common/protocol';
|
||||||
import {
|
import {
|
||||||
BoardListWatchRequest,
|
BoardListWatchRequest,
|
||||||
BoardListWatchResponse,
|
BoardListWatchResponse,
|
||||||
|
DetectedPort as RpcDetectedPort,
|
||||||
} from './cli-protocol/cc/arduino/cli/commands/v1/board_pb';
|
} from './cli-protocol/cc/arduino/cli/commands/v1/board_pb';
|
||||||
import {
|
|
||||||
Board,
|
|
||||||
Port,
|
|
||||||
NotificationServiceServer,
|
|
||||||
AvailablePorts,
|
|
||||||
AttachedBoardsChangeEvent,
|
|
||||||
} 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 { ArduinoCoreServiceClient } from './cli-protocol/cc/arduino/cli/commands/v1/commands_grpc_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 { ServiceError } from './service-error';
|
||||||
import { BackendApplicationContribution } from '@theia/core/lib/node';
|
|
||||||
import { Deferred } from '@theia/core/lib/common/promise-util';
|
|
||||||
|
|
||||||
type Duplex = ClientDuplexStream<BoardListWatchRequest, BoardListWatchResponse>;
|
type Duplex = ClientDuplexStream<BoardListWatchRequest, BoardListWatchResponse>;
|
||||||
interface StreamWrapper extends Disposable {
|
interface StreamWrapper extends Disposable {
|
||||||
@ -125,8 +128,8 @@ export class BoardDiscovery
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
setUploadInProgress(uploadAttemptInProgress: boolean): void {
|
setUploadInProgress(uploadInProgress: boolean): void {
|
||||||
this.uploadInProgress = uploadAttemptInProgress;
|
this.uploadInProgress = uploadInProgress;
|
||||||
}
|
}
|
||||||
|
|
||||||
private createTimeout(
|
private createTimeout(
|
||||||
@ -216,7 +219,7 @@ export class BoardDiscovery
|
|||||||
} else {
|
} else {
|
||||||
throw new Error(`Unhandled object type: ${arg}`);
|
throw new Error(`Unhandled object type: ${arg}`);
|
||||||
}
|
}
|
||||||
return JSON.stringify(object);
|
return JSON.stringify(object, null, 2); // TODO: remove `space`?
|
||||||
}
|
}
|
||||||
|
|
||||||
async start(): Promise<void> {
|
async start(): Promise<void> {
|
||||||
@ -234,103 +237,7 @@ export class BoardDiscovery
|
|||||||
this.logger.info('start new deferred');
|
this.logger.info('start new deferred');
|
||||||
const { client, instance } = await this.coreClient;
|
const { client, instance } = await this.coreClient;
|
||||||
const wrapper = await this.createWrapper(client);
|
const wrapper = await this.createWrapper(client);
|
||||||
wrapper.stream.on('data', async (resp: BoardListWatchResponse) => {
|
wrapper.stream.on('data', (resp) => this.onBoardListWatchResponse(resp));
|
||||||
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);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
this.logger.info('start request start watch');
|
this.logger.info('start request start watch');
|
||||||
await this.requestStartWatch(
|
await this.requestStartWatch(
|
||||||
new BoardListWatchRequest().setInstance(instance),
|
new BoardListWatchRequest().setInstance(instance),
|
||||||
@ -341,21 +248,124 @@ export class BoardDiscovery
|
|||||||
this.logger.info('start resolved watching');
|
this.logger.info('start resolved watching');
|
||||||
}
|
}
|
||||||
|
|
||||||
getAttachedBoards(state: AvailablePorts = this.availablePorts): Board[] {
|
// XXX: make this `protected` and override for tests if IDE2 wants to mock events from the CLI.
|
||||||
const attachedBoards: Board[] = [];
|
private onBoardListWatchResponse(resp: BoardListWatchResponse): void {
|
||||||
for (const portID of Object.keys(state)) {
|
this.logger.info(this.toJson(resp));
|
||||||
const [, boards] = state[portID];
|
const eventType = EventType.parse(resp.getEventType());
|
||||||
attachedBoards.push(...boards);
|
|
||||||
|
if (eventType === EventType.Quit) {
|
||||||
|
this.logger.info('quit received');
|
||||||
|
this.stop();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
}
|
}
|
||||||
return attachedBoards;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
getAvailablePorts(state: AvailablePorts = this.availablePorts): Port[] {
|
private fromRpc(detectedPort: RpcDetectedPort): DetectedPort {
|
||||||
const availablePorts: Port[] = [];
|
const rpcPort = detectedPort.getPort();
|
||||||
for (const portID of Object.keys(state)) {
|
const port = rpcPort && this.fromRpcPort(rpcPort);
|
||||||
const [port] = state[portID];
|
const boards = detectedPort.getMatchingBoardsList().map(
|
||||||
availablePorts.push(port);
|
(board) =>
|
||||||
}
|
({
|
||||||
return availablePorts;
|
fqbn: board.getFqbn(),
|
||||||
|
name: board.getName() || Unknown,
|
||||||
|
port,
|
||||||
|
} as Board)
|
||||||
|
);
|
||||||
|
return {
|
||||||
|
boards,
|
||||||
|
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}.'`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
interface DetectedPort {
|
||||||
|
port: Port | undefined;
|
||||||
|
boards: Board[];
|
||||||
|
}
|
||||||
|
@ -6,7 +6,6 @@ import {
|
|||||||
Installable,
|
Installable,
|
||||||
BoardsPackage,
|
BoardsPackage,
|
||||||
Board,
|
Board,
|
||||||
Port,
|
|
||||||
BoardDetails,
|
BoardDetails,
|
||||||
Tool,
|
Tool,
|
||||||
ConfigOption,
|
ConfigOption,
|
||||||
@ -65,14 +64,6 @@ export class BoardsServiceImpl
|
|||||||
return this.boardDiscovery.availablePorts;
|
return this.boardDiscovery.availablePorts;
|
||||||
}
|
}
|
||||||
|
|
||||||
async getAttachedBoards(): Promise<Board[]> {
|
|
||||||
return this.boardDiscovery.getAttachedBoards();
|
|
||||||
}
|
|
||||||
|
|
||||||
async getAvailablePorts(): Promise<Port[]> {
|
|
||||||
return this.boardDiscovery.getAvailablePorts();
|
|
||||||
}
|
|
||||||
|
|
||||||
async getBoardDetails(options: {
|
async getBoardDetails(options: {
|
||||||
fqbn: string;
|
fqbn: string;
|
||||||
}): Promise<BoardDetails | undefined> {
|
}): Promise<BoardDetails | undefined> {
|
||||||
|
@ -25,7 +25,7 @@ import {
|
|||||||
import { ResponseService } from '../common/protocol/response-service';
|
import { ResponseService } from '../common/protocol/response-service';
|
||||||
import { OutputMessage, Port, Status } from '../common/protocol';
|
import { OutputMessage, Port, Status } from '../common/protocol';
|
||||||
import { ArduinoCoreServiceClient } from './cli-protocol/cc/arduino/cli/commands/v1/commands_grpc_pb';
|
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 { ApplicationError, CommandService, Disposable, nls } from '@theia/core';
|
||||||
import { MonitorManager } from './monitor-manager';
|
import { MonitorManager } from './monitor-manager';
|
||||||
import { AutoFlushingBuffer } from './utils/buffers';
|
import { AutoFlushingBuffer } from './utils/buffers';
|
||||||
@ -411,15 +411,20 @@ export class CoreServiceImpl extends CoreClientAware implements CoreService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private createPort(port: Port | undefined): GrpcPort {
|
private createPort(port: Port | undefined): RpcPort {
|
||||||
const grpcPort = new GrpcPort();
|
const rpcPort = new RpcPort();
|
||||||
if (port) {
|
if (port) {
|
||||||
grpcPort.setAddress(port.address);
|
rpcPort.setAddress(port.address);
|
||||||
grpcPort.setLabel(port.addressLabel);
|
rpcPort.setLabel(port.addressLabel);
|
||||||
grpcPort.setProtocol(port.protocol);
|
rpcPort.setProtocol(port.protocol);
|
||||||
grpcPort.setProtocolLabel(port.protocolLabel);
|
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 =
|
type StreamingResponse =
|
||||||
|
@ -12,7 +12,7 @@ import {
|
|||||||
} from './cli-protocol/cc/arduino/cli/commands/v1/monitor_pb';
|
} from './cli-protocol/cc/arduino/cli/commands/v1/monitor_pb';
|
||||||
import { CoreClientAware } from './core-client-provider';
|
import { CoreClientAware } from './core-client-provider';
|
||||||
import { WebSocketProvider } from './web-socket/web-socket-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 {
|
import {
|
||||||
MonitorSettings,
|
MonitorSettings,
|
||||||
PluggableMonitorSettings,
|
PluggableMonitorSettings,
|
||||||
@ -193,10 +193,10 @@ export class MonitorService extends CoreClientAware implements Disposable {
|
|||||||
monitorRequest.setFqbn(this.board.fqbn);
|
monitorRequest.setFqbn(this.board.fqbn);
|
||||||
}
|
}
|
||||||
if (this.port?.address && this.port?.protocol) {
|
if (this.port?.address && this.port?.protocol) {
|
||||||
const port = new gRPCPort();
|
const rpcPort = new RpcPort();
|
||||||
port.setAddress(this.port.address);
|
rpcPort.setAddress(this.port.address);
|
||||||
port.setProtocol(this.port.protocol);
|
rpcPort.setProtocol(this.port.protocol);
|
||||||
monitorRequest.setPort(port);
|
monitorRequest.setPort(rpcPort);
|
||||||
}
|
}
|
||||||
const config = new MonitorPortConfiguration();
|
const config = new MonitorPortConfiguration();
|
||||||
for (const id in this.settings.pluggableMonitorSettings) {
|
for (const id in this.settings.pluggableMonitorSettings) {
|
||||||
|
@ -5,7 +5,6 @@ export const aBoard: Board = {
|
|||||||
fqbn: 'some:board:fqbn',
|
fqbn: 'some:board:fqbn',
|
||||||
name: 'Some Arduino Board',
|
name: 'Some Arduino Board',
|
||||||
port: {
|
port: {
|
||||||
id: '/lol/port1234|serial',
|
|
||||||
address: '/lol/port1234',
|
address: '/lol/port1234',
|
||||||
addressLabel: '/lol/port1234',
|
addressLabel: '/lol/port1234',
|
||||||
protocol: 'serial',
|
protocol: 'serial',
|
||||||
@ -13,7 +12,6 @@ export const aBoard: Board = {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
export const aPort: Port = {
|
export const aPort: Port = {
|
||||||
id: aBoard.port!.id,
|
|
||||||
address: aBoard.port!.address,
|
address: aBoard.port!.address,
|
||||||
addressLabel: aBoard.port!.addressLabel,
|
addressLabel: aBoard.port!.addressLabel,
|
||||||
protocol: aBoard.port!.protocol,
|
protocol: aBoard.port!.protocol,
|
||||||
@ -27,7 +25,6 @@ export const anotherBoard: Board = {
|
|||||||
fqbn: 'another:board:fqbn',
|
fqbn: 'another:board:fqbn',
|
||||||
name: 'Another Arduino Board',
|
name: 'Another Arduino Board',
|
||||||
port: {
|
port: {
|
||||||
id: '/kek/port5678|serial',
|
|
||||||
address: '/kek/port5678',
|
address: '/kek/port5678',
|
||||||
addressLabel: '/kek/port5678',
|
addressLabel: '/kek/port5678',
|
||||||
protocol: 'serial',
|
protocol: 'serial',
|
||||||
@ -35,7 +32,6 @@ export const anotherBoard: Board = {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
export const anotherPort: Port = {
|
export const anotherPort: Port = {
|
||||||
id: anotherBoard.port!.id,
|
|
||||||
address: anotherBoard.port!.address,
|
address: anotherBoard.port!.address,
|
||||||
addressLabel: anotherBoard.port!.addressLabel,
|
addressLabel: anotherBoard.port!.addressLabel,
|
||||||
protocol: anotherBoard.port!.protocol,
|
protocol: anotherBoard.port!.protocol,
|
||||||
|
Loading…
x
Reference in New Issue
Block a user