Skip to content

Commit 868fa07

Browse files
author
Akos Kitta
committed
fix: show board info based on the selected port
include serial number of board if available Closes #1489 Closes #1435 Signed-off-by: Akos Kitta <[email protected]>
1 parent 083a706 commit 868fa07

File tree

5 files changed

+242
-51
lines changed

5 files changed

+242
-51
lines changed

Diff for: arduino-ide-extension/src/browser/boards/boards-config.tsx

+2-4
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import { DisposableCollection } from '@theia/core/lib/common/disposable';
66
import {
77
Board,
88
Port,
9+
BoardConfig as ProtocolBoardConfig,
910
BoardWithPackage,
1011
} from '../../common/protocol/boards-service';
1112
import { NotificationCenter } from '../notification-center';
@@ -18,10 +19,7 @@ import { nls } from '@theia/core/lib/common';
1819
import { FrontendApplicationState } from '@theia/core/lib/common/frontend-application-state';
1920

2021
export namespace BoardsConfig {
21-
export interface Config {
22-
selectedBoard?: Board;
23-
selectedPort?: Port;
24-
}
22+
export type Config = ProtocolBoardConfig;
2523

2624
export interface Props {
2725
readonly boardsServiceProvider: BoardsServiceProvider;

Diff for: arduino-ide-extension/src/browser/contributions/board-selection.ts

+20-43
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import {
2020
InstalledBoardWithPackage,
2121
AvailablePorts,
2222
Port,
23+
getBoardInfo,
2324
} from '../../common/protocol';
2425
import { SketchContribution, Command, CommandRegistry } from './contribution';
2526
import { nls } from '@theia/core/lib/common';
@@ -49,52 +50,28 @@ export class BoardSelection extends SketchContribution {
4950
override registerCommands(registry: CommandRegistry): void {
5051
registry.registerCommand(BoardSelection.Commands.GET_BOARD_INFO, {
5152
execute: async () => {
52-
const { selectedBoard, selectedPort } =
53-
this.boardsServiceProvider.boardsConfig;
54-
if (!selectedBoard) {
55-
this.messageService.info(
56-
nls.localize(
57-
'arduino/board/selectBoardForInfo',
58-
'Please select a board to obtain board info.'
59-
)
60-
);
61-
return;
62-
}
63-
if (!selectedBoard.fqbn) {
64-
this.messageService.info(
65-
nls.localize(
66-
'arduino/board/platformMissing',
67-
"The platform for the selected '{0}' board is not installed.",
68-
selectedBoard.name
69-
)
70-
);
71-
return;
72-
}
73-
if (!selectedPort) {
74-
this.messageService.info(
75-
nls.localize(
76-
'arduino/board/selectPortForInfo',
77-
'Please select a port to obtain board info.'
78-
)
79-
);
53+
const boardInfo = await getBoardInfo(
54+
this.boardsServiceProvider.boardsConfig.selectedPort,
55+
this.boardsService.getState()
56+
);
57+
if (typeof boardInfo === 'string') {
58+
this.messageService.info(boardInfo);
8059
return;
8160
}
82-
const boardDetails = await this.boardsService.getBoardDetails({
83-
fqbn: selectedBoard.fqbn,
84-
});
85-
if (boardDetails) {
86-
const { VID, PID } = boardDetails;
87-
const detail = `BN: ${selectedBoard.name}
61+
const { BN, VID, PID, SN } = boardInfo;
62+
const detail = `
63+
BN: ${BN}
8864
VID: ${VID}
89-
PID: ${PID}`;
90-
await remote.dialog.showMessageBox(remote.getCurrentWindow(), {
91-
message: nls.localize('arduino/board/boardInfo', 'Board Info'),
92-
title: nls.localize('arduino/board/boardInfo', 'Board Info'),
93-
type: 'info',
94-
detail,
95-
buttons: [nls.localize('vscode/issueMainService/ok', 'OK')],
96-
});
97-
}
65+
PID: ${PID}
66+
SN: ${SN}
67+
`.trim();
68+
await remote.dialog.showMessageBox(remote.getCurrentWindow(), {
69+
message: nls.localize('arduino/board/boardInfo', 'Board Info'),
70+
title: nls.localize('arduino/board/boardInfo', 'Board Info'),
71+
type: 'info',
72+
detail,
73+
buttons: [nls.localize('vscode/issueMainService/ok', 'OK')],
74+
});
9875
},
9976
});
10077
}

Diff for: arduino-ide-extension/src/common/protocol/boards-service.ts

+105
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import {
1111
Updatable,
1212
} from '../nls';
1313
import URI from '@theia/core/lib/common/uri';
14+
import { MaybePromise } from '@theia/core/lib/common/types';
1415

1516
export type AvailablePorts = Record<string, [Port, Array<Board>]>;
1617
export namespace AvailablePorts {
@@ -657,3 +658,107 @@ export function sanitizeFqbn(fqbn: string | undefined): string | undefined {
657658
const [vendor, arch, id] = fqbn.split(':');
658659
return `${vendor}:${arch}:${id}`;
659660
}
661+
662+
export interface BoardConfig {
663+
selectedBoard?: Board;
664+
selectedPort?: Port;
665+
}
666+
667+
export interface BoardInfo {
668+
/**
669+
* Board name. Could be `'Unknown board`'.
670+
*/
671+
BN: string;
672+
/**
673+
* Vendor ID.
674+
*/
675+
VID: string;
676+
/**
677+
* Product ID.
678+
*/
679+
PID: string;
680+
/**
681+
* Serial number.
682+
*/
683+
SN: string;
684+
}
685+
686+
export const selectPortForInfo = nls.localize(
687+
'arduino/board/selectPortForInfo',
688+
'Please select a port to obtain board info.'
689+
);
690+
export const nonSerialPort = nls.localize(
691+
'arduino/board/nonSerialPort',
692+
"Non-serial port, can't obtain info."
693+
);
694+
export const noNativeSerialPort = nls.localize(
695+
'arduino/board/noNativeSerialPort',
696+
"Native serial port, can't obtain info."
697+
);
698+
export const unknownBoard = nls.localize(
699+
'arduino/board/unknownBoard',
700+
'Unknown board'
701+
);
702+
703+
/**
704+
* 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.
705+
*/
706+
export async function getBoardInfo(
707+
selectedPort: Port | undefined,
708+
availablePorts: MaybePromise<AvailablePorts>
709+
): Promise<BoardInfo | string> {
710+
if (!selectedPort) {
711+
return selectPortForInfo;
712+
}
713+
// IDE2 must show the board info based on the selected port.
714+
// https://github.com/arduino/arduino-ide/issues/1489
715+
// IDE 1.x supports only serial port protocol
716+
if (selectedPort.protocol !== 'serial') {
717+
return nonSerialPort;
718+
}
719+
const selectedPortKey = Port.keyOf(selectedPort);
720+
const state = await availablePorts;
721+
const boardListOnSelectedPort = Object.entries(state).filter(
722+
([portKey, [port]]) =>
723+
portKey === selectedPortKey && isNonNativeSerial(port)
724+
);
725+
726+
if (!boardListOnSelectedPort.length) {
727+
return noNativeSerialPort;
728+
}
729+
730+
const [, [port, boards]] = boardListOnSelectedPort[0];
731+
if (boardListOnSelectedPort.length > 1 || boards.length > 1) {
732+
console.warn(
733+
`Detected more than one available boards on the selected port : ${JSON.stringify(
734+
selectedPort
735+
)}. Detected boards were: ${JSON.stringify(
736+
boardListOnSelectedPort
737+
)}. Using the first one: ${JSON.stringify([port, boards])}`
738+
);
739+
}
740+
741+
const board = boards[0];
742+
const BN = board?.name ?? unknownBoard;
743+
const VID = readProperty('vid', port);
744+
const PID = readProperty('pid', port);
745+
const SN = readProperty('serialNumber', port);
746+
return { VID, PID, SN, BN };
747+
}
748+
749+
// serial protocol with one or many detected boards or available VID+PID properties from the port
750+
function isNonNativeSerial(port: Port): boolean {
751+
return !!(
752+
port.protocol === 'serial' &&
753+
port.properties?.['vid'] &&
754+
port.properties?.['pid']
755+
);
756+
}
757+
758+
function readProperty(property: string, port: Port): string {
759+
return falsyToNullString(port.properties?.[property]);
760+
}
761+
762+
function falsyToNullString(s: string | undefined): string {
763+
return !!s ? s : '(null)';
764+
}

Diff for: arduino-ide-extension/src/test/common/boards-service.test.ts

+111-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,17 @@
1+
import { Deferred } from '@theia/core/lib/common/promise-util';
2+
import { Mutable } from '@theia/core/lib/common/types';
13
import { expect } from 'chai';
2-
import { AttachedBoardsChangeEvent } from '../../common/protocol';
4+
import {
5+
AttachedBoardsChangeEvent,
6+
BoardInfo,
7+
getBoardInfo,
8+
noNativeSerialPort,
9+
nonSerialPort,
10+
Port,
11+
selectPortForInfo,
12+
unknownBoard,
13+
} from '../../common/protocol';
14+
import { firstToUpperCase } from '../../common/utils';
315

416
describe('boards-service', () => {
517
describe('AttachedBoardsChangeEvent', () => {
@@ -80,4 +92,102 @@ describe('boards-service', () => {
8092
);
8193
});
8294
});
95+
96+
describe('getBoardInfo', () => {
97+
const vid = '0x0';
98+
const pid = '0x1';
99+
const serialNumber = '1730323';
100+
const name = 'The Board';
101+
const fqbn = 'alma:korte:szolo';
102+
const selectedBoard = { name, fqbn };
103+
const selectedPort = (
104+
properties: Record<string, string> = {},
105+
protocol = 'serial'
106+
): Mutable<Port> => ({
107+
address: 'address',
108+
addressLabel: 'addressLabel',
109+
protocol,
110+
protocolLabel: firstToUpperCase(protocol),
111+
properties,
112+
});
113+
114+
it('should handle when no port is selected', async () => {
115+
const info = await getBoardInfo(undefined, never());
116+
expect(info).to.be.equal(selectPortForInfo);
117+
});
118+
119+
it("should handle when port protocol is not 'serial'", async () => {
120+
await Promise.allSettled(
121+
['network', 'teensy'].map(async (protocol) => {
122+
const selectedPort: Port = {
123+
address: 'address',
124+
addressLabel: 'addressLabel',
125+
protocolLabel: firstToUpperCase(protocol),
126+
protocol,
127+
};
128+
const info = await getBoardInfo(selectedPort, never());
129+
expect(info).to.be.equal(nonSerialPort);
130+
})
131+
);
132+
});
133+
134+
it("should not detect a port as non-native serial, if protocol is 'serial' but VID or PID is missing", async () => {
135+
const insufficientProperties: Record<string, string>[] = [
136+
{},
137+
{ vid },
138+
{ pid },
139+
{ VID: vid, pid: pid }, // case sensitive
140+
];
141+
for (const properties of insufficientProperties) {
142+
const port = selectedPort(properties);
143+
const info = await getBoardInfo(port, {
144+
[Port.keyOf(port)]: [port, []],
145+
});
146+
expect(info).to.be.equal(noNativeSerialPort);
147+
}
148+
});
149+
150+
it("should detect a port as non-native serial, if protocol is 'serial' and VID/PID are available", async () => {
151+
const port = selectedPort({ vid, pid });
152+
const info = await getBoardInfo(port, {
153+
[Port.keyOf(port)]: [port, []],
154+
});
155+
expect(typeof info).to.be.equal('object');
156+
const boardInfo = <BoardInfo>info;
157+
expect(boardInfo.VID).to.be.equal(vid);
158+
expect(boardInfo.PID).to.be.equal(pid);
159+
expect(boardInfo.SN).to.be.equal('(null)');
160+
expect(boardInfo.BN).to.be.equal(unknownBoard);
161+
});
162+
163+
it("should show the 'SN' even if no matching board was detected for the port", async () => {
164+
const port = selectedPort({ vid, pid, serialNumber });
165+
const info = await getBoardInfo(port, {
166+
[Port.keyOf(port)]: [port, []],
167+
});
168+
expect(typeof info).to.be.equal('object');
169+
const boardInfo = <BoardInfo>info;
170+
expect(boardInfo.VID).to.be.equal(vid);
171+
expect(boardInfo.PID).to.be.equal(pid);
172+
expect(boardInfo.SN).to.be.equal(serialNumber);
173+
expect(boardInfo.BN).to.be.equal(unknownBoard);
174+
});
175+
176+
it("should use the name of the matching board as 'BN' if available", async () => {
177+
const port = selectedPort({ vid, pid });
178+
const info = await getBoardInfo(port, {
179+
[Port.keyOf(port)]: [port, [selectedBoard]],
180+
});
181+
expect(typeof info).to.be.equal('object');
182+
const boardInfo = <BoardInfo>info;
183+
expect(boardInfo.VID).to.be.equal(vid);
184+
expect(boardInfo.PID).to.be.equal(pid);
185+
expect(boardInfo.SN).to.be.equal('(null)');
186+
expect(boardInfo.BN).to.be.equal(selectedBoard.name);
187+
});
188+
});
83189
});
190+
191+
function never<T>(): Promise<T> {
192+
return new Deferred<T>().promise;
193+
}

Diff for: i18n/en.json

+4-3
Original file line numberDiff line numberDiff line change
@@ -18,11 +18,12 @@
1818
"installNow": "The \"{0} {1}\" core has to be installed for the currently selected \"{2}\" board. Do you want to install it now?",
1919
"noBoardsFound": "No boards found for \"{0}\"",
2020
"noFQBN": "The FQBN is not available for the selected board \"{0}\". Do you have the corresponding core installed?",
21+
"noNativeSerialPort": "Native serial port, can't obtain info.",
2122
"noPortsDiscovered": "No ports discovered",
2223
"noPortsSelected": "No ports selected for board: '{0}'.",
24+
"nonSerialPort": "Non-serial port, can't obtain info.",
2325
"noneSelected": "No boards selected.",
2426
"openBoardsConfig": "Select other board and port…",
25-
"platformMissing": "The platform for the selected '{0}' board is not installed.",
2627
"pleasePickBoard": "Please pick a board connected to the port you have selected.",
2728
"port": "Port{0}",
2829
"portLabel": "Port: {0}",
@@ -31,13 +32,13 @@
3132
"reselectLater": "Reselect later",
3233
"searchBoard": "Search board",
3334
"selectBoard": "Select Board",
34-
"selectBoardForInfo": "Please select a board to obtain board info.",
3535
"selectPortForInfo": "Please select a port to obtain board info.",
3636
"showAllAvailablePorts": "Shows all available ports when enabled",
3737
"showAllPorts": "Show all ports",
3838
"succesfullyInstalledPlatform": "Successfully installed platform {0}:{1}",
3939
"succesfullyUninstalledPlatform": "Successfully uninstalled platform {0}:{1}",
40-
"typeOfPorts": "{0} ports"
40+
"typeOfPorts": "{0} ports",
41+
"unknownBoard": "Unknown board"
4142
},
4243
"boardsManager": "Boards Manager",
4344
"boardsType": {

0 commit comments

Comments
 (0)