Skip to content

Commit

Permalink
fix: show board info based on the selected port
Browse files Browse the repository at this point in the history
include serial number of board if available

Closes #1489
Closes #1435

Signed-off-by: Akos Kitta <[email protected]>
  • Loading branch information
Akos Kitta committed Feb 3, 2023
1 parent 083a706 commit 868fa07
Show file tree
Hide file tree
Showing 5 changed files with 242 additions and 51 deletions.
6 changes: 2 additions & 4 deletions arduino-ide-extension/src/browser/boards/boards-config.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { DisposableCollection } from '@theia/core/lib/common/disposable';
import {
Board,
Port,
BoardConfig as ProtocolBoardConfig,
BoardWithPackage,
} from '../../common/protocol/boards-service';
import { NotificationCenter } from '../notification-center';
Expand All @@ -18,10 +19,7 @@ import { nls } from '@theia/core/lib/common';
import { FrontendApplicationState } from '@theia/core/lib/common/frontend-application-state';

export namespace BoardsConfig {
export interface Config {
selectedBoard?: Board;
selectedPort?: Port;
}
export type Config = ProtocolBoardConfig;

export interface Props {
readonly boardsServiceProvider: BoardsServiceProvider;
Expand Down
63 changes: 20 additions & 43 deletions arduino-ide-extension/src/browser/contributions/board-selection.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import {
InstalledBoardWithPackage,
AvailablePorts,
Port,
getBoardInfo,
} from '../../common/protocol';
import { SketchContribution, Command, CommandRegistry } from './contribution';
import { nls } from '@theia/core/lib/common';
Expand Down Expand Up @@ -49,52 +50,28 @@ export class BoardSelection extends SketchContribution {
override registerCommands(registry: CommandRegistry): void {
registry.registerCommand(BoardSelection.Commands.GET_BOARD_INFO, {
execute: async () => {
const { selectedBoard, selectedPort } =
this.boardsServiceProvider.boardsConfig;
if (!selectedBoard) {
this.messageService.info(
nls.localize(
'arduino/board/selectBoardForInfo',
'Please select a board to obtain board info.'
)
);
return;
}
if (!selectedBoard.fqbn) {
this.messageService.info(
nls.localize(
'arduino/board/platformMissing',
"The platform for the selected '{0}' board is not installed.",
selectedBoard.name
)
);
return;
}
if (!selectedPort) {
this.messageService.info(
nls.localize(
'arduino/board/selectPortForInfo',
'Please select a port to obtain board info.'
)
);
const boardInfo = await getBoardInfo(
this.boardsServiceProvider.boardsConfig.selectedPort,
this.boardsService.getState()
);
if (typeof boardInfo === 'string') {
this.messageService.info(boardInfo);
return;
}
const boardDetails = await this.boardsService.getBoardDetails({
fqbn: selectedBoard.fqbn,
});
if (boardDetails) {
const { VID, PID } = boardDetails;
const detail = `BN: ${selectedBoard.name}
const { BN, VID, PID, SN } = boardInfo;
const detail = `
BN: ${BN}
VID: ${VID}
PID: ${PID}`;
await remote.dialog.showMessageBox(remote.getCurrentWindow(), {
message: nls.localize('arduino/board/boardInfo', 'Board Info'),
title: nls.localize('arduino/board/boardInfo', 'Board Info'),
type: 'info',
detail,
buttons: [nls.localize('vscode/issueMainService/ok', 'OK')],
});
}
PID: ${PID}
SN: ${SN}
`.trim();
await remote.dialog.showMessageBox(remote.getCurrentWindow(), {
message: nls.localize('arduino/board/boardInfo', 'Board Info'),
title: nls.localize('arduino/board/boardInfo', 'Board Info'),
type: 'info',
detail,
buttons: [nls.localize('vscode/issueMainService/ok', 'OK')],
});
},
});
}
Expand Down
105 changes: 105 additions & 0 deletions arduino-ide-extension/src/common/protocol/boards-service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import {
Updatable,
} from '../nls';
import URI from '@theia/core/lib/common/uri';
import { MaybePromise } from '@theia/core/lib/common/types';

export type AvailablePorts = Record<string, [Port, Array<Board>]>;
export namespace AvailablePorts {
Expand Down Expand Up @@ -657,3 +658,107 @@ export function sanitizeFqbn(fqbn: string | undefined): string | undefined {
const [vendor, arch, id] = fqbn.split(':');
return `${vendor}:${arch}:${id}`;
}

export interface BoardConfig {
selectedBoard?: Board;
selectedPort?: Port;
}

export interface BoardInfo {
/**
* Board name. Could be `'Unknown board`'.
*/
BN: string;
/**
* Vendor ID.
*/
VID: string;
/**
* Product ID.
*/
PID: string;
/**
* Serial number.
*/
SN: string;
}

export const selectPortForInfo = nls.localize(
'arduino/board/selectPortForInfo',
'Please select a port to obtain board info.'
);
export const nonSerialPort = nls.localize(
'arduino/board/nonSerialPort',
"Non-serial port, can't obtain info."
);
export const noNativeSerialPort = nls.localize(
'arduino/board/noNativeSerialPort',
"Native serial port, can't obtain info."
);
export const unknownBoard = nls.localize(
'arduino/board/unknownBoard',
'Unknown board'
);

/**
* The returned promise resolves to a `BoardInfo` if available to show in the UI or an info message explaining why showing the board info is not possible.
*/
export async function getBoardInfo(
selectedPort: Port | undefined,
availablePorts: MaybePromise<AvailablePorts>
): Promise<BoardInfo | string> {
if (!selectedPort) {
return selectPortForInfo;
}
// IDE2 must show the board info based on the selected port.
// https://github.com/arduino/arduino-ide/issues/1489
// IDE 1.x supports only serial port protocol
if (selectedPort.protocol !== 'serial') {
return nonSerialPort;
}
const selectedPortKey = Port.keyOf(selectedPort);
const state = await availablePorts;
const boardListOnSelectedPort = Object.entries(state).filter(
([portKey, [port]]) =>
portKey === selectedPortKey && isNonNativeSerial(port)
);

if (!boardListOnSelectedPort.length) {
return noNativeSerialPort;
}

const [, [port, boards]] = boardListOnSelectedPort[0];
if (boardListOnSelectedPort.length > 1 || boards.length > 1) {
console.warn(
`Detected more than one available boards on the selected port : ${JSON.stringify(
selectedPort
)}. Detected boards were: ${JSON.stringify(
boardListOnSelectedPort
)}. Using the first one: ${JSON.stringify([port, boards])}`
);
}

const board = boards[0];
const BN = board?.name ?? unknownBoard;
const VID = readProperty('vid', port);
const PID = readProperty('pid', port);
const SN = readProperty('serialNumber', port);
return { VID, PID, SN, BN };
}

// serial protocol with one or many detected boards or available VID+PID properties from the port
function isNonNativeSerial(port: Port): boolean {
return !!(
port.protocol === 'serial' &&
port.properties?.['vid'] &&
port.properties?.['pid']
);
}

function readProperty(property: string, port: Port): string {
return falsyToNullString(port.properties?.[property]);
}

function falsyToNullString(s: string | undefined): string {
return !!s ? s : '(null)';
}
112 changes: 111 additions & 1 deletion arduino-ide-extension/src/test/common/boards-service.test.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,17 @@
import { Deferred } from '@theia/core/lib/common/promise-util';
import { Mutable } from '@theia/core/lib/common/types';
import { expect } from 'chai';
import { AttachedBoardsChangeEvent } from '../../common/protocol';
import {
AttachedBoardsChangeEvent,
BoardInfo,
getBoardInfo,
noNativeSerialPort,
nonSerialPort,
Port,
selectPortForInfo,
unknownBoard,
} from '../../common/protocol';
import { firstToUpperCase } from '../../common/utils';

describe('boards-service', () => {
describe('AttachedBoardsChangeEvent', () => {
Expand Down Expand Up @@ -80,4 +92,102 @@ describe('boards-service', () => {
);
});
});

describe('getBoardInfo', () => {
const vid = '0x0';
const pid = '0x1';
const serialNumber = '1730323';
const name = 'The Board';
const fqbn = 'alma:korte:szolo';
const selectedBoard = { name, fqbn };
const selectedPort = (
properties: Record<string, string> = {},
protocol = 'serial'
): Mutable<Port> => ({
address: 'address',
addressLabel: 'addressLabel',
protocol,
protocolLabel: firstToUpperCase(protocol),
properties,
});

it('should handle when no port is selected', async () => {
const info = await getBoardInfo(undefined, never());
expect(info).to.be.equal(selectPortForInfo);
});

it("should handle when port protocol is not 'serial'", async () => {
await Promise.allSettled(
['network', 'teensy'].map(async (protocol) => {
const selectedPort: Port = {
address: 'address',
addressLabel: 'addressLabel',
protocolLabel: firstToUpperCase(protocol),
protocol,
};
const info = await getBoardInfo(selectedPort, never());
expect(info).to.be.equal(nonSerialPort);
})
);
});

it("should not detect a port as non-native serial, if protocol is 'serial' but VID or PID is missing", async () => {
const insufficientProperties: Record<string, string>[] = [
{},
{ vid },
{ pid },
{ VID: vid, pid: pid }, // case sensitive
];
for (const properties of insufficientProperties) {
const port = selectedPort(properties);
const info = await getBoardInfo(port, {
[Port.keyOf(port)]: [port, []],
});
expect(info).to.be.equal(noNativeSerialPort);
}
});

it("should detect a port as non-native serial, if protocol is 'serial' and VID/PID are available", async () => {
const port = selectedPort({ vid, pid });
const info = await getBoardInfo(port, {
[Port.keyOf(port)]: [port, []],
});
expect(typeof info).to.be.equal('object');
const boardInfo = <BoardInfo>info;
expect(boardInfo.VID).to.be.equal(vid);
expect(boardInfo.PID).to.be.equal(pid);
expect(boardInfo.SN).to.be.equal('(null)');
expect(boardInfo.BN).to.be.equal(unknownBoard);
});

it("should show the 'SN' even if no matching board was detected for the port", async () => {
const port = selectedPort({ vid, pid, serialNumber });
const info = await getBoardInfo(port, {
[Port.keyOf(port)]: [port, []],
});
expect(typeof info).to.be.equal('object');
const boardInfo = <BoardInfo>info;
expect(boardInfo.VID).to.be.equal(vid);
expect(boardInfo.PID).to.be.equal(pid);
expect(boardInfo.SN).to.be.equal(serialNumber);
expect(boardInfo.BN).to.be.equal(unknownBoard);
});

it("should use the name of the matching board as 'BN' if available", async () => {
const port = selectedPort({ vid, pid });
const info = await getBoardInfo(port, {
[Port.keyOf(port)]: [port, [selectedBoard]],
});
expect(typeof info).to.be.equal('object');
const boardInfo = <BoardInfo>info;
expect(boardInfo.VID).to.be.equal(vid);
expect(boardInfo.PID).to.be.equal(pid);
expect(boardInfo.SN).to.be.equal('(null)');
expect(boardInfo.BN).to.be.equal(selectedBoard.name);
});
});
});

function never<T>(): Promise<T> {
return new Deferred<T>().promise;
}
7 changes: 4 additions & 3 deletions i18n/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,12 @@
"installNow": "The \"{0} {1}\" core has to be installed for the currently selected \"{2}\" board. Do you want to install it now?",
"noBoardsFound": "No boards found for \"{0}\"",
"noFQBN": "The FQBN is not available for the selected board \"{0}\". Do you have the corresponding core installed?",
"noNativeSerialPort": "Native serial port, can't obtain info.",
"noPortsDiscovered": "No ports discovered",
"noPortsSelected": "No ports selected for board: '{0}'.",
"nonSerialPort": "Non-serial port, can't obtain info.",
"noneSelected": "No boards selected.",
"openBoardsConfig": "Select other board and port…",
"platformMissing": "The platform for the selected '{0}' board is not installed.",
"pleasePickBoard": "Please pick a board connected to the port you have selected.",
"port": "Port{0}",
"portLabel": "Port: {0}",
Expand All @@ -31,13 +32,13 @@
"reselectLater": "Reselect later",
"searchBoard": "Search board",
"selectBoard": "Select Board",
"selectBoardForInfo": "Please select a board to obtain board info.",
"selectPortForInfo": "Please select a port to obtain board info.",
"showAllAvailablePorts": "Shows all available ports when enabled",
"showAllPorts": "Show all ports",
"succesfullyInstalledPlatform": "Successfully installed platform {0}:{1}",
"succesfullyUninstalledPlatform": "Successfully uninstalled platform {0}:{1}",
"typeOfPorts": "{0} ports"
"typeOfPorts": "{0} ports",
"unknownBoard": "Unknown board"
},
"boardsManager": "Boards Manager",
"boardsType": {
Expand Down

0 comments on commit 868fa07

Please sign in to comment.