Enhance MonitorManager APIs

This commit is contained in:
Silvano Cerza 2022-03-04 18:03:28 +01:00 committed by Alberto Iannaccone
parent 2c95e7f033
commit 480492a7c8
4 changed files with 235 additions and 18 deletions

View File

@ -1,6 +1,13 @@
import { Emitter } from "@theia/core";
import { injectable } from "@theia/core/shared/inversify";
import { MonitorManagerProxyClient } from "../common/monitor-manager-proxy";
@injectable()
export class MonitorManagerProxyClientImpl implements MonitorManagerProxyClient {
protected readonly onWebSocketChangedEmitter = new Emitter<number>();
readonly onWebSocketChanged = this.onWebSocketChangedEmitter.event;
notifyWebSocketChanged(message: number): void {
this.onWebSocketChangedEmitter.fire(message);
}
}

View File

@ -1,12 +0,0 @@
import { JsonRpcServer } from "@theia/core";
export const MonitorManagerProxyPath = '/services/monitor-manager-proxy';
export const MonitorManagerProxy = Symbol('MonitorManagerProxy');
export interface MonitorManagerProxy extends JsonRpcServer<MonitorManagerProxyClient> {
}
export const MonitorManagerProxyClient = Symbol('MonitorManagerProxyClient');
export interface MonitorManagerProxyClient {
}

View File

@ -1,20 +1,57 @@
import { inject, injectable } from "@theia/core/shared/inversify";
import { MonitorManagerProxy, MonitorManagerProxyClient } from "../common/monitor-manager-proxy";
import { Emitter, ILogger } from "@theia/core";
import { inject, injectable, named } from "@theia/core/shared/inversify";
import { Disposable } from "@theia/core/shared/vscode-languageserver-protocol";
import { MonitorManagerProxy, MonitorManagerProxyClient, MonitorSettings, Status } from "../common/protocol";
import { Board, Port } from "../common/protocol";
import { MonitorManager } from "./monitor-manager";
@injectable()
export class MonitorManagerProxyImpl implements MonitorManagerProxy {
protected client: MonitorManagerProxyClient;
protected selectedBoard: Board | null;
protected selectedPort: Port | null;
constructor(
@inject(ILogger)
@named("monitor-manager-proxy")
protected readonly logger: ILogger,
@inject(MonitorManager)
protected readonly manager: MonitorManager,
) {
}
dispose(): void {
// TODO
// NOOP
}
// setMonitorConfig is called by the FE when trying to establish a monitor connection to a board or when changing some
// settings (such as the baudrate, when available)
async setMonitorSettings(board: Board, port: Port, settings: MonitorSettings): Promise<void> {
// check if it's a different connection or a change in the settings
if (board === this.selectedBoard && port === this.selectedPort) {
// TODO: update the settings
return;
}
const startStatus: Status = await this.manager.startMonitor(board, port);
if (startStatus === Status.ALREADY_CONNECTED || startStatus === Status.OK) {
this.client.notifyWebSocketChanged(this.manager.getWebsocketAddress(board, port));
}
}
setClient(client: MonitorManagerProxyClient | undefined): void {
// TODO
if (!client) {
return;
}
this.client = client;
}
}

View File

