diff --git a/arduino-ide-extension/src/browser/monitor-manager-proxy-client-impl.ts b/arduino-ide-extension/src/browser/monitor-manager-proxy-client-impl.ts index 76425a4e..7af890a4 100644 --- a/arduino-ide-extension/src/browser/monitor-manager-proxy-client-impl.ts +++ b/arduino-ide-extension/src/browser/monitor-manager-proxy-client-impl.ts @@ -1,13 +1,103 @@ -import { Emitter } from "@theia/core"; -import { injectable } from "@theia/core/shared/inversify"; -import { MonitorManagerProxyClient } from "../common/protocol/monitor-service"; +import { Emitter, JsonRpcProxy, MessageService } from "@theia/core"; +import { inject, injectable } from "@theia/core/shared/inversify"; +import { Board, Port } from "../common/protocol"; +import { Monitor, MonitorManagerProxy, MonitorManagerProxyClient, MonitorSettings } from "../common/protocol/monitor-service"; @injectable() export class MonitorManagerProxyClientImpl implements MonitorManagerProxyClient { - protected readonly onWebSocketChangedEmitter = new Emitter(); - readonly onWebSocketChanged = this.onWebSocketChangedEmitter.event; + // When pluggable monitor messages are received from the backend + // this event is triggered. + // Ideally a frontend component is connected to this event + // to update the UI. + protected readonly onMessagesReceivedEmitter = new Emitter<{ messages: string[] }>(); + readonly onMessagesReceived = this.onMessagesReceivedEmitter.event; - notifyWebSocketChanged(message: number): void { - this.onWebSocketChangedEmitter.fire(message); + // WebSocket used to handle pluggable monitor communication between + // frontend and backend. + private webSocket?: WebSocket; + private wsPort?: number; + + getWebSocketPort(): number | undefined { + return this.wsPort; + } + + constructor( + @inject(MessageService) + protected messageService: MessageService, + + // This is necessary to call the backend methods from the frontend + @inject(MonitorManagerProxy) + protected server: JsonRpcProxy + ) { + + } + + /** + * Connects a localhost WebSocket using the specified port. + * @param addressPort port of the WebSocket + */ + connect(addressPort: number): void { + if (this.webSocket) { + return; + } + try { + this.webSocket = new WebSocket(`ws://localhost:${addressPort}`); + } catch { + this.messageService.error('Unable to connect to websocket'); + return; + } + + this.webSocket.onmessage = (res) => { + const messages = JSON.parse(res.data); + this.onMessagesReceivedEmitter.fire({ messages }); + } + this.wsPort = addressPort; + } + + /** + * Disconnects the WebSocket if connected. + */ + disconnect(): void { + try { + this.webSocket?.close(); + this.webSocket = undefined; + } catch { + this.messageService.error('Unable to close websocket'); + } + } + + async isWSConnected(): Promise { + return !!this.webSocket; + } + + async startMonitor(board: Board, port: Port, settings?: MonitorSettings): Promise { + return this.server.startMonitor(board, port, settings); + } + + getCurrentSettings(board: Board, port: Port): MonitorSettings { + return this.server.getCurrentSettings(board, port); + } + + send(message: string): void { + if (!this.webSocket) { + return; + } + + this.webSocket.send(JSON.stringify({ + command: Monitor.Command.SEND_MESSAGE, + data: message, + })); + } + + changeSettings(settings: MonitorSettings): void { + if (!this.webSocket) { + return; + } + + this.webSocket.send(JSON.stringify({ + command: Monitor.Command.CHANGE_SETTINGS, + // TODO: This might be wrong, verify if it works + data: settings, + })); } } diff --git a/arduino-ide-extension/src/common/protocol/monitor-service.ts b/arduino-ide-extension/src/common/protocol/monitor-service.ts index 11217ca5..71c4b7e6 100644 --- a/arduino-ide-extension/src/common/protocol/monitor-service.ts +++ b/arduino-ide-extension/src/common/protocol/monitor-service.ts @@ -7,13 +7,20 @@ export interface MonitorManagerProxy extends JsonRpcServer; changeMonitorSettings(board: Board, port: Port, settings: MonitorSettings): Promise; stopMonitor(board: Board, port: Port): Promise; - getSupportedSettings(protocol: string, fqbn: string): Promise; + getCurrentSettings(board: Board, port: Port): MonitorSettings; } export const MonitorManagerProxyClient = Symbol('MonitorManagerProxyClient'); export interface MonitorManagerProxyClient { - onWebSocketChanged: Event; - notifyWebSocketChanged(message: number): void; + onMessagesReceived: Event<{ messages: string[] }>; + connect(addressPort: number): void; + disconnect(): void; + getWebSocketPort(): number | undefined; + isWSConnected(): Promise; + startMonitor(board: Board, port: Port, settings?: MonitorSettings): Promise; + getCurrentSettings(board: Board, port: Port): MonitorSettings; + send(message: string): void; + changeSettings(settings: MonitorSettings): void } export interface MonitorSetting { @@ -29,4 +36,35 @@ export interface MonitorSetting { selectedValue: string; } -export type MonitorSettings = Record; \ No newline at end of file +export type MonitorSettings = Record; + +export namespace Monitor { + export enum Command { + SEND_MESSAGE = 'MONITOR_SEND_MESSAGE', + CHANGE_SETTINGS = 'MONITOR_CHANGE_SETTINGS', + } + + export type Message = { + command: Monitor.Command, + data: string; + } +} + +export interface Status { } +export type OK = Status; +export interface ErrorStatus extends Status { + readonly message: string; +} +export namespace Status { + export function isOK(status: Status & { message?: string }): status is OK { + return !!status && typeof status.message !== 'string'; + } + export const OK: OK = {}; + export const NOT_CONNECTED: ErrorStatus = { message: 'Not connected.' }; + export const ALREADY_CONNECTED: ErrorStatus = { + message: 'Already connected.', + }; + export const CONFIG_MISSING: ErrorStatus = { + message: 'Serial Config missing.', + }; +} diff --git a/arduino-ide-extension/src/node/monitor-manager-proxy-impl.ts b/arduino-ide-extension/src/node/monitor-manager-proxy-impl.ts index 2291228e..851a8486 100644 --- a/arduino-ide-extension/src/node/monitor-manager-proxy-impl.ts +++ b/arduino-ide-extension/src/node/monitor-manager-proxy-impl.ts @@ -19,7 +19,7 @@ export class MonitorManagerProxyImpl implements MonitorManagerProxy { } dispose(): void { - // NOOP + this.client?.disconnect(); } /** @@ -36,7 +36,8 @@ export class MonitorManagerProxyImpl implements MonitorManagerProxy { } const status = await this.manager.startMonitor(board, port); if (status === Status.ALREADY_CONNECTED || status === Status.OK) { - this.client.notifyWebSocketChanged(this.manager.getWebsocketAddressPort(board, port)); + // Monitor started correctly, connect it with the frontend + this.client.connect(this.manager.getWebsocketAddressPort(board, port)); } } @@ -65,15 +66,14 @@ export class MonitorManagerProxyImpl implements MonitorManagerProxy { } /** - * Returns the settings supported by the pluggable monitor for the specified - * protocol, the fqbn is necessary since it's used to tell different monitors - * using the same protocol. - * @param protocol protocol of a pluggable monitor - * @param fqbn unique ID of a board + * Returns the current settings by the pluggable monitor connected to specified + * by board/port combination. + * @param board board connected to port + * @param port port monitored * @returns a map of MonitorSetting */ - async getSupportedSettings(protocol: string, fqbn: string): Promise { - return this.manager.portMonitorSettings(protocol, fqbn); + getCurrentSettings(board: Board, port: Port): MonitorSettings { + return this.manager.currentMonitorSettings(board, port); } setClient(client: MonitorManagerProxyClient | undefined): void { diff --git a/arduino-ide-extension/src/node/monitor-service.ts b/arduino-ide-extension/src/node/monitor-service.ts index f843565b..4503a3ca 100644 --- a/arduino-ide-extension/src/node/monitor-service.ts +++ b/arduino-ide-extension/src/node/monitor-service.ts @@ -321,29 +321,16 @@ export class MonitorService extends CoreClientAware implements Disposable { if (!this.onMessageReceived) { this.onMessageReceived = this.webSocketProvider.onMessageReceived( (msg: string) => { - const message: SerialPlotter.Protocol.Message = JSON.parse(msg); + const message: Monitor.Message = JSON.parse(msg); switch (message.command) { - case SerialPlotter.Protocol.Command.PLOTTER_SEND_MESSAGE: + case Monitor.Command.SEND_MESSAGE: this.send(message.data); - break; - - case SerialPlotter.Protocol.Command.PLOTTER_SET_BAUDRATE: - this.theiaFEClient?.notifyBaudRateChanged( - parseInt(message.data, 10) as SerialConfig.BaudRate - ); - break; - - case SerialPlotter.Protocol.Command.PLOTTER_SET_LINE_ENDING: - this.theiaFEClient?.notifyLineEndingChanged(message.data); - break; - - case SerialPlotter.Protocol.Command.PLOTTER_SET_INTERPOLATE: - this.theiaFEClient?.notifyInterpolateChanged(message.data); - break; - - default: - break; + break + case Monitor.Command.CHANGE_SETTINGS: + const settings: MonitorSettings = JSON.parse(message.data); + this.changeSettings(settings); + break } } )