mirror of
https://github.com/arduino/arduino-ide.git
synced 2025-11-20 07:39:28 +00:00
ATL-374: Refactored the Output services.
Signed-off-by: Akos Kitta <kittaakos@typefox.io>
This commit is contained in:
@@ -9,7 +9,7 @@ import { Event, Emitter } from '@theia/core/lib/common/event';
|
||||
import { environment } from '@theia/application-package/lib/environment';
|
||||
import { EnvVariablesServer } from '@theia/core/lib/common/env-variables';
|
||||
import { BackendApplicationContribution } from '@theia/core/lib/node/backend-application';
|
||||
import { ArduinoDaemon, ArduinoDaemonClient, ToolOutputServiceServer } from '../common/protocol';
|
||||
import { ArduinoDaemon, NotificationServiceServer } from '../common/protocol';
|
||||
import { DaemonLog } from './daemon-log';
|
||||
import { CLI_CONFIG } from './cli-config';
|
||||
import { getExecPath, spawnCommand } from './exec-util';
|
||||
@@ -21,13 +21,12 @@ export class ArduinoDaemonImpl implements ArduinoDaemon, BackendApplicationContr
|
||||
@named('daemon')
|
||||
protected readonly logger: ILogger
|
||||
|
||||
@inject(ToolOutputServiceServer)
|
||||
protected readonly toolOutputService: ToolOutputServiceServer;
|
||||
|
||||
@inject(EnvVariablesServer)
|
||||
protected readonly envVariablesServer: EnvVariablesServer;
|
||||
|
||||
protected readonly clients: Array<ArduinoDaemonClient> = [];
|
||||
@inject(NotificationServiceServer)
|
||||
protected readonly notificationService: NotificationServiceServer;
|
||||
|
||||
protected readonly toDispose = new DisposableCollection();
|
||||
protected readonly onDaemonStartedEmitter = new Emitter<void>();
|
||||
protected readonly onDaemonStoppedEmitter = new Emitter<void>();
|
||||
@@ -42,37 +41,6 @@ export class ArduinoDaemonImpl implements ArduinoDaemon, BackendApplicationContr
|
||||
this.startDaemon();
|
||||
}
|
||||
|
||||
onStop(): void {
|
||||
this.dispose();
|
||||
}
|
||||
|
||||
// JSON-RPC proxy
|
||||
|
||||
setClient(client: ArduinoDaemonClient | undefined): void {
|
||||
if (client) {
|
||||
if (this._running) {
|
||||
client.notifyStarted()
|
||||
} else {
|
||||
client.notifyStopped();
|
||||
}
|
||||
this.clients.push(client);
|
||||
}
|
||||
}
|
||||
|
||||
dispose(): void {
|
||||
this.toDispose.dispose();
|
||||
this.clients.length = 0;
|
||||
}
|
||||
|
||||
disposeClient(client: ArduinoDaemonClient): void {
|
||||
const index = this.clients.indexOf(client);
|
||||
if (index === -1) {
|
||||
this.logger.warn('Could not dispose client. It was not registered or was already disposed.');
|
||||
} else {
|
||||
this.clients.splice(index, 1);
|
||||
}
|
||||
}
|
||||
|
||||
// Daemon API
|
||||
|
||||
async isRunning(): Promise<boolean> {
|
||||
@@ -184,7 +152,7 @@ export class ArduinoDaemonImpl implements ArduinoDaemon, BackendApplicationContr
|
||||
if (code === 0 || signal === 'SIGINT' || signal === 'SIGKILL') {
|
||||
this.onData('Daemon has stopped.');
|
||||
} else {
|
||||
this.onData(`Daemon exited with ${typeof code === 'undefined' ? `signal '${signal}'` : `exit code: ${code}`}.`, { useOutput: false });
|
||||
this.onData(`Daemon exited with ${typeof code === 'undefined' ? `signal '${signal}'` : `exit code: ${code}`}.`);
|
||||
}
|
||||
});
|
||||
daemon.on('error', error => {
|
||||
@@ -198,9 +166,7 @@ export class ArduinoDaemonImpl implements ArduinoDaemon, BackendApplicationContr
|
||||
this._running = true;
|
||||
this._ready.resolve();
|
||||
this.onDaemonStartedEmitter.fire();
|
||||
for (const client of this.clients) {
|
||||
client.notifyStarted();
|
||||
}
|
||||
this.notificationService.notifyDaemonStarted();
|
||||
}
|
||||
|
||||
protected fireDaemonStopped(): void {
|
||||
@@ -211,15 +177,10 @@ export class ArduinoDaemonImpl implements ArduinoDaemon, BackendApplicationContr
|
||||
this._ready.reject(); // Reject all pending.
|
||||
this._ready = new Deferred<void>();
|
||||
this.onDaemonStoppedEmitter.fire();
|
||||
for (const client of this.clients) {
|
||||
client.notifyStopped();
|
||||
}
|
||||
this.notificationService.notifyDaemonStopped();
|
||||
}
|
||||
|
||||
protected onData(message: string, options: { useOutput: boolean } = { useOutput: true }): void {
|
||||
if (options.useOutput) {
|
||||
this.toolOutputService.append({ tool: 'daemon', chunk: DaemonLog.toPrettyString(message) });
|
||||
}
|
||||
protected onData(message: string): void {
|
||||
DaemonLog.log(this.logger, message);
|
||||
}
|
||||
|
||||
|
||||
@@ -5,130 +5,169 @@ import { ContainerModule } from 'inversify';
|
||||
import { ArduinoDaemonImpl } from './arduino-daemon-impl';
|
||||
import { ILogger } from '@theia/core/lib/common/logger';
|
||||
import { BackendApplicationContribution } from '@theia/core/lib/node/backend-application';
|
||||
import { LibraryServiceServerPath, LibraryServiceServer, LibraryServiceClient } from '../common/protocol/library-service';
|
||||
import { BoardsService, BoardsServicePath, BoardsServiceClient } from '../common/protocol/boards-service';
|
||||
import { LibraryServiceServerImpl } from './library-service-server-impl';
|
||||
import { LibraryService, LibraryServicePath } from '../common/protocol/library-service';
|
||||
import { BoardsService, BoardsServicePath } from '../common/protocol/boards-service';
|
||||
import { LibraryServiceImpl } from './library-service-server-impl';
|
||||
import { BoardsServiceImpl } from './boards-service-impl';
|
||||
import { CoreServiceImpl } from './core-service-impl';
|
||||
import { CoreService, CoreServicePath, CoreServiceClient } from '../common/protocol/core-service';
|
||||
import { CoreService, CoreServicePath } from '../common/protocol/core-service';
|
||||
import { ConnectionContainerModule } from '@theia/core/lib/node/messaging/connection-container-module';
|
||||
import { CoreClientProvider } from './core-client-provider';
|
||||
import { ToolOutputService, ToolOutputServiceClient, ToolOutputServiceServer } from '../common/protocol/tool-output-service';
|
||||
import { ConnectionHandler, JsonRpcConnectionHandler } from '@theia/core';
|
||||
import { ToolOutputServiceServerImpl } from './tool-output-service-impl';
|
||||
import { DefaultWorkspaceServerExt } from './default-workspace-server-ext';
|
||||
import { WorkspaceServer } from '@theia/workspace/lib/common';
|
||||
import { ConnectionHandler, JsonRpcConnectionHandler, JsonRpcProxy } from '@theia/core';
|
||||
import { DefaultWorkspaceServer } from './theia/workspace/default-workspace-server';
|
||||
import { WorkspaceServer as TheiaWorkspaceServer } from '@theia/workspace/lib/common';
|
||||
import { SketchesServiceImpl } from './sketches-service-impl';
|
||||
import { SketchesService, SketchesServicePath } from '../common/protocol/sketches-service';
|
||||
import { ConfigService, ConfigServicePath, ConfigServiceClient } from '../common/protocol/config-service';
|
||||
import { ArduinoDaemon, ArduinoDaemonPath, ArduinoDaemonClient } from '../common/protocol/arduino-daemon';
|
||||
import { ConfigService, ConfigServicePath } from '../common/protocol/config-service';
|
||||
import { ArduinoDaemon, ArduinoDaemonPath } from '../common/protocol/arduino-daemon';
|
||||
import { MonitorServiceImpl } from './monitor/monitor-service-impl';
|
||||
import { MonitorService, MonitorServicePath, MonitorServiceClient } from '../common/protocol/monitor-service';
|
||||
import { MonitorClientProvider } from './monitor/monitor-client-provider';
|
||||
import { ConfigServiceImpl } from './config-service-impl';
|
||||
import { ArduinoHostedPluginReader } from './arduino-plugin-reader';
|
||||
import { HostedPluginReader } from '@theia/plugin-ext/lib/hosted/node/plugin-reader';
|
||||
import { HostedPluginReader } from './theia/plugin-ext/plugin-reader';
|
||||
import { HostedPluginReader as TheiaHostedPluginReader } from '@theia/plugin-ext/lib/hosted/node/plugin-reader';
|
||||
import { ConfigFileValidator } from './config-file-validator';
|
||||
import { EnvVariablesServer } from '@theia/core/lib/common/env-variables';
|
||||
import { ArduinoEnvVariablesServer } from './arduino-env-variables-server';
|
||||
import { EnvVariablesServer as TheiaEnvVariablesServer } from '@theia/core/lib/common/env-variables';
|
||||
import { EnvVariablesServer } from './theia/env-variables/env-variables-server';
|
||||
import { NodeFileSystemExt } from './node-filesystem-ext';
|
||||
import { FileSystemExt, FileSystemExtPath } from '../common/protocol/filesystem-ext';
|
||||
import { ExamplesServiceImpl } from './examples-service-impl';
|
||||
import { ExamplesService, ExamplesServicePath } from '../common/protocol/examples-service';
|
||||
import { ExecutableService, ExecutableServicePath } from '../common/protocol/executable-service';
|
||||
import { ExecutableServiceImpl } from './executable-service-impl';
|
||||
import { OutputServicePath, OutputService } from '../common/protocol/output-service';
|
||||
import { NotificationServiceServerImpl } from './notification-service-server';
|
||||
import { NotificationServiceServer, NotificationServiceClient, NotificationServicePath } from '../common/protocol';
|
||||
|
||||
export default new ContainerModule((bind, unbind, isBound, rebind) => {
|
||||
rebind(EnvVariablesServer).to(ArduinoEnvVariablesServer).inSingletonScope();
|
||||
bind(ConfigFileValidator).toSelf().inSingletonScope();
|
||||
// XXX: The config service must start earlier than the daemon, hence the binding order does matter.
|
||||
// Shared config service
|
||||
bind(ConfigFileValidator).toSelf().inSingletonScope();
|
||||
bind(ConfigServiceImpl).toSelf().inSingletonScope();
|
||||
bind(ConfigService).toService(ConfigServiceImpl);
|
||||
// Note: The config service must start earlier than the daemon, hence the binding order of the BA contribution does matter.
|
||||
bind(BackendApplicationContribution).toService(ConfigServiceImpl);
|
||||
bind(ConnectionHandler).toDynamicValue(context =>
|
||||
new JsonRpcConnectionHandler<ConfigServiceClient>(ConfigServicePath, client => {
|
||||
const server = context.container.get<ConfigServiceImpl>(ConfigServiceImpl);
|
||||
server.setClient(client);
|
||||
client.onDidCloseConnection(() => server.disposeClient(client));
|
||||
return server;
|
||||
})
|
||||
).inSingletonScope();
|
||||
bind(ConnectionHandler).toDynamicValue(context => new JsonRpcConnectionHandler(ConfigServicePath, () => context.container.get(ConfigService))).inSingletonScope();
|
||||
|
||||
// Shared daemon
|
||||
bind(ArduinoDaemonImpl).toSelf().inSingletonScope();
|
||||
bind(ArduinoDaemon).toService(ArduinoDaemonImpl);
|
||||
bind(BackendApplicationContribution).toService(ArduinoDaemonImpl);
|
||||
bind(ConnectionHandler).toDynamicValue(context =>
|
||||
new JsonRpcConnectionHandler<ArduinoDaemonClient>(ArduinoDaemonPath, async client => {
|
||||
const server = context.container.get<ArduinoDaemonImpl>(ArduinoDaemonImpl);
|
||||
server.setClient(client);
|
||||
client.onDidCloseConnection(() => server.disposeClient(client));
|
||||
return server;
|
||||
})
|
||||
).inSingletonScope();
|
||||
bind(ConnectionHandler).toDynamicValue(context => new JsonRpcConnectionHandler(ArduinoDaemonPath, () => context.container.get(ArduinoDaemon))).inSingletonScope();
|
||||
|
||||
// Shared examples service
|
||||
bind(ExamplesServiceImpl).toSelf().inSingletonScope();
|
||||
bind(ExamplesService).toService(ExamplesServiceImpl);
|
||||
bind(ConnectionHandler).toDynamicValue(context => new JsonRpcConnectionHandler(ExamplesServicePath, () => context.container.get(ExamplesService))).inSingletonScope();
|
||||
// Examples service. One per backend, each connected FE gets a proxy.
|
||||
bind(ConnectionContainerModule).toConstantValue(ConnectionContainerModule.create(({ bind, bindBackendService }) => {
|
||||
// const ExamplesServiceProxy = Symbol('ExamplesServiceProxy');
|
||||
// bind(ExamplesServiceProxy).toDynamicValue(ctx => new Proxy(ctx.container.get(ExamplesService), {}));
|
||||
// bindBackendService(ExamplesServicePath, ExamplesServiceProxy);
|
||||
bind(ExamplesServiceImpl).toSelf().inSingletonScope();
|
||||
bind(ExamplesService).toService(ExamplesServiceImpl);
|
||||
bindBackendService(ExamplesServicePath, ExamplesService);
|
||||
}));
|
||||
|
||||
// Exposes the executable paths/URIs to the frontend
|
||||
bind(ExecutableServiceImpl).toSelf().inSingletonScope();
|
||||
bind(ExecutableService).toService(ExecutableServiceImpl);
|
||||
bind(ConnectionHandler).toDynamicValue(context => new JsonRpcConnectionHandler(ExecutableServicePath, () => context.container.get(ExecutableService))).inSingletonScope();
|
||||
|
||||
// Library service
|
||||
bind(LibraryServiceServerImpl).toSelf().inSingletonScope();
|
||||
bind(LibraryServiceServer).toService(LibraryServiceServerImpl);
|
||||
bind(ConnectionHandler).toDynamicValue(context =>
|
||||
new JsonRpcConnectionHandler<LibraryServiceClient>(LibraryServiceServerPath, client => {
|
||||
const server = context.container.get<LibraryServiceServerImpl>(LibraryServiceServerImpl);
|
||||
server.setClient(client);
|
||||
client.onDidCloseConnection(() => server.dispose());
|
||||
return server;
|
||||
})
|
||||
).inSingletonScope();
|
||||
// Library service. Singleton per backend, each connected FE gets its proxy.
|
||||
bind(ConnectionContainerModule).toConstantValue(ConnectionContainerModule.create(({ bind, bindBackendService }) => {
|
||||
// const LibraryServiceProxy = Symbol('LibraryServiceProxy');
|
||||
// bind(LibraryServiceProxy).toDynamicValue(ctx => new Proxy(ctx.container.get(LibraryService), {}));
|
||||
// bindBackendService(LibraryServicePath, LibraryServiceProxy);
|
||||
bind(LibraryServiceImpl).toSelf().inSingletonScope();
|
||||
bind(LibraryService).toService(LibraryServiceImpl);
|
||||
bindBackendService(LibraryServicePath, LibraryService);
|
||||
}));
|
||||
|
||||
// Shred sketches service
|
||||
bind(SketchesServiceImpl).toSelf().inSingletonScope();
|
||||
bind(SketchesService).toService(SketchesServiceImpl);
|
||||
bind(ConnectionHandler).toDynamicValue(context => new JsonRpcConnectionHandler(SketchesServicePath, () => context.container.get(SketchesService))).inSingletonScope();
|
||||
|
||||
// Boards service
|
||||
const boardsServiceConnectionModule = ConnectionContainerModule.create(async ({ bind, bindBackendService }) => {
|
||||
// Boards service. One singleton per backend that does the board and port polling. Each connected FE gets its proxy.
|
||||
bind(ConnectionContainerModule).toConstantValue(ConnectionContainerModule.create(({ bind, bindBackendService }) => {
|
||||
// const BoardsServiceProxy = Symbol('BoardsServiceProxy');
|
||||
// bind(BoardsServiceProxy).toDynamicValue(ctx => new Proxy(ctx.container.get(BoardsService), {}));
|
||||
// bindBackendService(BoardsServicePath, BoardsServiceProxy);
|
||||
bind(BoardsServiceImpl).toSelf().inSingletonScope();
|
||||
bind(BoardsService).toService(BoardsServiceImpl);
|
||||
bindBackendService<BoardsService, BoardsServiceClient>(BoardsServicePath, BoardsService, (service, client) => {
|
||||
service.setClient(client);
|
||||
bindBackendService<BoardsServiceImpl, JsonRpcProxy<object>>(BoardsServicePath, BoardsService, (service, client) => {
|
||||
client.onDidCloseConnection(() => service.dispose());
|
||||
return service;
|
||||
});
|
||||
});
|
||||
bind(ConnectionContainerModule).toConstantValue(boardsServiceConnectionModule);
|
||||
}));
|
||||
|
||||
// Shared Arduino core client provider service for the backend.
|
||||
bind(CoreClientProvider).toSelf().inSingletonScope();
|
||||
|
||||
// Core service -> `verify` and `upload`. One per Theia connection.
|
||||
const connectionConnectionModule = ConnectionContainerModule.create(({ bind, bindBackendService }) => {
|
||||
// Core service -> `verify` and `upload`. Singleton per BE, each FE connection gets its proxy.
|
||||
bind(ConnectionContainerModule).toConstantValue(ConnectionContainerModule.create(({ bind, bindBackendService }) => {
|
||||
// const CoreServiceProxy = Symbol('CoreServiceProxy');
|
||||
// bind(CoreServiceProxy).toDynamicValue(ctx => new Proxy(ctx.container.get(CoreService), {}));
|
||||
// bindBackendService(CoreServicePath, CoreServiceProxy);
|
||||
bind(CoreServiceImpl).toSelf().inSingletonScope();
|
||||
bind(CoreService).toService(CoreServiceImpl);
|
||||
bindBackendService(BoardsServicePath, BoardsService);
|
||||
bindBackendService<CoreService, CoreServiceClient>(CoreServicePath, CoreService, (service, client) => {
|
||||
bindBackendService(CoreServicePath, CoreService);
|
||||
}));
|
||||
|
||||
// #region Theia customizations
|
||||
|
||||
bind(DefaultWorkspaceServer).toSelf().inSingletonScope();
|
||||
rebind(TheiaWorkspaceServer).toService(DefaultWorkspaceServer);
|
||||
|
||||
bind(EnvVariablesServer).toSelf().inSingletonScope();
|
||||
rebind(TheiaEnvVariablesServer).toService(EnvVariablesServer);
|
||||
|
||||
bind(HostedPluginReader).toSelf().inSingletonScope();
|
||||
rebind(TheiaHostedPluginReader).toService(HostedPluginReader);
|
||||
|
||||
// #endregion Theia customizations
|
||||
|
||||
// Shared monitor client provider service for the backend.
|
||||
bind(MonitorClientProvider).toSelf().inSingletonScope();
|
||||
bind(MonitorServiceImpl).toSelf().inSingletonScope();
|
||||
bind(MonitorService).toService(MonitorServiceImpl);
|
||||
bind(ConnectionContainerModule).toConstantValue(ConnectionContainerModule.create(({ bind, bindBackendService }) => {
|
||||
const MonitorServiceProxy = Symbol('MonitorServiceProxy');
|
||||
bind(MonitorServiceProxy).toDynamicValue(ctx => new Proxy(ctx.container.get(MonitorService), {}));
|
||||
bindBackendService<MonitorService, MonitorServiceClient>(MonitorServicePath, MonitorServiceProxy, (service, client) => {
|
||||
service.setClient(client);
|
||||
client.onDidCloseConnection(() => service.dispose());
|
||||
return service;
|
||||
});
|
||||
});
|
||||
bind(ConnectionContainerModule).toConstantValue(connectionConnectionModule);
|
||||
}));
|
||||
|
||||
// Tool output service -> feedback from the daemon, compile and flash
|
||||
bind(ToolOutputServiceServerImpl).toSelf().inSingletonScope();
|
||||
bind(ToolOutputServiceServer).toService(ToolOutputServiceServerImpl);
|
||||
// Set up cpp extension
|
||||
if (!process.env.CPP_CLANGD_COMMAND) {
|
||||
const segments = ['..', '..', 'build'];
|
||||
if (os.platform() === 'win32') {
|
||||
segments.push('clangd.exe');
|
||||
} else {
|
||||
segments.push('bin');
|
||||
segments.push('clangd');
|
||||
}
|
||||
const clangdCommand = join(__dirname, ...segments);
|
||||
if (fs.existsSync(clangdCommand)) {
|
||||
process.env.CPP_CLANGD_COMMAND = clangdCommand;
|
||||
}
|
||||
}
|
||||
|
||||
// File-system extension for mapping paths to URIs
|
||||
bind(NodeFileSystemExt).toSelf().inSingletonScope();
|
||||
bind(FileSystemExt).toService(NodeFileSystemExt);
|
||||
bind(ConnectionHandler).toDynamicValue(context => new JsonRpcConnectionHandler(FileSystemExtPath, () => context.container.get(FileSystemExt))).inSingletonScope();
|
||||
|
||||
// Output service per connection.
|
||||
bind(ConnectionContainerModule).toConstantValue(ConnectionContainerModule.create(({ bindFrontendService }) => {
|
||||
bindFrontendService(OutputServicePath, OutputService);
|
||||
}));
|
||||
|
||||
// Notify all connected frontend instances
|
||||
bind(NotificationServiceServerImpl).toSelf().inSingletonScope();
|
||||
bind(NotificationServiceServer).toService(NotificationServiceServerImpl);
|
||||
bind(ConnectionHandler).toDynamicValue(context =>
|
||||
new JsonRpcConnectionHandler<ToolOutputServiceClient>(ToolOutputService.SERVICE_PATH, client => {
|
||||
const server = context.container.get<ToolOutputServiceServer>(ToolOutputServiceServer);
|
||||
new JsonRpcConnectionHandler<NotificationServiceClient>(NotificationServicePath, client => {
|
||||
const server = context.container.get<NotificationServiceServer>(NotificationServiceServer);
|
||||
server.setClient(client);
|
||||
client.onDidCloseConnection(() => server.disposeClient(client));
|
||||
return server;
|
||||
@@ -153,52 +192,10 @@ export default new ContainerModule((bind, unbind, isBound, rebind) => {
|
||||
return parentLogger.child('config');
|
||||
}).inSingletonScope().whenTargetNamed('config');
|
||||
|
||||
// Default workspace server extension to initialize and use a fallback workspace.
|
||||
// If nothing was set previously.
|
||||
bind(DefaultWorkspaceServerExt).toSelf().inSingletonScope();
|
||||
rebind(WorkspaceServer).toService(DefaultWorkspaceServerExt);
|
||||
|
||||
// Shared monitor client provider service for the backend.
|
||||
bind(MonitorClientProvider).toSelf().inSingletonScope();
|
||||
|
||||
// Connection scoped service for the serial monitor.
|
||||
const monitorServiceConnectionModule = ConnectionContainerModule.create(({ bind, bindBackendService }) => {
|
||||
bind(MonitorServiceImpl).toSelf().inSingletonScope();
|
||||
bind(MonitorService).toService(MonitorServiceImpl);
|
||||
bindBackendService<MonitorService, MonitorServiceClient>(MonitorServicePath, MonitorService, (service, client) => {
|
||||
service.setClient(client);
|
||||
client.onDidCloseConnection(() => service.dispose());
|
||||
return service;
|
||||
});
|
||||
});
|
||||
bind(ConnectionContainerModule).toConstantValue(monitorServiceConnectionModule);
|
||||
|
||||
// Logger for the monitor service.
|
||||
bind(ILogger).toDynamicValue(ctx => {
|
||||
const parentLogger = ctx.container.get<ILogger>(ILogger);
|
||||
return parentLogger.child('monitor-service');
|
||||
}).inSingletonScope().whenTargetNamed('monitor-service');
|
||||
|
||||
// Set up cpp extension
|
||||
if (!process.env.CPP_CLANGD_COMMAND) {
|
||||
const segments = ['..', '..', 'build'];
|
||||
if (os.platform() === 'win32') {
|
||||
segments.push('clangd.exe');
|
||||
} else {
|
||||
segments.push('bin');
|
||||
segments.push('clangd');
|
||||
}
|
||||
const clangdCommand = join(__dirname, ...segments);
|
||||
if (fs.existsSync(clangdCommand)) {
|
||||
process.env.CPP_CLANGD_COMMAND = clangdCommand;
|
||||
}
|
||||
}
|
||||
|
||||
bind(ArduinoHostedPluginReader).toSelf().inSingletonScope();
|
||||
rebind(HostedPluginReader).toService(ArduinoHostedPluginReader);
|
||||
|
||||
// File-system extension for mapping paths to URIs
|
||||
bind(NodeFileSystemExt).toSelf().inSingletonScope();
|
||||
bind(FileSystemExt).toService(NodeFileSystemExt);
|
||||
bind(ConnectionHandler).toDynamicValue(context => new JsonRpcConnectionHandler(FileSystemExtPath, () => context.container.get(FileSystemExt))).inSingletonScope();
|
||||
});
|
||||
|
||||
@@ -1,19 +1,22 @@
|
||||
import { injectable, inject, postConstruct, named } from 'inversify';
|
||||
import { ILogger } from '@theia/core/lib/common/logger';
|
||||
import { Deferred } from '@theia/core/lib/common/promise-util';
|
||||
import { BoardsService, BoardsPackage, Board, BoardsServiceClient, Port, BoardDetails, Tool, ConfigOption, ConfigValue, Programmer } from '../common/protocol';
|
||||
import {
|
||||
BoardsService,
|
||||
BoardsPackage, Board, Port, BoardDetails, Tool, ConfigOption, ConfigValue, Programmer, OutputService, NotificationServiceServer, AttachedBoardsChangeEvent
|
||||
} from '../common/protocol';
|
||||
import {
|
||||
PlatformSearchReq, PlatformSearchResp, PlatformInstallReq, PlatformInstallResp, PlatformListReq,
|
||||
PlatformListResp, Platform, PlatformUninstallResp, PlatformUninstallReq
|
||||
} from './cli-protocol/commands/core_pb';
|
||||
import { CoreClientProvider } from './core-client-provider';
|
||||
import { BoardListReq, BoardListResp, BoardDetailsReq, BoardDetailsResp } from './cli-protocol/commands/board_pb';
|
||||
import { ToolOutputServiceServer } from '../common/protocol/tool-output-service';
|
||||
import { Installable } from '../common/protocol/installable';
|
||||
import { ListProgrammersAvailableForUploadReq, ListProgrammersAvailableForUploadResp } from './cli-protocol/commands/upload_pb';
|
||||
import { Disposable } from '@theia/core/lib/common/disposable';
|
||||
|
||||
@injectable()
|
||||
export class BoardsServiceImpl implements BoardsService {
|
||||
export class BoardsServiceImpl implements BoardsService, Disposable {
|
||||
|
||||
@inject(ILogger)
|
||||
protected logger: ILogger;
|
||||
@@ -25,8 +28,11 @@ export class BoardsServiceImpl implements BoardsService {
|
||||
@inject(CoreClientProvider)
|
||||
protected readonly coreClientProvider: CoreClientProvider;
|
||||
|
||||
@inject(ToolOutputServiceServer)
|
||||
protected readonly toolOutputService: ToolOutputServiceServer;
|
||||
@inject(OutputService)
|
||||
protected readonly outputService: OutputService;
|
||||
|
||||
@inject(NotificationServiceServer)
|
||||
protected readonly notificationService: NotificationServiceServer;
|
||||
|
||||
protected discoveryInitialized = false;
|
||||
protected discoveryTimer: NodeJS.Timer | undefined;
|
||||
@@ -38,7 +44,6 @@ export class BoardsServiceImpl implements BoardsService {
|
||||
protected attachedBoards: Board[] = [];
|
||||
protected availablePorts: Port[] = [];
|
||||
protected started = new Deferred<void>();
|
||||
protected client: BoardsServiceClient | undefined;
|
||||
|
||||
@postConstruct()
|
||||
protected async init(): Promise<void> {
|
||||
@@ -49,19 +54,19 @@ export class BoardsServiceImpl implements BoardsService {
|
||||
const update = (oldBoards: Board[], newBoards: Board[], oldPorts: Port[], newPorts: Port[], message: string) => {
|
||||
this.attachedBoards = newBoards;
|
||||
this.availablePorts = newPorts;
|
||||
this.discoveryLogger.info(`${message} - Discovered boards: ${JSON.stringify(newBoards)} and available ports: ${JSON.stringify(newPorts)}`);
|
||||
if (this.client) {
|
||||
this.client.notifyAttachedBoardsChanged({
|
||||
oldState: {
|
||||
boards: oldBoards,
|
||||
ports: oldPorts
|
||||
},
|
||||
newState: {
|
||||
boards: newBoards,
|
||||
ports: newPorts
|
||||
}
|
||||
});
|
||||
}
|
||||
const event = {
|
||||
oldState: {
|
||||
boards: oldBoards,
|
||||
ports: oldPorts
|
||||
},
|
||||
newState: {
|
||||
boards: newBoards,
|
||||
ports: newPorts
|
||||
}
|
||||
};
|
||||
this.discoveryLogger.info(`${message}`);
|
||||
this.discoveryLogger.info(`${AttachedBoardsChangeEvent.toString(event)}`);
|
||||
this.notificationService.notifyAttachedBoardsChanged(event);
|
||||
}
|
||||
const sortedBoards = boards.sort(Board.compare);
|
||||
const sortedPorts = ports.sort(Port.compare);
|
||||
@@ -103,16 +108,13 @@ export class BoardsServiceImpl implements BoardsService {
|
||||
}, 1000);
|
||||
}
|
||||
|
||||
setClient(client: BoardsServiceClient | undefined): void {
|
||||
this.client = client;
|
||||
}
|
||||
|
||||
dispose(): void {
|
||||
this.logger.info('>>> Disposing boards service...');
|
||||
if (this.discoveryTimer !== undefined) {
|
||||
this.logger.info('>>> Disposing the boards discovery...');
|
||||
clearInterval(this.discoveryTimer);
|
||||
this.logger.info('<<< Disposed the boards discovery.');
|
||||
}
|
||||
this.client = undefined;
|
||||
this.logger.info('<<< Disposed boards service.');
|
||||
}
|
||||
|
||||
@@ -213,14 +215,28 @@ export class BoardsServiceImpl implements BoardsService {
|
||||
const detailsReq = new BoardDetailsReq();
|
||||
detailsReq.setInstance(instance);
|
||||
detailsReq.setFqbn(fqbn);
|
||||
const detailsResp = await new Promise<BoardDetailsResp>((resolve, reject) => client.boardDetails(detailsReq, (err, resp) => {
|
||||
const detailsResp = await new Promise<BoardDetailsResp | undefined>((resolve, reject) => client.boardDetails(detailsReq, (err, resp) => {
|
||||
if (err) {
|
||||
// Required cores are not installed manually: https://github.com/arduino/arduino-cli/issues/954
|
||||
if (err.message.indexOf('missing platform release') !== -1 && err.message.indexOf('referenced by board') !== -1) {
|
||||
resolve(undefined);
|
||||
return;
|
||||
}
|
||||
reject(err);
|
||||
return;
|
||||
}
|
||||
resolve(resp);
|
||||
}));
|
||||
|
||||
if (!detailsResp) {
|
||||
return {
|
||||
fqbn,
|
||||
configOptions: [],
|
||||
programmers: [],
|
||||
requiredTools: []
|
||||
};
|
||||
}
|
||||
|
||||
const requiredTools = detailsResp.getToolsdependenciesList().map(t => <Tool>{
|
||||
name: t.getName(),
|
||||
packager: t.getPackager(),
|
||||
@@ -391,18 +407,17 @@ export class BoardsServiceImpl implements BoardsService {
|
||||
resp.on('data', (r: PlatformInstallResp) => {
|
||||
const prog = r.getProgress();
|
||||
if (prog && prog.getFile()) {
|
||||
this.toolOutputService.append({ tool: 'board download', chunk: `downloading ${prog.getFile()}\n` });
|
||||
this.outputService.append({ name: 'board download', chunk: `downloading ${prog.getFile()}\n` });
|
||||
}
|
||||
});
|
||||
await new Promise<void>((resolve, reject) => {
|
||||
resp.on('end', resolve);
|
||||
resp.on('error', reject);
|
||||
});
|
||||
if (this.client) {
|
||||
const items = await this.search({});
|
||||
const updated = items.find(other => BoardsPackage.equals(other, item)) || item;
|
||||
this.client.notifyInstalled({ item: updated });
|
||||
}
|
||||
|
||||
const items = await this.search({});
|
||||
const updated = items.find(other => BoardsPackage.equals(other, item)) || item;
|
||||
this.notificationService.notifyPlatformInstalled({ item: updated });
|
||||
console.info('<<< Boards package installation done.', item);
|
||||
}
|
||||
|
||||
@@ -426,7 +441,7 @@ export class BoardsServiceImpl implements BoardsService {
|
||||
const resp = client.platformUninstall(req);
|
||||
resp.on('data', (_: PlatformUninstallResp) => {
|
||||
if (!logged) {
|
||||
this.toolOutputService.append({ tool: 'board uninstall', chunk: `uninstalling ${item.id}\n` });
|
||||
this.outputService.append({ name: 'board uninstall', chunk: `uninstalling ${item.id}\n` });
|
||||
logged = true;
|
||||
}
|
||||
})
|
||||
@@ -434,10 +449,9 @@ export class BoardsServiceImpl implements BoardsService {
|
||||
resp.on('end', resolve);
|
||||
resp.on('error', reject);
|
||||
});
|
||||
if (this.client) {
|
||||
// Here, unlike at `install` we send out the argument `item`. Otherwise, we would not know about the board FQBN.
|
||||
this.client.notifyUninstalled({ item });
|
||||
}
|
||||
|
||||
// Here, unlike at `install` we send out the argument `item`. Otherwise, we would not know about the board FQBN.
|
||||
this.notificationService.notifyPlatformUninstalled({ item });
|
||||
console.info('<<< Boards package uninstallation done.', item);
|
||||
}
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@ import { ILogger } from '@theia/core/lib/common/logger';
|
||||
import { FileUri } from '@theia/core/lib/node/file-uri';
|
||||
import { Event, Emitter } from '@theia/core/lib/common/event';
|
||||
import { BackendApplicationContribution } from '@theia/core/lib/node/backend-application';
|
||||
import { ConfigService, Config, ConfigServiceClient } from '../common/protocol';
|
||||
import { ConfigService, Config, NotificationServiceServer } from '../common/protocol';
|
||||
import * as fs from './fs-extra';
|
||||
import { spawnCommand } from './exec-util';
|
||||
import { RawData } from './cli-protocol/settings/settings_pb';
|
||||
@@ -38,10 +38,12 @@ export class ConfigServiceImpl implements BackendApplicationContribution, Config
|
||||
@inject(ArduinoDaemonImpl)
|
||||
protected readonly daemon: ArduinoDaemonImpl;
|
||||
|
||||
@inject(NotificationServiceServer)
|
||||
protected readonly notificationService: NotificationServiceServer;
|
||||
|
||||
protected updating = false;
|
||||
protected config: Config;
|
||||
protected cliConfig: DefaultCliConfig | undefined;
|
||||
protected clients: Array<ConfigServiceClient> = [];
|
||||
protected ready = new Deferred<void>();
|
||||
protected readonly configChangeEmitter = new Emitter<Config>();
|
||||
|
||||
@@ -94,25 +96,6 @@ export class ConfigServiceImpl implements BackendApplicationContribution, Config
|
||||
return this.getConfiguration().then(({ sketchDirUri }) => new URI(sketchDirUri).isEqualOrParent(new URI(uri)));
|
||||
}
|
||||
|
||||
setClient(client: ConfigServiceClient | undefined): void {
|
||||
if (client) {
|
||||
this.clients.push(client);
|
||||
}
|
||||
}
|
||||
|
||||
dispose(): void {
|
||||
this.clients.length = 0;
|
||||
}
|
||||
|
||||
disposeClient(client: ConfigServiceClient): void {
|
||||
const index = this.clients.indexOf(client);
|
||||
if (index === -1) {
|
||||
this.logger.warn('Could not dispose client. It was not registered or was already disposed.');
|
||||
} else {
|
||||
this.clients.splice(index, 1);
|
||||
}
|
||||
}
|
||||
|
||||
protected async loadCliConfig(): Promise<DefaultCliConfig | undefined> {
|
||||
const cliConfigFileUri = await this.getCliConfigFileUri();
|
||||
const cliConfigPath = FileUri.fsPath(cliConfigFileUri);
|
||||
@@ -214,9 +197,7 @@ export class ConfigServiceImpl implements BackendApplicationContribution, Config
|
||||
this.cliConfig = cliConfig;
|
||||
this.config = config;
|
||||
this.configChangeEmitter.fire(this.config);
|
||||
for (const client of this.clients) {
|
||||
client.notifyConfigChanged(this.config);
|
||||
}
|
||||
this.notificationService.notifyConfigChanged({ config: this.config });
|
||||
}).finally(() => this.updating = false);
|
||||
} catch (err) {
|
||||
this.logger.error('Failed to update the daemon with the current CLI configuration.', err);
|
||||
@@ -227,15 +208,11 @@ export class ConfigServiceImpl implements BackendApplicationContribution, Config
|
||||
}
|
||||
|
||||
protected fireConfigChanged(config: Config): void {
|
||||
for (const client of this.clients) {
|
||||
client.notifyConfigChanged(config);
|
||||
}
|
||||
this.notificationService.notifyConfigChanged({ config });
|
||||
}
|
||||
|
||||
protected fireInvalidConfig(): void {
|
||||
for (const client of this.clients) {
|
||||
client.notifyInvalidConfig();
|
||||
}
|
||||
this.notificationService.notifyConfigChanged({ config: undefined });
|
||||
}
|
||||
|
||||
protected async unwatchCliConfig(): Promise<void> {
|
||||
|
||||
@@ -1,26 +1,21 @@
|
||||
import * as grpc from '@grpc/grpc-js';
|
||||
import { inject, injectable } from 'inversify';
|
||||
import { Event, Emitter } from '@theia/core/lib/common/event';
|
||||
import { ToolOutputServiceServer } from '../common/protocol';
|
||||
import { GrpcClientProvider } from './grpc-client-provider';
|
||||
import { ArduinoCoreClient } from './cli-protocol/commands/commands_grpc_pb';
|
||||
import * as commandsGrpcPb from './cli-protocol/commands/commands_grpc_pb';
|
||||
import { Instance } from './cli-protocol/commands/common_pb';
|
||||
import { InitReq, InitResp, UpdateIndexReq, UpdateIndexResp, UpdateLibrariesIndexResp, UpdateLibrariesIndexReq } from './cli-protocol/commands/commands_pb';
|
||||
import { NotificationServiceServer } from '../common/protocol';
|
||||
|
||||
@injectable()
|
||||
export class CoreClientProvider extends GrpcClientProvider<CoreClientProvider.Client> {
|
||||
|
||||
@inject(ToolOutputServiceServer)
|
||||
protected readonly toolOutputService: ToolOutputServiceServer;
|
||||
@inject(NotificationServiceServer)
|
||||
protected readonly notificationService: NotificationServiceServer;
|
||||
|
||||
protected readonly onIndexUpdatedEmitter = new Emitter<void>();
|
||||
protected readonly onClientReadyEmitter = new Emitter<void>();
|
||||
|
||||
get onIndexUpdated(): Event<void> {
|
||||
return this.onIndexUpdatedEmitter.event;
|
||||
}
|
||||
|
||||
get onClientReady(): Event<void> {
|
||||
return this.onClientReadyEmitter.event;
|
||||
}
|
||||
@@ -76,11 +71,11 @@ export class CoreClientProvider extends GrpcClientProvider<CoreClientProvider.Cl
|
||||
indexUpdateSucceeded = true;
|
||||
break;
|
||||
} catch (e) {
|
||||
this.toolOutputService.append({ tool: 'daemon', chunk: `Error while updating index in attempt ${i}: ${e}`, severity: 'error' });
|
||||
console.error(`Error while updating index in attempt ${i}.`, e);
|
||||
}
|
||||
}
|
||||
if (!indexUpdateSucceeded) {
|
||||
this.toolOutputService.append({ tool: 'daemon', chunk: 'Was unable to update the index. Please restart to try again.', severity: 'error' });
|
||||
console.error('Could not update the index. Please restart to try again.');
|
||||
}
|
||||
|
||||
let libIndexUpdateSucceeded = true;
|
||||
@@ -90,15 +85,15 @@ export class CoreClientProvider extends GrpcClientProvider<CoreClientProvider.Cl
|
||||
libIndexUpdateSucceeded = true;
|
||||
break;
|
||||
} catch (e) {
|
||||
this.toolOutputService.append({ tool: 'daemon', chunk: `Error while updating library index in attempt ${i}: ${e}`, severity: 'error' });
|
||||
console.error(`Error while updating library index in attempt ${i}.`, e);
|
||||
}
|
||||
}
|
||||
if (!libIndexUpdateSucceeded) {
|
||||
this.toolOutputService.append({ tool: 'daemon', chunk: `Was unable to update the library index. Please restart to try again.`, severity: 'error' });
|
||||
console.error('Could not update the library index. Please restart to try again.');
|
||||
}
|
||||
|
||||
if (indexUpdateSucceeded && libIndexUpdateSucceeded) {
|
||||
this.onIndexUpdatedEmitter.fire();
|
||||
this.notificationService.notifyIndexUpdated();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -116,12 +111,12 @@ export class CoreClientProvider extends GrpcClientProvider<CoreClientProvider.Cl
|
||||
if (progress.getCompleted()) {
|
||||
if (file) {
|
||||
if (/\s/.test(file)) {
|
||||
this.toolOutputService.append({ tool: 'daemon', chunk: `${file} completed.\n` });
|
||||
console.log(`${file} completed.`);
|
||||
} else {
|
||||
this.toolOutputService.append({ tool: 'daemon', chunk: `Download of '${file}' completed.\n'` });
|
||||
console.log(`Download of '${file}' completed.`);
|
||||
}
|
||||
} else {
|
||||
this.toolOutputService.append({ tool: 'daemon', chunk: `The library index has been successfully updated.\n'` });
|
||||
console.log('The library index has been successfully updated.');
|
||||
}
|
||||
file = undefined;
|
||||
}
|
||||
@@ -147,12 +142,12 @@ export class CoreClientProvider extends GrpcClientProvider<CoreClientProvider.Cl
|
||||
if (progress.getCompleted()) {
|
||||
if (file) {
|
||||
if (/\s/.test(file)) {
|
||||
this.toolOutputService.append({ tool: 'daemon', chunk: `${file} completed.\n` });
|
||||
console.log(`${file} completed.`);
|
||||
} else {
|
||||
this.toolOutputService.append({ tool: 'daemon', chunk: `Download of '${file}' completed.\n'` });
|
||||
console.log(`Download of '${file}' completed.`);
|
||||
}
|
||||
} else {
|
||||
this.toolOutputService.append({ tool: 'daemon', chunk: `The index has been successfully updated.\n'` });
|
||||
console.log('The index has been successfully updated.');
|
||||
}
|
||||
file = undefined;
|
||||
}
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
import { inject, injectable, postConstruct } from 'inversify';
|
||||
import { FileUri } from '@theia/core/lib/node/file-uri';
|
||||
import { CoreService, CoreServiceClient } from '../common/protocol/core-service';
|
||||
import { inject, injectable } from 'inversify';
|
||||
import { dirname } from 'path';
|
||||
import { CoreService } from '../common/protocol/core-service';
|
||||
import { CompileReq, CompileResp } from './cli-protocol/commands/compile_pb';
|
||||
import { BoardsService } 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, BurnBootloaderReq, BurnBootloaderResp } from './cli-protocol/commands/upload_pb';
|
||||
import { OutputService } from '../common/protocol/output-service';
|
||||
import { NotificationServiceServer } from '../common/protocol';
|
||||
|
||||
@injectable()
|
||||
export class CoreServiceImpl implements CoreService {
|
||||
@@ -14,29 +14,17 @@ export class CoreServiceImpl implements CoreService {
|
||||
@inject(CoreClientProvider)
|
||||
protected readonly coreClientProvider: CoreClientProvider;
|
||||
|
||||
@inject(OutputService)
|
||||
protected readonly outputService: OutputService;
|
||||
|
||||
@inject(BoardsService)
|
||||
protected readonly boardsService: BoardsService;
|
||||
|
||||
@inject(ToolOutputServiceServer)
|
||||
protected readonly toolOutputService: ToolOutputServiceServer;
|
||||
|
||||
protected client: CoreServiceClient | undefined;
|
||||
|
||||
@postConstruct()
|
||||
protected init(): void {
|
||||
this.coreClientProvider.onIndexUpdated(() => {
|
||||
if (this.client) {
|
||||
this.client.notifyIndexUpdated();
|
||||
}
|
||||
})
|
||||
}
|
||||
@inject(NotificationServiceServer)
|
||||
protected readonly notificationService: NotificationServiceServer;
|
||||
|
||||
async compile(options: CoreService.Compile.Options): Promise<void> {
|
||||
this.toolOutputService.append({ tool: 'compile', chunk: 'Compiling...\n' + JSON.stringify(options, null, 2) + '\n--------------------------\n' });
|
||||
this.outputService.append({ name: 'compile', chunk: 'Compiling...\n' + JSON.stringify(options, null, 2) + '\n--------------------------\n' });
|
||||
const { sketchUri, fqbn } = options;
|
||||
const sketchFilePath = FileUri.fsPath(sketchUri);
|
||||
const sketchpath = path.dirname(sketchFilePath);
|
||||
const sketchpath = dirname(sketchFilePath);
|
||||
|
||||
const coreClient = await this.coreClientProvider.client();
|
||||
if (!coreClient) {
|
||||
@@ -61,25 +49,25 @@ export class CoreServiceImpl implements CoreService {
|
||||
try {
|
||||
await new Promise<void>((resolve, reject) => {
|
||||
result.on('data', (cr: CompileResp) => {
|
||||
this.toolOutputService.append({ tool: 'compile', chunk: Buffer.from(cr.getOutStream_asU8()).toString() });
|
||||
this.toolOutputService.append({ tool: 'compile', chunk: Buffer.from(cr.getErrStream_asU8()).toString() });
|
||||
this.outputService.append({ name: 'compile', chunk: Buffer.from(cr.getOutStream_asU8()).toString() });
|
||||
this.outputService.append({ name: 'compile', chunk: Buffer.from(cr.getErrStream_asU8()).toString() });
|
||||
});
|
||||
result.on('error', error => reject(error));
|
||||
result.on('end', () => resolve());
|
||||
});
|
||||
this.toolOutputService.append({ tool: 'compile', chunk: '\n--------------------------\nCompilation complete.\n' });
|
||||
this.outputService.append({ name: 'compile', chunk: '\n--------------------------\nCompilation complete.\n' });
|
||||
} catch (e) {
|
||||
this.toolOutputService.append({ tool: 'compile', chunk: `Compilation error: ${e}\n`, severity: 'error' });
|
||||
this.outputService.append({ name: 'compile', chunk: `Compilation error: ${e}\n`, severity: 'error' });
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
async upload(options: CoreService.Upload.Options): Promise<void> {
|
||||
await this.compile(options);
|
||||
this.toolOutputService.append({ tool: 'upload', chunk: 'Uploading...\n' + JSON.stringify(options, null, 2) + '\n--------------------------\n' });
|
||||
this.outputService.append({ name: 'upload', chunk: 'Uploading...\n' + JSON.stringify(options, null, 2) + '\n--------------------------\n' });
|
||||
const { sketchUri, fqbn } = options;
|
||||
const sketchFilePath = FileUri.fsPath(sketchUri);
|
||||
const sketchpath = path.dirname(sketchFilePath);
|
||||
const sketchpath = dirname(sketchFilePath);
|
||||
|
||||
const coreClient = await this.coreClientProvider.client();
|
||||
if (!coreClient) {
|
||||
@@ -106,15 +94,15 @@ export class CoreServiceImpl implements CoreService {
|
||||
try {
|
||||
await new Promise<void>((resolve, reject) => {
|
||||
result.on('data', (resp: UploadResp) => {
|
||||
this.toolOutputService.append({ tool: 'upload', chunk: Buffer.from(resp.getOutStream_asU8()).toString() });
|
||||
this.toolOutputService.append({ tool: 'upload', chunk: Buffer.from(resp.getErrStream_asU8()).toString() });
|
||||
this.outputService.append({ name: 'upload', chunk: Buffer.from(resp.getOutStream_asU8()).toString() });
|
||||
this.outputService.append({ name: 'upload', chunk: Buffer.from(resp.getErrStream_asU8()).toString() });
|
||||
});
|
||||
result.on('error', error => reject(error));
|
||||
result.on('end', () => resolve());
|
||||
});
|
||||
this.toolOutputService.append({ tool: 'upload', chunk: '\n--------------------------\nUpload complete.\n' });
|
||||
this.outputService.append({ name: 'upload', chunk: '\n--------------------------\nUpload complete.\n' });
|
||||
} catch (e) {
|
||||
this.toolOutputService.append({ tool: 'upload', chunk: `Upload error: ${e}\n`, severity: 'error' });
|
||||
this.outputService.append({ name: 'upload', chunk: `Upload error: ${e}\n`, severity: 'error' });
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
@@ -141,24 +129,16 @@ export class CoreServiceImpl implements CoreService {
|
||||
try {
|
||||
await new Promise<void>((resolve, reject) => {
|
||||
result.on('data', (resp: BurnBootloaderResp) => {
|
||||
this.toolOutputService.append({ tool: 'bootloader', chunk: Buffer.from(resp.getOutStream_asU8()).toString() });
|
||||
this.toolOutputService.append({ tool: 'bootloader', chunk: Buffer.from(resp.getErrStream_asU8()).toString() });
|
||||
this.outputService.append({ name: 'bootloader', chunk: Buffer.from(resp.getOutStream_asU8()).toString() });
|
||||
this.outputService.append({ name: 'bootloader', chunk: Buffer.from(resp.getErrStream_asU8()).toString() });
|
||||
});
|
||||
result.on('error', error => reject(error));
|
||||
result.on('end', () => resolve());
|
||||
});
|
||||
} catch (e) {
|
||||
this.toolOutputService.append({ tool: 'bootloader', chunk: `Error while burning the bootloader: ${e}\n`, severity: 'error' });
|
||||
this.outputService.append({ name: 'bootloader', chunk: `Error while burning the bootloader: ${e}\n`, severity: 'error' });
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
setClient(client: CoreServiceClient | undefined): void {
|
||||
this.client = client;
|
||||
}
|
||||
|
||||
dispose(): void {
|
||||
this.client = undefined;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@ import { notEmpty } from '@theia/core/lib/common/objects';
|
||||
import { Sketch } from '../common/protocol/sketches-service';
|
||||
import { SketchesServiceImpl } from './sketches-service-impl';
|
||||
import { ExamplesService, ExampleContainer } from '../common/protocol/examples-service';
|
||||
import { LibraryServiceServer, LibraryLocation, LibraryPackage } from '../common/protocol';
|
||||
import { LibraryLocation, LibraryPackage, LibraryService } from '../common/protocol';
|
||||
import { ConfigServiceImpl } from './config-service-impl';
|
||||
|
||||
@injectable()
|
||||
@@ -15,8 +15,8 @@ export class ExamplesServiceImpl implements ExamplesService {
|
||||
@inject(SketchesServiceImpl)
|
||||
protected readonly sketchesService: SketchesServiceImpl;
|
||||
|
||||
@inject(LibraryServiceServer)
|
||||
protected readonly libraryService: LibraryServiceServer;
|
||||
@inject(LibraryService)
|
||||
protected readonly libraryService: LibraryService;
|
||||
|
||||
@inject(ConfigServiceImpl)
|
||||
protected readonly configService: ConfigServiceImpl;
|
||||
@@ -44,7 +44,7 @@ export class ExamplesServiceImpl implements ExamplesService {
|
||||
const current: ExampleContainer[] = [];
|
||||
const any: ExampleContainer[] = [];
|
||||
if (fqbn) {
|
||||
const packages = await this.libraryService.list({ fqbn });
|
||||
const packages: LibraryPackage[] = await this.libraryService.list({ fqbn });
|
||||
for (const pkg of packages) {
|
||||
const container = await this.tryGroupExamples(pkg);
|
||||
const { location } = pkg;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { injectable, inject, postConstruct } from 'inversify';
|
||||
import { LibraryPackage, LibraryServiceClient, LibraryServiceServer } from '../common/protocol/library-service';
|
||||
import { LibraryPackage, LibraryService } from '../common/protocol/library-service';
|
||||
import { CoreClientProvider } from './core-client-provider';
|
||||
import {
|
||||
LibrarySearchReq,
|
||||
@@ -14,14 +14,14 @@ import {
|
||||
LibraryUninstallResp,
|
||||
Library
|
||||
} from './cli-protocol/commands/lib_pb';
|
||||
import { ToolOutputServiceServer } from '../common/protocol/tool-output-service';
|
||||
import { Installable } from '../common/protocol/installable';
|
||||
import { ILogger, notEmpty } from '@theia/core';
|
||||
import { Deferred } from '@theia/core/lib/common/promise-util';
|
||||
import { FileUri } from '@theia/core/lib/node';
|
||||
import { OutputService, NotificationServiceServer } from '../common/protocol';
|
||||
|
||||
@injectable()
|
||||
export class LibraryServiceServerImpl implements LibraryServiceServer {
|
||||
export class LibraryServiceImpl implements LibraryService {
|
||||
|
||||
@inject(ILogger)
|
||||
protected logger: ILogger;
|
||||
@@ -29,11 +29,13 @@ export class LibraryServiceServerImpl implements LibraryServiceServer {
|
||||
@inject(CoreClientProvider)
|
||||
protected readonly coreClientProvider: CoreClientProvider;
|
||||
|
||||
@inject(ToolOutputServiceServer)
|
||||
protected readonly toolOutputService: ToolOutputServiceServer;
|
||||
@inject(OutputService)
|
||||
protected readonly outputService: OutputService;
|
||||
|
||||
@inject(NotificationServiceServer)
|
||||
protected readonly notificationServer: NotificationServiceServer;
|
||||
|
||||
protected ready = new Deferred<void>();
|
||||
protected client: LibraryServiceClient | undefined;
|
||||
|
||||
@postConstruct()
|
||||
protected init(): void {
|
||||
@@ -108,7 +110,31 @@ export class LibraryServiceServerImpl implements LibraryServiceServer {
|
||||
req.setFqbn(fqbn);
|
||||
}
|
||||
|
||||
const resp = await new Promise<LibraryListResp>((resolve, reject) => client.libraryList(req, ((error, resp) => !!error ? reject(error) : resolve(resp))));
|
||||
const resp = await new Promise<LibraryListResp | undefined>((resolve, reject) => {
|
||||
client.libraryList(req, ((error, r) => {
|
||||
if (error) {
|
||||
const { message } = error;
|
||||
// Required core dependency is missing.
|
||||
// https://github.com/arduino/arduino-cli/issues/954
|
||||
if (message.indexOf('missing platform release') !== -1 && message.indexOf('referenced by board') !== -1) {
|
||||
resolve(undefined);
|
||||
return;
|
||||
}
|
||||
// The core for the board is not installed, `lib list` cannot be filtered based on FQBN.
|
||||
// https://github.com/arduino/arduino-cli/issues/955
|
||||
if (message.indexOf('platform') !== -1 && message.indexOf('is not installed') !== -1) {
|
||||
resolve(undefined);
|
||||
return;
|
||||
}
|
||||
reject(error);
|
||||
return;
|
||||
}
|
||||
resolve(r);
|
||||
}));
|
||||
});
|
||||
if (!resp) {
|
||||
return [];
|
||||
}
|
||||
return resp.getInstalledLibraryList().map(item => {
|
||||
const library = item.getLibrary();
|
||||
if (!library) {
|
||||
@@ -151,7 +177,7 @@ export class LibraryServiceServerImpl implements LibraryServiceServer {
|
||||
resp.on('data', (r: LibraryInstallResp) => {
|
||||
const prog = r.getProgress();
|
||||
if (prog) {
|
||||
this.toolOutputService.append({ tool: 'library', chunk: `downloading ${prog.getFile()}: ${prog.getCompleted()}%\n` });
|
||||
this.outputService.append({ name: 'library', chunk: `downloading ${prog.getFile()}: ${prog.getCompleted()}%\n` });
|
||||
}
|
||||
});
|
||||
await new Promise<void>((resolve, reject) => {
|
||||
@@ -159,12 +185,9 @@ export class LibraryServiceServerImpl implements LibraryServiceServer {
|
||||
resp.on('error', reject);
|
||||
});
|
||||
|
||||
if (this.client) {
|
||||
const items = await this.search({});
|
||||
const updated = items.find(other => LibraryPackage.equals(other, item)) || item;
|
||||
this.client.notifyInstalled({ item: updated });
|
||||
}
|
||||
|
||||
const items = await this.search({});
|
||||
const updated = items.find(other => LibraryPackage.equals(other, item)) || item;
|
||||
this.notificationServer.notifyLibraryInstalled({ item: updated });
|
||||
console.info('<<< Library package installation done.', item);
|
||||
}
|
||||
|
||||
@@ -186,7 +209,7 @@ export class LibraryServiceServerImpl implements LibraryServiceServer {
|
||||
const resp = client.libraryUninstall(req);
|
||||
resp.on('data', (_: LibraryUninstallResp) => {
|
||||
if (!logged) {
|
||||
this.toolOutputService.append({ tool: 'library', chunk: `uninstalling ${item.name}:${item.installedVersion}%\n` });
|
||||
this.outputService.append({ name: 'library', chunk: `uninstalling ${item.name}:${item.installedVersion}%\n` });
|
||||
logged = true;
|
||||
}
|
||||
});
|
||||
@@ -194,19 +217,13 @@ export class LibraryServiceServerImpl implements LibraryServiceServer {
|
||||
resp.on('end', resolve);
|
||||
resp.on('error', reject);
|
||||
});
|
||||
if (this.client) {
|
||||
this.client.notifyUninstalled({ item });
|
||||
}
|
||||
console.info('<<< Library package uninstallation done.', item);
|
||||
}
|
||||
|
||||
setClient(client: LibraryServiceClient | undefined): void {
|
||||
this.client = client;
|
||||
this.notificationServer.notifyLibraryUninstalled({ item });
|
||||
console.info('<<< Library package uninstallation done.', item);
|
||||
}
|
||||
|
||||
dispose(): void {
|
||||
this.logger.info('>>> Disposing library service...');
|
||||
this.client = undefined;
|
||||
this.logger.info('<<< Disposed library service.');
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,65 @@
|
||||
import { injectable } from 'inversify';
|
||||
import { NotificationServiceServer, NotificationServiceClient, AttachedBoardsChangeEvent, BoardsPackage, LibraryPackage, Config } from '../common/protocol';
|
||||
|
||||
@injectable()
|
||||
export class NotificationServiceServerImpl implements NotificationServiceServer {
|
||||
|
||||
protected readonly clients: NotificationServiceClient[] = [];
|
||||
|
||||
notifyIndexUpdated(): void {
|
||||
this.clients.forEach(client => client.notifyIndexUpdated());
|
||||
}
|
||||
|
||||
notifyDaemonStarted(): void {
|
||||
this.clients.forEach(client => client.notifyDaemonStarted());
|
||||
}
|
||||
|
||||
notifyDaemonStopped(): void {
|
||||
this.clients.forEach(client => client.notifyDaemonStopped());
|
||||
}
|
||||
|
||||
notifyPlatformInstalled(event: { item: BoardsPackage }): void {
|
||||
this.clients.forEach(client => client.notifyPlatformInstalled(event));
|
||||
}
|
||||
|
||||
notifyPlatformUninstalled(event: { item: BoardsPackage }): void {
|
||||
this.clients.forEach(client => client.notifyPlatformUninstalled(event));
|
||||
}
|
||||
|
||||
notifyLibraryInstalled(event: { item: LibraryPackage }): void {
|
||||
this.clients.forEach(client => client.notifyLibraryInstalled(event));
|
||||
}
|
||||
|
||||
notifyLibraryUninstalled(event: { item: LibraryPackage }): void {
|
||||
this.clients.forEach(client => client.notifyLibraryUninstalled(event));
|
||||
}
|
||||
|
||||
notifyAttachedBoardsChanged(event: AttachedBoardsChangeEvent): void {
|
||||
this.clients.forEach(client => client.notifyAttachedBoardsChanged(event));
|
||||
}
|
||||
|
||||
notifyConfigChanged(event: { config: Config | undefined }): void {
|
||||
this.clients.forEach(client => client.notifyConfigChanged(event));
|
||||
}
|
||||
|
||||
setClient(client: NotificationServiceClient): void {
|
||||
this.clients.push(client);
|
||||
}
|
||||
|
||||
disposeClient(client: NotificationServiceClient): void {
|
||||
const index = this.clients.indexOf(client);
|
||||
if (index === -1) {
|
||||
console.warn(`Could not dispose notification service client. It was not registered.`);
|
||||
return;
|
||||
}
|
||||
this.clients.splice(index, 1);
|
||||
}
|
||||
|
||||
dispose(): void {
|
||||
for (const client of this.clients) {
|
||||
this.disposeClient(client);
|
||||
}
|
||||
this.clients.length = 0;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -2,11 +2,11 @@ import { join } from 'path';
|
||||
import { homedir } from 'os';
|
||||
import { injectable } from 'inversify';
|
||||
import { FileUri } from '@theia/core/lib/node/file-uri';
|
||||
import { EnvVariablesServerImpl } from '@theia/core/lib/node/env-variables/env-variables-server';
|
||||
import { BackendApplicationConfigProvider } from '@theia/core/lib/node/backend-application-config-provider';
|
||||
import { EnvVariablesServerImpl as TheiaEnvVariablesServerImpl } from '@theia/core/lib/node/env-variables/env-variables-server';
|
||||
|
||||
@injectable()
|
||||
export class ArduinoEnvVariablesServer extends EnvVariablesServerImpl {
|
||||
export class EnvVariablesServer extends TheiaEnvVariablesServerImpl {
|
||||
|
||||
protected readonly configDirUri = Promise.resolve(FileUri.create(join(homedir(), BackendApplicationConfigProvider.get().configDirName)).toString());
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
import { injectable, inject } from 'inversify';
|
||||
import { HostedPluginReader } from '@theia/plugin-ext/lib/hosted/node/plugin-reader';
|
||||
import { HostedPluginReader as TheiaHostedPluginReader } from '@theia/plugin-ext/lib/hosted/node/plugin-reader';
|
||||
import { PluginPackage, PluginContribution } from '@theia/plugin-ext/lib/common/plugin-protocol';
|
||||
import { CLI_CONFIG } from './cli-config';
|
||||
import { ConfigServiceImpl } from './config-service-impl';
|
||||
import { CLI_CONFIG } from '../../cli-config';
|
||||
import { ConfigServiceImpl } from '../../config-service-impl';
|
||||
|
||||
@injectable()
|
||||
export class ArduinoHostedPluginReader extends HostedPluginReader {
|
||||
export class HostedPluginReader extends TheiaHostedPluginReader {
|
||||
|
||||
@inject(ConfigServiceImpl)
|
||||
protected readonly configService: ConfigServiceImpl;
|
||||
@@ -1,10 +1,10 @@
|
||||
import { injectable, inject } from 'inversify';
|
||||
import { ILogger } from '@theia/core';
|
||||
import { DefaultWorkspaceServer } from '@theia/workspace/lib/node/default-workspace-server';
|
||||
import { ConfigService } from '../common/protocol/config-service';
|
||||
import { ILogger } from '@theia/core/lib/common/logger';
|
||||
import { DefaultWorkspaceServer as TheiaDefaultWorkspaceServer } from '@theia/workspace/lib/node/default-workspace-server';
|
||||
import { ConfigService } from '../../../common/protocol/config-service';
|
||||
|
||||
@injectable()
|
||||
export class DefaultWorkspaceServerExt extends DefaultWorkspaceServer {
|
||||
export class DefaultWorkspaceServer extends TheiaDefaultWorkspaceServer {
|
||||
|
||||
@inject(ConfigService)
|
||||
protected readonly configService: ConfigService;
|
||||
@@ -1,40 +0,0 @@
|
||||
import { injectable } from 'inversify';
|
||||
import { ToolOutputServiceServer, ToolOutputServiceClient, ToolOutputMessage } from '../common/protocol/tool-output-service';
|
||||
|
||||
@injectable()
|
||||
export class ToolOutputServiceServerImpl implements ToolOutputServiceServer {
|
||||
protected clients: ToolOutputServiceClient[] = [];
|
||||
|
||||
append(message: ToolOutputMessage): void {
|
||||
if (!message.chunk) {
|
||||
return;
|
||||
}
|
||||
for (const client of this.clients) {
|
||||
client.onMessageReceived(message);
|
||||
}
|
||||
}
|
||||
|
||||
setClient(client: ToolOutputServiceClient | undefined): void {
|
||||
if (!client) {
|
||||
return;
|
||||
}
|
||||
this.clients.push(client);
|
||||
}
|
||||
|
||||
disposeClient(client: ToolOutputServiceClient): void {
|
||||
const index = this.clients.indexOf(client);
|
||||
if (index === -1) {
|
||||
console.warn(`Could not dispose tools output client. It was not registered.`);
|
||||
return;
|
||||
}
|
||||
this.clients.splice(index, 1);
|
||||
}
|
||||
|
||||
dispose(): void {
|
||||
for (const client of this.clients) {
|
||||
this.disposeClient(client);
|
||||
}
|
||||
this.clients.length = 0;
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user