@ -1,6 +1,191 @@
import { injectable } from "@theia/core/shared/inversify";
import { Emitter, ILogger } from "@theia/core";
import { inject, injectable, named } from "@theia/core/shared/inversify";
import { Board, Port, Status, MonitorSetting, MonitorSettings } from "../common/protocol";
import { EnumerateMonitorPortSettingsRequest, EnumerateMonitorPortSettingsResponse } from "./cli-protocol/cc/arduino/cli/commands/v1/monitor_pb";
import { CoreClientAware } from "./core-client-provider";
import { MonitorService } from "./monitor-service";
type MonitorID = string;
@injectable()
export class MonitorManager {
export class MonitorManager extends CoreClientAware {
// Map of monitor services that manage the running pluggable monitors.
// Each service handles the lifetime of one, and only one, monitor.
// If either the board or port managed changes a new service must
// be started.
private monitorServices = new Map<MonitorID, MonitorService>();
// Used to notify a monitor service that an upload process started
// to the board/port combination it manages
protected readonly onUploadStartedEmitter = new Emitter<{ board: Board, port: Port }>();
readonly onUploadStarted = this.onUploadStartedEmitter.event;
// Used to notify a monitor service that an upload process finished
// to the board/port combination it manages
constructor(
@inject(ILogger)
@named('monitor-manager')
protected readonly logger: ILogger,
) {
super();
}
/**
* Returns the possible configurations used to connect a monitor
* to the board specified by fqbn using the specified protocol
* @param protocol the protocol of the monitor we want get settings for
* @param fqbn the fqbn of the board we want to monitor
* @returns a map of all the settings supported by the monitor
*/
async portMonitorSettings(protocol: string, fqbn: string): Promise<MonitorSettings> {
const coreClient = await this.coreClient();
const { client, instance } = coreClient;
const req = new EnumerateMonitorPortSettingsRequest();
req.setInstance(instance);
req.setPortProtocol(protocol);
req.setFqbn(fqbn);
const res = await new Promise<EnumerateMonitorPortSettingsResponse>((resolve, reject) => {
client.enumerateMonitorPortSettings(req, (err, resp) => {
if (!!err) {
reject(err)
}
resolve(resp)
})
})
let settings: MonitorSettings = {};
for (const iterator of res.getSettingsList()) {
settings[iterator.getSettingId()] = {
'id': iterator.getSettingId(),
'label': iterator.getLabel(),
'type': iterator.getType(),
'values': iterator.getEnumValuesList(),
'selectedValue': iterator.getValue(),
}
}
return settings;
}
/**
*
* @param board
* @param port
*/
async startMonitor(board: Board, port: Port): Promise<Status> {
const monitorID = this.monitorID(board, port);
let monitor = this.monitorServices.get(monitorID);
if (!monitor) {
monitor = this.createMonitor(board, port)
}
return await monitor.start();
// TODO: I need to return the address here right?
}
async stopMonitor(board: Board, port: Port): Promise<void> {
const monitorID = this.monitorID(board, port);
const monitor = this.monitorServices.get(monitorID);
if (!monitor) {
// There's no monitor to stop, bail
return;
}
return await monitor.stop();
}
getWebsocketAddress(board: Board, port: Port): number {
const monitorID = this.monitorID(board, port);
const monitor = this.monitorServices.get(monitorID);
if (!monitor) {
return -1;
}
return monitor.getWebsocketAddress();
}
/**
* Notifies the monitor service of that board/port combination
* that an upload process started on that exact board/port combination.
* This must be done so that we can stop the monitor for the time being
* until the upload process finished.
* @param board
* @param port
*/
async notifyUploadStarted(board?: Board, port?: Port): Promise<void> {
if (!board || !port) {
// We have no way of knowing which monitor
// to retrieve if we don't have this information.
return;
}
const monitorID = this.monitorID(board, port);
const monitor = this.monitorServices.get(monitorID);
if (!monitor) {
// There's no monitor running there, bail
return;
}
return await monitor.pause();
}
/**
* Notifies the monitor service of that board/port combination
* that an upload process started on that exact board/port combination.
* @param board
* @param port
* @returns
*/
async notifyUploadFinished(board?: Board, port?: Port): Promise<Status> {
if (!board || !port) {
// We have no way of knowing which monitor
// to retrieve if we don't have this information.
return Status.NOT_CONNECTED;
}
const monitorID = this.monitorID(board, port);
const monitor = this.monitorServices.get(monitorID);
if (!monitor) {
// There's no monitor running there, bail
return Status.NOT_CONNECTED;
}
return await monitor.start();
}
/**
*
* @param board
* @param port
* @param settings map of monitor settings to change
*/
changeMonitorSettings(board: Board, port: Port, settings: Record<string, MonitorSetting>) {
const monitorID = this.monitorID(board, port);
let monitor = this.monitorServices.get(monitorID);
if (!monitor) {
monitor = this.createMonitor(board, port)
monitor.changeSettings(settings);
}
}
private createMonitor(board: Board, port: Port): MonitorService {
const monitorID = this.monitorID(board, port);
const monitor = new MonitorService(
this.logger,
board,
port
);
monitor.onDispose((() => {
this.monitorServices.delete(monitorID);
}).bind(this));
return monitor
}
/**
* Utility function to create a unique ID for a monitor service.
* @param board
* @param port
* @returns a unique monitor ID
*/
private monitorID(board: Board, port: Port): MonitorID {
return `${board.fqbn}-${port.address}-${port.protocol}`;
}
}