mirror of
https://github.com/arduino/arduino-ide.git
synced 2025-07-08 11:56:36 +00:00
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 <a.kitta@arduino.cc>
This commit is contained in:
parent
f63ee85fa3
commit
5d264ef5b6
@ -6,6 +6,7 @@ import { DisposableCollection } from '@theia/core/lib/common/disposable';
|
|||||||
import {
|
import {
|
||||||
Board,
|
Board,
|
||||||
Port,
|
Port,
|
||||||
|
BoardConfig as ProtocolBoardConfig,
|
||||||
BoardWithPackage,
|
BoardWithPackage,
|
||||||
} from '../../common/protocol/boards-service';
|
} from '../../common/protocol/boards-service';
|
||||||
import { NotificationCenter } from '../notification-center';
|
import { NotificationCenter } from '../notification-center';
|
||||||
@ -18,10 +19,7 @@ import { nls } from '@theia/core/lib/common';
|
|||||||
import { FrontendApplicationState } from '@theia/core/lib/common/frontend-application-state';
|
import { FrontendApplicationState } from '@theia/core/lib/common/frontend-application-state';
|
||||||
|
|
||||||
export namespace BoardsConfig {
|
export namespace BoardsConfig {
|
||||||
export interface Config {
|
export type Config = ProtocolBoardConfig;
|
||||||
selectedBoard?: Board;
|
|
||||||
selectedPort?: Port;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface Props {
|
export interface Props {
|
||||||
readonly boardsServiceProvider: BoardsServiceProvider;
|
readonly boardsServiceProvider: BoardsServiceProvider;
|
||||||
|
@ -20,6 +20,7 @@ import {
|
|||||||
InstalledBoardWithPackage,
|
InstalledBoardWithPackage,
|
||||||
AvailablePorts,
|
AvailablePorts,
|
||||||
Port,
|
Port,
|
||||||
|
getBoardInfo,
|
||||||
} from '../../common/protocol';
|
} from '../../common/protocol';
|
||||||
import { SketchContribution, Command, CommandRegistry } from './contribution';
|
import { SketchContribution, Command, CommandRegistry } from './contribution';
|
||||||
import { nls } from '@theia/core/lib/common';
|
import { nls } from '@theia/core/lib/common';
|
||||||
@ -49,52 +50,28 @@ export class BoardSelection extends SketchContribution {
|
|||||||
override registerCommands(registry: CommandRegistry): void {
|
override registerCommands(registry: CommandRegistry): void {
|
||||||
registry.registerCommand(BoardSelection.Commands.GET_BOARD_INFO, {
|
registry.registerCommand(BoardSelection.Commands.GET_BOARD_INFO, {
|
||||||
execute: async () => {
|
execute: async () => {
|
||||||
const { selectedBoard, selectedPort } =
|
const boardInfo = await getBoardInfo(
|
||||||
this.boardsServiceProvider.boardsConfig;
|
this.boardsServiceProvider.boardsConfig.selectedPort,
|
||||||
if (!selectedBoard) {
|
this.boardsService.getState()
|
||||||
this.messageService.info(
|
);
|
||||||
nls.localize(
|
if (typeof boardInfo === 'string') {
|
||||||
'arduino/board/selectBoardForInfo',
|
this.messageService.info(boardInfo);
|
||||||
'Please select a board to obtain board info.'
|
|
||||||
)
|
|
||||||
);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (!selectedBoard.fqbn) {
|
const { BN, VID, PID, SN } = boardInfo;
|
||||||
this.messageService.info(
|
const detail = `
|
||||||
nls.localize(
|
BN: ${BN}
|
||||||
'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.'
|
|
||||||
)
|
|
||||||
);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const boardDetails = await this.boardsService.getBoardDetails({
|
|
||||||
fqbn: selectedBoard.fqbn,
|
|
||||||
});
|
|
||||||
if (boardDetails) {
|
|
||||||
const { VID, PID } = boardDetails;
|
|
||||||
const detail = `BN: ${selectedBoard.name}
|
|
||||||
VID: ${VID}
|
VID: ${VID}
|
||||||
PID: ${PID}`;
|
PID: ${PID}
|
||||||
await remote.dialog.showMessageBox(remote.getCurrentWindow(), {
|
SN: ${SN}
|
||||||
message: nls.localize('arduino/board/boardInfo', 'Board Info'),
|
`.trim();
|
||||||
title: nls.localize('arduino/board/boardInfo', 'Board Info'),
|
await remote.dialog.showMessageBox(remote.getCurrentWindow(), {
|
||||||
type: 'info',
|
message: nls.localize('arduino/board/boardInfo', 'Board Info'),
|
||||||
detail,
|
title: nls.localize('arduino/board/boardInfo', 'Board Info'),
|
||||||
buttons: [nls.localize('vscode/issueMainService/ok', 'OK')],
|
type: 'info',
|
||||||
});
|
detail,
|
||||||
}
|
buttons: [nls.localize('vscode/issueMainService/ok', 'OK')],
|
||||||
|
});
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -11,6 +11,7 @@ import {
|
|||||||
Updatable,
|
Updatable,
|
||||||
} from '../nls';
|
} from '../nls';
|
||||||
import URI from '@theia/core/lib/common/uri';
|
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 type AvailablePorts = Record<string, [Port, Array<Board>]>;
|
||||||
export namespace AvailablePorts {
|
export namespace AvailablePorts {
|
||||||
@ -657,3 +658,107 @@ export function sanitizeFqbn(fqbn: string | undefined): string | undefined {
|
|||||||
const [vendor, arch, id] = fqbn.split(':');
|
const [vendor, arch, id] = fqbn.split(':');
|
||||||
return `${vendor}:${arch}:${id}`;
|
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)';
|
||||||
|
}
|
||||||
|
@ -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 { 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('boards-service', () => {
|
||||||
describe('AttachedBoardsChangeEvent', () => {
|
describe('AttachedBoardsChangeEvent', () => {
|
||||||
@ -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;
|
||||||
|
}
|
||||||
|
@ -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?",
|
"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}\"",
|
"noBoardsFound": "No boards found for \"{0}\"",
|
||||||
"noFQBN": "The FQBN is not available for the selected board \"{0}\". Do you have the corresponding core installed?",
|
"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",
|
"noPortsDiscovered": "No ports discovered",
|
||||||
"noPortsSelected": "No ports selected for board: '{0}'.",
|
"noPortsSelected": "No ports selected for board: '{0}'.",
|
||||||
|
"nonSerialPort": "Non-serial port, can't obtain info.",
|
||||||
"noneSelected": "No boards selected.",
|
"noneSelected": "No boards selected.",
|
||||||
"openBoardsConfig": "Select other board and port…",
|
"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.",
|
"pleasePickBoard": "Please pick a board connected to the port you have selected.",
|
||||||
"port": "Port{0}",
|
"port": "Port{0}",
|
||||||
"portLabel": "Port: {0}",
|
"portLabel": "Port: {0}",
|
||||||
@ -31,13 +32,13 @@
|
|||||||
"reselectLater": "Reselect later",
|
"reselectLater": "Reselect later",
|
||||||
"searchBoard": "Search board",
|
"searchBoard": "Search board",
|
||||||
"selectBoard": "Select Board",
|
"selectBoard": "Select Board",
|
||||||
"selectBoardForInfo": "Please select a board to obtain board info.",
|
|
||||||
"selectPortForInfo": "Please select a port to obtain board info.",
|
"selectPortForInfo": "Please select a port to obtain board info.",
|
||||||
"showAllAvailablePorts": "Shows all available ports when enabled",
|
"showAllAvailablePorts": "Shows all available ports when enabled",
|
||||||
"showAllPorts": "Show all ports",
|
"showAllPorts": "Show all ports",
|
||||||
"succesfullyInstalledPlatform": "Successfully installed platform {0}:{1}",
|
"succesfullyInstalledPlatform": "Successfully installed platform {0}:{1}",
|
||||||
"succesfullyUninstalledPlatform": "Successfully uninstalled platform {0}:{1}",
|
"succesfullyUninstalledPlatform": "Successfully uninstalled platform {0}:{1}",
|
||||||
"typeOfPorts": "{0} ports"
|
"typeOfPorts": "{0} ports",
|
||||||
|
"unknownBoard": "Unknown board"
|
||||||
},
|
},
|
||||||
"boardsManager": "Boards Manager",
|
"boardsManager": "Boards Manager",
|
||||||
"boardsType": {
|
"boardsType": {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user