diff --git a/arduino-ide-extension/src/browser/arduino-frontend-contribution.tsx b/arduino-ide-extension/src/browser/arduino-frontend-contribution.tsx index 524b15ce..ea346a1d 100644 --- a/arduino-ide-extension/src/browser/arduino-frontend-contribution.tsx +++ b/arduino-ide-extension/src/browser/arduino-frontend-contribution.tsx @@ -75,10 +75,11 @@ export class ArduinoFrontendContribution extends DefaultFrontendApplicationContr registry.registerCommand(ArduinoCommands.UPLOAD, { isVisible: widget => this.isArduinoEditor(widget), isEnabled: widget => this.isArduinoEditor(widget), - execute: widget => { + execute: async widget => { const uri = this.toUri(widget); if (uri) { - this.messageService.info(`Uploading ${uri.toString()}`); + const result = await this.coreService.upload({ uri: uri.toString() }); + console.log('upload result', result); } } }); diff --git a/arduino-ide-extension/src/browser/components/connected-boards.tsx b/arduino-ide-extension/src/browser/components/connected-boards.tsx index 149701c9..8704862c 100644 --- a/arduino-ide-extension/src/browser/components/connected-boards.tsx +++ b/arduino-ide-extension/src/browser/components/connected-boards.tsx @@ -4,40 +4,60 @@ import * as React from 'react'; import { BoardsService, AttachedBoard } from '../../common/protocol/boards-service'; export class ConnectedBoards extends React.Component { - static TOOLBAR_ID: 'connected-boards-toolbar'; + constructor(props: ConnectedBoards.Props) { + super(props); + this.state = { boardsLoading: false }; + } + render(): React.ReactNode { + let label = "no board available"; + if (this.state.boardsLoading) { + label = "Loading ..."; + } else if (!!this.state.current) { + label = this.state.current.name; + } + + let content = []; + if (!!this.state.boards) { + content = this.state.boards.map((b, i) => ); + } else { + content = [ ]; + } + return
- {this.select(this.state ? this.state.boards : undefined, this.state ? this.state.current : undefined)} +
; } componentDidMount(): void { - this.props.boardsService.attachedBoards().then(result => { - const { boards } = result; - this.setState({ boards }); - }); + this.reloadBoards(); } - private select(boards: AttachedBoard[] | undefined, current: AttachedBoard | undefined): React.ReactNode { - // Initial pessimistic. - const options = []; + protected async reloadBoards() { + this.setState({ boardsLoading: true, boards: undefined, current: undefined }); + const { boards } = await this.props.boardsService.getAttachedBoards() + this.setState({ boards, boardsLoading: false }); + if (boards) { - options.length = 0; - options.push(...boards.map(b => b.name).map(name => )); + this.selectBoard(boards[0]); } - const onChange = (event: React.ChangeEvent) => { - const current = (boards || []).find(board => board.name === event.target.value); - this.setState({ current }); - }; - return + } + + protected async onBoardSelect(evt: React.ChangeEvent) { + const selectedBoard = (this.state.boards || [])[parseInt(evt.target.value, 10)]; + if (!selectedBoard) { + return; + } + this.selectBoard(selectedBoard); + } + + protected async selectBoard(board: AttachedBoard) { + await this.props.boardsService.selectBoard(board); + this.setState({ current: board }); } } @@ -49,6 +69,7 @@ export namespace ConnectedBoards { } export interface State { + boardsLoading: boolean; boards?: AttachedBoard[]; current?: AttachedBoard; } diff --git a/arduino-ide-extension/src/common/protocol/boards-service.ts b/arduino-ide-extension/src/common/protocol/boards-service.ts index 795d71d0..efd89948 100644 --- a/arduino-ide-extension/src/common/protocol/boards-service.ts +++ b/arduino-ide-extension/src/common/protocol/boards-service.ts @@ -3,7 +3,10 @@ import { ArduinoComponent } from "./arduino-component"; export const BoardsServicePath = '/services/boards-service'; export const BoardsService = Symbol('BoardsService'); export interface BoardsService { - attachedBoards(): Promise<{ boards: AttachedBoard[] }>; + getAttachedBoards(): Promise<{ boards: AttachedBoard[] }>; + selectBoard(board: AttachedBoard): Promise; + getSelectBoard(): Promise; + search(options: { query?: string }): Promise<{ items: Board[] }>; install(board: Board): Promise; } diff --git a/arduino-ide-extension/src/common/protocol/core-service.ts b/arduino-ide-extension/src/common/protocol/core-service.ts index 3e443264..8f671a56 100644 --- a/arduino-ide-extension/src/common/protocol/core-service.ts +++ b/arduino-ide-extension/src/common/protocol/core-service.ts @@ -2,10 +2,17 @@ export const CoreServicePath = '/services/core-service'; export const CoreService = Symbol('CoreService'); export interface CoreService { compile(options: CoreService.Compile.Options): Promise; - upload(): Promise; + upload(options: CoreService.Upload.Options): Promise; } export namespace CoreService { + + export namespace Upload { + export interface Options { + readonly uri: string; + } + } + export namespace Compile { export interface Options { readonly uri: string; diff --git a/arduino-ide-extension/src/node/arduino-daemon.ts b/arduino-ide-extension/src/node/arduino-daemon.ts index 87937a09..63a6445e 100644 --- a/arduino-ide-extension/src/node/arduino-daemon.ts +++ b/arduino-ide-extension/src/node/arduino-daemon.ts @@ -33,13 +33,13 @@ export class ArduinoDaemon implements BackendApplicationContribution { }); if (daemon.stdout) { daemon.stdout.on('data', data => { - this.toolOutputService.publishNewOutput("daeomn", data.toString()); + this.toolOutputService.publishNewOutput("daemon", data.toString()); DaemonLog.log(this.logger, data.toString()); }); } if (daemon.stderr) { daemon.stderr.on('data', data => { - this.toolOutputService.publishNewOutput("daeomn error", data.toString()); + this.toolOutputService.publishNewOutput("daemon error", data.toString()); DaemonLog.log(this.logger, data.toString()); }); } diff --git a/arduino-ide-extension/src/node/boards-service-impl.ts b/arduino-ide-extension/src/node/boards-service-impl.ts index 16f3f085..882d9e67 100644 --- a/arduino-ide-extension/src/node/boards-service-impl.ts +++ b/arduino-ide-extension/src/node/boards-service-impl.ts @@ -10,7 +10,9 @@ export class BoardsServiceImpl implements BoardsService { @inject(CoreClientProvider) protected readonly coreClientProvider: CoreClientProvider; - public async attachedBoards(): Promise<{ boards: AttachedBoard[] }> { + protected selectedBoard: AttachedBoard | undefined; + + public async getAttachedBoards(): Promise<{ boards: AttachedBoard[] }> { const { client, instance } = await this.coreClientProvider.getClient(); const req = new BoardListReq(); @@ -36,6 +38,14 @@ export class BoardsServiceImpl implements BoardsService { return { boards: serialBoards.concat(networkBoards) }; } + async selectBoard(board: AttachedBoard): Promise { + this.selectedBoard = board; + } + + async getSelectBoard(): Promise { + return this.selectedBoard; + } + async search(options: { query?: string }): Promise<{ items: Board[] }> { const { client, instance } = await this.coreClientProvider.getClient(); diff --git a/arduino-ide-extension/src/node/core-service-impl.ts b/arduino-ide-extension/src/node/core-service-impl.ts index 4c56895e..0c8ffa0f 100644 --- a/arduino-ide-extension/src/node/core-service-impl.ts +++ b/arduino-ide-extension/src/node/core-service-impl.ts @@ -2,10 +2,11 @@ import { inject, injectable } from 'inversify'; import { FileSystem } from '@theia/filesystem/lib/common/filesystem'; import { CoreService } from '../common/protocol/core-service'; import { CompileReq, CompileResp } from './cli-protocol/compile_pb'; -import { BoardsService } from '../common/protocol/boards-service'; +import { BoardsService, AttachedSerialBoard } from '../common/protocol/boards-service'; import { CoreClientProvider } from './core-client-provider'; import * as path from 'path'; import { ToolOutputServiceServer } from '../common/protocol/tool-output-service'; +import { UploadReq, UploadResp } from './cli-protocol/upload_pb'; @injectable() export class CoreServiceImpl implements CoreService { @@ -32,16 +33,19 @@ export class CoreServiceImpl implements CoreService { const sketchpath = path.dirname(sketchFilePath); const { client, instance } = await this.coreClientProvider.getClient(uri); - // const boards = await this.boardsService.connectedBoards(); - // if (!boards.current) { - // throw new Error(`No selected board. The connected boards were: ${boards.boards}.`); - // } - // https://github.com/cmaglie/arduino-cli/blob/bd5e78701e7546787649d3cca6b21c5d22d0e438/cli/compile/compile.go#L78-L88 + + const currentBoard = await this.boardsService.getSelectBoard(); + if (!currentBoard) { + throw new Error("no board selected"); + } + if (!currentBoard.fqbn) { + throw new Error(`selected board (${currentBoard.name}) has no FQBN`); + } const compilerReq = new CompileReq(); compilerReq.setInstance(instance); compilerReq.setSketchpath(sketchpath); - compilerReq.setFqbn('arduino:avr:uno'/*boards.current.name*/); + compilerReq.setFqbn(currentBoard.fqbn!); compilerReq.setPreprocess(false); compilerReq.setVerbose(true); compilerReq.setQuiet(false); @@ -57,8 +61,43 @@ export class CoreServiceImpl implements CoreService { }); } - upload(): Promise { - throw new Error("Method not implemented."); + async upload(options: CoreService.Upload.Options): Promise { + console.log('upload', options); + const { uri } = options; + const sketchFilePath = await this.fileSystem.getFsPath(options.uri); + if (!sketchFilePath) { + throw new Error(`Cannot resolve filesystem path for URI: ${uri}.`); + } + const sketchpath = path.dirname(sketchFilePath); + + const currentBoard = await this.boardsService.getSelectBoard(); + if (!currentBoard) { + throw new Error("no board selected"); + } + if (!currentBoard.fqbn) { + throw new Error(`selected board (${currentBoard.name}) has no FQBN`); + } + + const { client, instance } = await this.coreClientProvider.getClient(uri); + + const req = new UploadReq(); + req.setInstance(instance); + req.setSketchPath(sketchpath); + req.setFqbn(currentBoard.fqbn); + if (AttachedSerialBoard.is(currentBoard)) { + req.setPort(currentBoard.port); + } else { + throw new Error("can only upload to serial boards"); + } + const result = client.upload(req); + return new Promise((resolve, reject) => { + result.on('data', (cr: UploadResp) => { + this.toolOutputService.publishNewOutput("upload", new Buffer(cr.getOutStream_asU8()).toString()); + this.toolOutputService.publishNewOutput("upload error", new Buffer(cr.getErrStream_asU8()).toString()); + }); + result.on('error', error => reject(error)); + result.on('end', () => resolve()); + }); } }