mirror of
https://github.com/arduino/arduino-ide.git
synced 2025-04-19 12:57:17 +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 {
|
||||
Board,
|
||||
Port,
|
||||
BoardConfig as ProtocolBoardConfig,
|
||||
BoardWithPackage,
|
||||
} from '../../common/protocol/boards-service';
|
||||
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';
|
||||
|
||||
export namespace BoardsConfig {
|
||||
export interface Config {
|
||||
selectedBoard?: Board;
|
||||
selectedPort?: Port;
|
||||
}
|
||||
export type Config = ProtocolBoardConfig;
|
||||
|
||||
export interface Props {
|
||||
readonly boardsServiceProvider: BoardsServiceProvider;
|
||||
|
@ -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';
|
||||
@ -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.'
|
||||
)
|
||||
);
|
||||
const boardInfo = await getBoardInfo(
|
||||
this.boardsServiceProvider.boardsConfig.selectedPort,
|
||||
this.boardsService.getState()
|
||||
);
|
||||
if (typeof boardInfo === 'string') {
|
||||
this.messageService.info(boardInfo);
|
||||
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.'
|
||||
)
|
||||
);
|
||||
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')],
|
||||
});
|
||||
},
|
||||
});
|
||||
}
|
||||
|
@ -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 {
|
||||
@ -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)';
|
||||
}
|
||||
|
@ -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', () => {
|
||||
@ -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?",
|
||||
"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}",
|
||||
@ -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": {
|
||||
|
Loading…
x
Reference in New Issue
Block a user