From c48d80b1374dbb6b4234fb9fdbea1d9942737361 Mon Sep 17 00:00:00 2001 From: Christian Weichel Date: Mon, 6 May 2019 15:48:33 +0200 Subject: [PATCH] Made boards installable --- .../component-list/component-list-item.tsx | 8 ++- .../component-list/component-list.tsx | 3 +- .../filterable-list-container.tsx | 15 ++++ .../installation-progress-dialog.tsx | 12 ++++ .../src/common/protocol/boards-service.ts | 2 + .../src/common/protocol/library-service.ts | 1 + .../src/node/boards-service-impl.ts | 72 ++++++++++++++++--- .../src/node/core-client-provider-impl.ts | 45 +++++++----- .../src/node/core-service-impl.ts | 12 +--- .../src/node/library-service-impl.ts | 4 ++ 10 files changed, 135 insertions(+), 39 deletions(-) create mode 100644 arduino-ide-extension/src/browser/components/installation-progress-dialog.tsx diff --git a/arduino-ide-extension/src/browser/components/component-list/component-list-item.tsx b/arduino-ide-extension/src/browser/components/component-list/component-list-item.tsx index 4e7b294e..9d1fddbe 100644 --- a/arduino-ide-extension/src/browser/components/component-list/component-list-item.tsx +++ b/arduino-ide-extension/src/browser/components/component-list/component-list-item.tsx @@ -12,6 +12,10 @@ export class ComponentListItem extends React.Component } } + 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 const description = !!item.description &&
{item.description}
; const moreInfo = !!item.moreInfoLink && More info; - const install = item.installable && !item.installedVersion && ; + const install = this.props.install && item.installable && !item.installedVersion && + ; return
@@ -52,6 +57,7 @@ export namespace ComponentListItem { export interface Props { readonly item: ArduinoComponent; readonly windowService: WindowService; + readonly install: (comp: ArduinoComponent) => Promise; } export namespace Styles { diff --git a/arduino-ide-extension/src/browser/components/component-list/component-list.tsx b/arduino-ide-extension/src/browser/components/component-list/component-list.tsx index 15b05f91..699b5bd0 100644 --- a/arduino-ide-extension/src/browser/components/component-list/component-list.tsx +++ b/arduino-ide-extension/src/browser/components/component-list/component-list.tsx @@ -7,7 +7,7 @@ export class ComponentList extends React.Component { render(): React.ReactNode { return
- {this.props.items.map(item => )} + {this.props.items.map(item => )}
; } @@ -18,6 +18,7 @@ export namespace ComponentList { export interface Props { readonly items: ArduinoComponent[]; readonly windowService: WindowService; + readonly install: (comp: ArduinoComponent) => Promise; } } diff --git a/arduino-ide-extension/src/browser/components/component-list/filterable-list-container.tsx b/arduino-ide-extension/src/browser/components/component-list/filterable-list-container.tsx index 41730ef7..20994388 100644 --- a/arduino-ide-extension/src/browser/components/component-list/filterable-list-container.tsx +++ b/arduino-ide-extension/src/browser/components/component-list/filterable-list-container.tsx @@ -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 { @@ -27,6 +28,7 @@ export class FilterableListContainer extends React.Component
@@ -42,6 +44,18 @@ export class FilterableListContainer extends React.Component { + 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; } } diff --git a/arduino-ide-extension/src/browser/components/installation-progress-dialog.tsx b/arduino-ide-extension/src/browser/components/installation-progress-dialog.tsx new file mode 100644 index 00000000..8d0be2de --- /dev/null +++ b/arduino-ide-extension/src/browser/components/installation-progress-dialog.tsx @@ -0,0 +1,12 @@ +import { AbstractDialog } from "@theia/core/lib/browser"; + + +export class InstallationProgressDialog extends AbstractDialog { + readonly value: "does-not-matter"; + + constructor(componentName: string) { + super({ title: 'Installation in progress' }); + this.contentNode.textContent = `Installing ${componentName}. Please wait.`; + } + +} \ No newline at end of file diff --git a/arduino-ide-extension/src/common/protocol/boards-service.ts b/arduino-ide-extension/src/common/protocol/boards-service.ts index 7af478a2..3f6db28c 100644 --- a/arduino-ide-extension/src/common/protocol/boards-service.ts +++ b/arduino-ide-extension/src/common/protocol/boards-service.ts @@ -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; } export interface Board extends ArduinoComponent { + id: string; } diff --git a/arduino-ide-extension/src/common/protocol/library-service.ts b/arduino-ide-extension/src/common/protocol/library-service.ts index 56c2f8d8..f8277b0b 100644 --- a/arduino-ide-extension/src/common/protocol/library-service.ts +++ b/arduino-ide-extension/src/common/protocol/library-service.ts @@ -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; } export interface Library extends ArduinoComponent { diff --git a/arduino-ide-extension/src/node/boards-service-impl.ts b/arduino-ide-extension/src/node/boards-service-impl.ts index 4f15b7c3..ea98b600 100644 --- a/arduino-ide-extension/src/node/boards-service-impl.ts +++ b/arduino-ide-extension/src/node/boards-service-impl.ts @@ -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((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((resolve, reject) => client.platformSearch(req, (err, resp) => (!!err ? reject : resolve)(!!err ? err : resp))); - items = resp.getSearchOutputList().map(o => { - 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 { + 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((resolve, reject) => { + resp.on('end', resolve); + resp.on('error', reject); + }); + console.info("Board installation done", board); + } + } diff --git a/arduino-ide-extension/src/node/core-client-provider-impl.ts b/arduino-ide-extension/src/node/core-client-provider-impl.ts index 0b9dfd68..e6a3c1e2 100644 --- a/arduino-ide-extension/src/node/core-client-provider-impl.ts +++ b/arduino-ide-extension/src/node/core-client-provider-impl.ts @@ -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((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((resolve, reject) => { diff --git a/arduino-ide-extension/src/node/core-service-impl.ts b/arduino-ide-extension/src/node/core-service-impl.ts index e8c631f3..c464a1e8 100644 --- a/arduino-ide-extension/src/node/core-service-impl.ts +++ b/arduino-ide-extension/src/node/core-service-impl.ts @@ -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'); diff --git a/arduino-ide-extension/src/node/library-service-impl.ts b/arduino-ide-extension/src/node/library-service-impl.ts index 11ed65fe..9d435d21 100644 --- a/arduino-ide-extension/src/node/library-service-impl.ts +++ b/arduino-ide-extension/src/node/library-service-impl.ts @@ -43,4 +43,8 @@ export class LibraryServiceImpl implements LibraryService { }; } + async install(board: Library): Promise { + + } + }