Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

#740, #1331 Use port properties #1335

Merged
merged 3 commits into from
Aug 25, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion arduino-ide-extension/src/browser/boards/boards-config.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -354,7 +354,7 @@ export class BoardsConfig extends React.Component<
<div className="ports list">
{ports.map((port) => (
<Item<Port>
key={`${port.id}`}
key={`${Port.keyOf(port)}`}
item={port}
label={Port.toString(port)}
selected={Port.sameAs(this.state.selectedPort, port)}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import {
AttachedBoardsChangeEvent,
BoardWithPackage,
BoardUserField,
AvailablePorts,
} from '../../common/protocol';
import { BoardsConfig } from './boards-config';
import { naturalCompare } from '../../common/utils';
Expand All @@ -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 {
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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,
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,7 @@ export class BoardsDropDown extends React.Component<BoardsDropDown.Props> {
{boardLabel}
</div>
<div className="arduino-boards-dropdown-item--port-label noWrapInfo noselect">
{port.address}
{port.addressLabel}
</div>
</div>
{selected ? <div className="fa fa-check" /> : ''}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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})`;
Expand Down Expand Up @@ -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) => {
Expand Down
30 changes: 26 additions & 4 deletions arduino-ide-extension/src/browser/contributions/upload-sketch.ts
Original file line number Diff line number Diff line change
@@ -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 {
Expand All @@ -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';

Expand Down Expand Up @@ -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,
Expand All @@ -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()) ?? [];
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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 = {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -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
);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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<T extends ArduinoComponent> {
Expand Down Expand Up @@ -42,11 +43,7 @@ export class ListItemRenderer<T extends ArduinoComponent> {
} else if ((item as any).id) {
nameAndAuthor = <span className="name">{(item as any).id}</span>;
} else {
nameAndAuthor = (
<span className="name">
{nls.localize('arduino/common/unknown', 'Unknown')}
</span>
);
nameAndAuthor = <span className="name">{Unknown}</span>;
}
const onClickUninstall = () => uninstall(item);
const installedVersion = !!item.installedVersion && (
Expand Down
3 changes: 3 additions & 0 deletions arduino-ide-extension/src/common/nls.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import { nls } from '@theia/core/lib/common/nls';

export const Unknown = nls.localize('arduino/common/unknown', 'Unknown');
82 changes: 57 additions & 25 deletions arduino-ide-extension/src/common/protocol/boards-service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { ArduinoComponent } from './arduino-component';

export type AvailablePorts = Record<string, [Port, Array<Board>]>;
export namespace AvailablePorts {
export function byProtocol(
export function groupByProtocol(
availablePorts: AvailablePorts
): Map<string, AvailablePorts> {
const grouped = new Map<string, AvailablePorts>();
Expand All @@ -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 {
Expand Down Expand Up @@ -117,16 +132,6 @@ export const BoardsService = Symbol('BoardsService');
export interface BoardsService
extends Installable<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>;
getBoardDetails(options: { fqbn: string }): Promise<BoardDetails | undefined>;
getBoardPackage(options: { id: string }): Promise<BoardsPackage | undefined>;
Expand All @@ -141,28 +146,55 @@ export interface BoardsService
}

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 addressLabel: string;
readonly protocol: string;
readonly protocolLabel: string;
readonly properties?: Record<string, string>;
}
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<string, string>;
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<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;
}

/**
* 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 {
Expand Down
9 changes: 5 additions & 4 deletions arduino-ide-extension/src/node/auth/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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,
},
};
}
Expand Down
Loading