mirror of
https://github.com/arduino/arduino-ide.git
synced 2025-07-24 11:46:32 +00:00
Made boards installable
This commit is contained in:
parent
201351fea8
commit
c48d80b137
@ -12,6 +12,10 @@ export class ComponentListItem extends React.Component<ComponentListItem.Props>
|
||||
}
|
||||
}
|
||||
|
||||
private async install(item: ArduinoComponent) {
|
||||
await this.props.install(item);
|
||||
}
|
||||
|
||||
render(): React.ReactNode {
|
||||
const { item } = this.props;
|
||||
|
||||
@ -27,7 +31,8 @@ export class ComponentListItem extends React.Component<ComponentListItem.Props>
|
||||
const description = !!item.description && <div className={style.DESCRIPTION_CLASS}>{item.description}</div>;
|
||||
|
||||
const moreInfo = !!item.moreInfoLink && <a href={item.moreInfoLink} onClick={this.onClick}>More info</a>;
|
||||
const install = item.installable && !item.installedVersion && <button className={style.INSTALL_BTN_CLASS}>INSTALL</button>;
|
||||
const install = this.props.install && item.installable && !item.installedVersion &&
|
||||
<button className={style.INSTALL_BTN_CLASS} onClick={this.install.bind(this, item)}>INSTALL</button>;
|
||||
|
||||
return <div className={[style.LIST_ITEM_CLASS, style.NO_SELECT_CLASS].join(' ')}>
|
||||
<div className={style.HEADER_CLASS}>
|
||||
@ -52,6 +57,7 @@ export namespace ComponentListItem {
|
||||
export interface Props {
|
||||
readonly item: ArduinoComponent;
|
||||
readonly windowService: WindowService;
|
||||
readonly install: (comp: ArduinoComponent) => Promise<void>;
|
||||
}
|
||||
|
||||
export namespace Styles {
|
||||
|
@ -7,7 +7,7 @@ export class ComponentList extends React.Component<ComponentList.Props> {
|
||||
|
||||
render(): React.ReactNode {
|
||||
return <div>
|
||||
{this.props.items.map(item => <ComponentListItem key={item.name} item={item} windowService={this.props.windowService}/>)}
|
||||
{this.props.items.map(item => <ComponentListItem key={item.name} item={item} windowService={this.props.windowService} install={this.props.install} />)}
|
||||
</div>;
|
||||
}
|
||||
|
||||
@ -18,6 +18,7 @@ export namespace ComponentList {
|
||||
export interface Props {
|
||||
readonly items: ArduinoComponent[];
|
||||
readonly windowService: WindowService;
|
||||
readonly install: (comp: ArduinoComponent) => Promise<void>;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -3,6 +3,7 @@ import { WindowService } from '@theia/core/lib/browser/window/window-service';
|
||||
import { ComponentList } from './component-list';
|
||||
import { SearchBar } from './search-bar';
|
||||
import { ArduinoComponent } from '../../../common/protocol/arduino-component';
|
||||
import { InstallationProgressDialog } from '../installation-progress-dialog';
|
||||
|
||||
export class FilterableListContainer extends React.Component<FilterableListContainer.Props, FilterableListContainer.State> {
|
||||
|
||||
@ -27,6 +28,7 @@ export class FilterableListContainer extends React.Component<FilterableListConta
|
||||
/>
|
||||
<ComponentList
|
||||
items={this.state.items}
|
||||
install={this.install.bind(this)}
|
||||
windowService={this.props.windowService}
|
||||
/>
|
||||
</div>
|
||||
@ -42,6 +44,18 @@ export class FilterableListContainer extends React.Component<FilterableListConta
|
||||
});
|
||||
}
|
||||
|
||||
protected async install(comp: ArduinoComponent): Promise<void> {
|
||||
const dialog = new InstallationProgressDialog(comp.name);
|
||||
dialog.open();
|
||||
try {
|
||||
await this.props.service.install(comp);
|
||||
const { items } = await this.props.service.search({ query: this.state.filterText });
|
||||
this.setState({ items });
|
||||
} finally {
|
||||
dialog.close();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export namespace FilterableListContainer {
|
||||
@ -62,6 +76,7 @@ export namespace FilterableListContainer {
|
||||
|
||||
export interface ComponentSource {
|
||||
search(req: { query: string }): Promise<{ items: ArduinoComponent[] }>
|
||||
install(board: ArduinoComponent): Promise<void>;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -0,0 +1,12 @@
|
||||
import { AbstractDialog } from "@theia/core/lib/browser";
|
||||
|
||||
|
||||
export class InstallationProgressDialog extends AbstractDialog<string> {
|
||||
readonly value: "does-not-matter";
|
||||
|
||||
constructor(componentName: string) {
|
||||
super({ title: 'Installation in progress' });
|
||||
this.contentNode.textContent = `Installing ${componentName}. Please wait.`;
|
||||
}
|
||||
|
||||
}
|
@ -5,7 +5,9 @@ export const BoardsService = Symbol('BoardsService');
|
||||
export interface BoardsService {
|
||||
connectedBoards(): Promise<{ boards: Board[], current?: Board }>;
|
||||
search(options: { query?: string }): Promise<{ items: Board[] }>;
|
||||
install(board: Board): Promise<void>;
|
||||
}
|
||||
|
||||
export interface Board extends ArduinoComponent {
|
||||
id: string;
|
||||
}
|
||||
|
@ -4,6 +4,7 @@ export const LibraryServicePath = '/services/library-service';
|
||||
export const LibraryService = Symbol('LibraryService');
|
||||
export interface LibraryService {
|
||||
search(options: { query?: string }): Promise<{ items: Library[] }>;
|
||||
install(board: Library): Promise<void>;
|
||||
}
|
||||
|
||||
export interface Library extends ArduinoComponent {
|
||||
|
@ -1,6 +1,6 @@
|
||||
import { injectable, inject } from 'inversify';
|
||||
import { BoardsService, Board } from '../common/protocol/boards-service';
|
||||
import { PlatformSearchReq, PlatformSearchResp } from './cli-protocol/core_pb';
|
||||
import { PlatformSearchReq, PlatformSearchResp, PlatformInstallReq, PlatformInstallResp, PlatformListReq, PlatformListResp } from './cli-protocol/core_pb';
|
||||
import { CoreClientProvider } from './core-client-provider';
|
||||
|
||||
@injectable()
|
||||
@ -14,24 +14,76 @@ export class BoardsServiceImpl implements BoardsService {
|
||||
}
|
||||
|
||||
async search(options: { query?: string }): Promise<{ items: Board[] }> {
|
||||
let items: Board[] = [];
|
||||
|
||||
const { client, instance } = await this.coreClientProvider.getClient();
|
||||
|
||||
const installedPlatformsReq = new PlatformListReq();
|
||||
installedPlatformsReq.setInstance(instance);
|
||||
const installedPlatformsResp = await new Promise<PlatformListResp>((resolve, reject) =>
|
||||
client.platformList(installedPlatformsReq, (err, resp) => (!!err ? reject : resolve)(!!err ? err : resp))
|
||||
);
|
||||
const installedPlatforms = installedPlatformsResp.getInstalledPlatformList();
|
||||
console.info("Installed platforms", installedPlatforms);
|
||||
|
||||
const req = new PlatformSearchReq();
|
||||
req.setSearchArgs(options.query || "");
|
||||
req.setInstance(instance);
|
||||
const resp = await new Promise<PlatformSearchResp>((resolve, reject) => client.platformSearch(req, (err, resp) => (!!err ? reject : resolve)(!!err ? err : resp)));
|
||||
items = resp.getSearchOutputList().map(o => <Board>{
|
||||
name: o.getName(),
|
||||
author: "Someone",
|
||||
availableVersions: [],
|
||||
description: "lorem ipsum sit dolor amet",
|
||||
installable: false,
|
||||
summary: "has none"
|
||||
|
||||
let items = resp.getSearchOutputList().map(o => {
|
||||
let installedVersion: string | undefined;
|
||||
const matchingPlatform = installedPlatforms.find(ip => ip.getId().startsWith(`${o.getId()}@`));
|
||||
if (!!matchingPlatform) {
|
||||
installedVersion = matchingPlatform.getInstalled();
|
||||
}
|
||||
|
||||
const result: Board = {
|
||||
id: o.getId(),
|
||||
name: o.getName(),
|
||||
author: "Someone",
|
||||
availableVersions: [ o.getVersion() ],
|
||||
description: "lorem ipsum sit dolor amet",
|
||||
installable: true,
|
||||
summary: "has none",
|
||||
installedVersion,
|
||||
}
|
||||
return result;
|
||||
}).sort((a, b) => {
|
||||
if (a.name < b.name) {
|
||||
return -1;
|
||||
} else if (a.name === b.name) {
|
||||
return 0;
|
||||
} else {
|
||||
return 1;
|
||||
}
|
||||
});
|
||||
|
||||
return { items };
|
||||
}
|
||||
|
||||
async install(board: Board): Promise<void> {
|
||||
const { client, instance } = await this.coreClientProvider.getClient();
|
||||
|
||||
const [ platform, boardName ] = board.id.split(":");
|
||||
|
||||
const req = new PlatformInstallReq();
|
||||
req.setArchitecture(boardName);
|
||||
req.setPlatformPackage(platform);
|
||||
req.setVersion(board.availableVersions[0]);
|
||||
req.setInstance(instance);
|
||||
|
||||
console.info("Starting board installation", board);
|
||||
const resp = client.platformInstall(req);
|
||||
resp.on('data', (r: PlatformInstallResp) => {
|
||||
const prog = r.getProgress();
|
||||
if (prog) {
|
||||
console.info(`downloading ${prog.getFile()}: ${prog.getCompleted()}%`)
|
||||
}
|
||||
});
|
||||
await new Promise<void>((resolve, reject) => {
|
||||
resp.on('end', resolve);
|
||||
resp.on('error', reject);
|
||||
});
|
||||
console.info("Board installation done", board);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -6,6 +6,8 @@ import { WorkspaceServiceExt } from '../browser/workspace-service-ext';
|
||||
import { FileSystem } from '@theia/filesystem/lib/common';
|
||||
import URI from '@theia/core/lib/common/uri';
|
||||
import { CoreClientProvider, Client } from './core-client-provider';
|
||||
import * as fs from 'fs';
|
||||
import * as path from 'path';
|
||||
|
||||
@injectable()
|
||||
export class CoreClientProviderImpl implements CoreClientProvider {
|
||||
@ -44,14 +46,15 @@ export class CoreClientProviderImpl implements CoreClientProvider {
|
||||
|
||||
console.info(` >>> Creating and caching a new client for ${rootUri}...`);
|
||||
const client = new ArduinoCoreClient('localhost:50051', grpc.credentials.createInsecure());
|
||||
|
||||
const config = new Configuration();
|
||||
const path = await this.fileSystem.getFsPath(rootUri);
|
||||
if (!path) {
|
||||
const rootPath = await this.fileSystem.getFsPath(rootUri);
|
||||
if (!rootPath) {
|
||||
throw new Error(`Could not resolve file-system path of URI: ${rootUri}.`);
|
||||
}
|
||||
config.setSketchbookdir(path);
|
||||
config.setDatadir(path);
|
||||
config.setDownloadsdir(path);
|
||||
config.setSketchbookdir(rootPath);
|
||||
config.setDatadir(rootPath);
|
||||
config.setDownloadsdir(rootPath);
|
||||
|
||||
const initReq = new InitReq();
|
||||
initReq.setConfiguration(config);
|
||||
@ -60,19 +63,27 @@ export class CoreClientProviderImpl implements CoreClientProvider {
|
||||
if (!instance) {
|
||||
throw new Error(`Could not retrieve instance from the initialize response.`);
|
||||
}
|
||||
const updateReq = new UpdateIndexReq();
|
||||
updateReq.setInstance(instance);
|
||||
const updateResp = client.updateIndex(updateReq);
|
||||
updateResp.on('data', (o: UpdateIndexResp) => {
|
||||
const progress = o.getDownloadProgress();
|
||||
if (progress) {
|
||||
if (progress.getCompleted()) {
|
||||
console.log(`Download${progress.getFile() ? ` of ${progress.getFile()}` : ''} completed.`);
|
||||
} else {
|
||||
console.log(`Downloading${progress.getFile() ? ` ${progress.getFile()}:` : ''} ${progress.getDownloaded()}.`);
|
||||
|
||||
// workaround to speed up startup on existing workspaces
|
||||
if (!fs.existsSync(path.join(config.getDatadir(), "package_index.json"))) {
|
||||
const updateReq = new UpdateIndexReq();
|
||||
updateReq.setInstance(instance);
|
||||
const updateResp = client.updateIndex(updateReq);
|
||||
updateResp.on('data', (o: UpdateIndexResp) => {
|
||||
const progress = o.getDownloadProgress();
|
||||
if (progress) {
|
||||
if (progress.getCompleted()) {
|
||||
console.log(`Download${progress.getFile() ? ` of ${progress.getFile()}` : ''} completed.`);
|
||||
} else {
|
||||
console.log(`Downloading${progress.getFile() ? ` ${progress.getFile()}:` : ''} ${progress.getDownloaded()}.`);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
await new Promise<void>((resolve, reject) => {
|
||||
updateResp.on('error', reject);
|
||||
updateResp.on('end', resolve);
|
||||
});
|
||||
}
|
||||
// TODO: revisit this!!!
|
||||
// `updateResp.on('data'` is called only when running, for instance, `compile`. It does not run eagerly.
|
||||
// await new Promise<void>((resolve, reject) => {
|
||||
|
@ -24,8 +24,9 @@ export class CoreServiceImpl implements CoreService {
|
||||
const { uri } = options;
|
||||
const sketchpath = await this.fileSystem.getFsPath(options.uri);
|
||||
if (!sketchpath) {
|
||||
throw new Error(`Cannot resolve FS path for URI: ${uri}.`);
|
||||
throw new Error(`Cannot resolve filesystem path for URI: ${uri}.`);
|
||||
}
|
||||
|
||||
const { client, instance } = await this.coreClientProvider.getClient(uri);
|
||||
// const boards = await this.boardsService.connectedBoards();
|
||||
// if (!boards.current) {
|
||||
@ -33,15 +34,6 @@ export class CoreServiceImpl implements CoreService {
|
||||
// }
|
||||
// https://github.com/cmaglie/arduino-cli/blob/bd5e78701e7546787649d3cca6b21c5d22d0e438/cli/compile/compile.go#L78-L88
|
||||
|
||||
const installPlatformReq = new PlatformInstallReq();
|
||||
installPlatformReq.setArchitecture('samd');
|
||||
installPlatformReq.setVersion('1.6.0');
|
||||
installPlatformReq.setInstance(instance);
|
||||
const resp = client.platformInstall(installPlatformReq);
|
||||
console.log(resp);
|
||||
|
||||
|
||||
|
||||
const installLibReq = new LibraryInstallReq();
|
||||
installLibReq.setInstance(instance);
|
||||
installLibReq.setName('arduino:samd');
|
||||
|
@ -43,4 +43,8 @@ export class LibraryServiceImpl implements LibraryService {
|
||||
};
|
||||
}
|
||||
|
||||
async install(board: Library): Promise<void> {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user