Akos Kitta 69ae38effa
feat: simplify board and port handling (#2165)
Use Arduino CLI revision `38479dc`

Closes #43
Closes #82
Closes #1319
Closes #1366
Closes #2143
Closes #2158

Ref: 38479dc706

Signed-off-by: Akos Kitta <a.kitta@arduino.cc>
2023-08-18 14:42:50 +02:00

251 lines
7.1 KiB
TypeScript

import { ApplicationError } from '@theia/core/lib/common/application-error';
import type { Event } from '@theia/core/lib/common/event';
import type { JsonRpcServer } from '@theia/core/lib/common/messaging/proxy-factory';
import { nls } from '@theia/core/lib/common/nls';
import type { BoardIdentifier, PortIdentifier } from './boards-service';
export type PluggableMonitorSettings = Record<string, PluggableMonitorSetting>;
export interface MonitorSettings {
pluggableMonitorSettings?: PluggableMonitorSettings;
monitorUISettings?: Partial<MonitorState>;
}
export const MonitorManagerProxyFactory = Symbol('MonitorManagerProxyFactory');
export type MonitorManagerProxyFactory = () => MonitorManagerProxy;
export const MonitorManagerProxyPath = '/services/monitor-manager-proxy';
export const MonitorManagerProxy = Symbol('MonitorManagerProxy');
export interface MonitorManagerProxy
extends JsonRpcServer<MonitorManagerProxyClient> {
startMonitor(
board: BoardIdentifier,
port: PortIdentifier,
settings?: PluggableMonitorSettings
): Promise<void>;
changeMonitorSettings(
board: BoardIdentifier,
port: PortIdentifier,
settings: MonitorSettings
): Promise<void>;
stopMonitor(board: BoardIdentifier, port: PortIdentifier): Promise<void>;
getCurrentSettings(
board: BoardIdentifier,
port: PortIdentifier
): Promise<MonitorSettings>;
}
export const MonitorManagerProxyClient = Symbol('MonitorManagerProxyClient');
export interface MonitorManagerProxyClient {
onMessagesReceived: Event<{ messages: string[] }>;
onMonitorSettingsDidChange: Event<MonitorSettings>;
onMonitorShouldReset: Event<void>;
connect(addressPort: number): Promise<void>;
disconnect(): void;
getWebSocketPort(): number | undefined;
isWSConnected(): Promise<boolean>;
startMonitor(settings?: PluggableMonitorSettings): Promise<void>;
getCurrentSettings(
board: BoardIdentifier,
port: PortIdentifier
): Promise<MonitorSettings>;
send(message: string): void;
changeSettings(settings: MonitorSettings): void;
}
export interface PluggableMonitorSetting {
// The setting identifier
readonly id: string;
// A human-readable label of the setting (to be displayed on the GUI)
readonly label: string;
// The setting type (at the moment only "enum" is available)
readonly type: string;
// The values allowed on "enum" types
readonly values: string[];
// The selected value
selectedValue: string;
}
export namespace Monitor {
// Commands sent by the clients to the web socket server
export enum ClientCommand {
SEND_MESSAGE = 'SEND_MESSAGE',
CHANGE_SETTINGS = 'CHANGE_SETTINGS',
}
// Commands sent by the backend to the clients
export enum MiddlewareCommand {
ON_SETTINGS_DID_CHANGE = 'ON_SETTINGS_DID_CHANGE',
}
export type Message = {
command: Monitor.ClientCommand | Monitor.MiddlewareCommand;
data: string | MonitorSettings;
};
}
export const MonitorErrorCodes = {
ConnectionFailed: 6001,
NotConnected: 6002,
AlreadyConnected: 6003,
MissingConfiguration: 6004,
} as const;
export const ConnectionFailedError = declareMonitorError(
MonitorErrorCodes.ConnectionFailed
);
export const NotConnectedError = declareMonitorError(
MonitorErrorCodes.NotConnected
);
export const AlreadyConnectedError = declareMonitorError(
MonitorErrorCodes.AlreadyConnected
);
export const MissingConfigurationError = declareMonitorError(
MonitorErrorCodes.MissingConfiguration
);
export function createConnectionFailedError(
port: PortIdentifier,
details?: string
): ApplicationError<number, PortDescriptor> {
const { protocol, address } = port;
let message;
if (details) {
const detailsWithPeriod = details.endsWith('.') ? details : `${details}.`;
message = nls.localize(
'arduino/monitor/connectionFailedErrorWithDetails',
'{0} Could not connect to {1} {2} port.',
detailsWithPeriod,
address,
protocol
);
} else {
message = nls.localize(
'arduino/monitor/connectionFailedError',
'Could not connect to {0} {1} port.',
address,
protocol
);
}
return ConnectionFailedError(message, { protocol, address });
}
export function createNotConnectedError(
port: PortIdentifier
): ApplicationError<number, PortDescriptor> {
const { protocol, address } = port;
return NotConnectedError(
nls.localize(
'arduino/monitor/notConnectedError',
'Not connected to {0} {1} port.',
address,
protocol
),
{ protocol, address }
);
}
export function createAlreadyConnectedError(
port: PortIdentifier
): ApplicationError<number, PortDescriptor> {
const { protocol, address } = port;
return AlreadyConnectedError(
nls.localize(
'arduino/monitor/alreadyConnectedError',
'Could not connect to {0} {1} port. Already connected.',
address,
protocol
),
{ protocol, address }
);
}
export function createMissingConfigurationError(
port: PortIdentifier
): ApplicationError<number, PortDescriptor> {
const { protocol, address } = port;
return MissingConfigurationError(
nls.localize(
'arduino/monitor/missingConfigurationError',
'Could not connect to {0} {1} port. The monitor configuration is missing.',
address,
protocol
),
{ protocol, address }
);
}
/**
* Bare minimum representation of a port. Supports neither UI labels nor properties.
*/
interface PortDescriptor {
readonly protocol: string;
readonly address: string;
}
function declareMonitorError(
code: number
): ApplicationError.Constructor<number, PortDescriptor> {
return ApplicationError.declare(
code,
(message: string, data: PortDescriptor) => ({ data, message })
);
}
export interface MonitorConnectionError {
readonly errorMessage: string;
}
export type MonitorConnectionStatus =
| 'connecting'
| 'connected'
| 'not-connected'
| MonitorConnectionError;
export function monitorConnectionStatusEquals(
left: MonitorConnectionStatus,
right: MonitorConnectionStatus
): boolean {
if (typeof left === 'object' && typeof right === 'object') {
return left.errorMessage === right.errorMessage;
}
return left === right;
}
/**
* @deprecated see `MonitorState#connected`
*/
export function isMonitorConnected(
status: MonitorConnectionStatus
): status is 'connected' {
return status === 'connected';
}
export function isMonitorConnectionError(
status: MonitorConnectionStatus
): status is MonitorConnectionError {
return typeof status === 'object';
}
export interface MonitorState {
autoscroll: boolean;
timestamp: boolean;
lineEnding: MonitorEOL;
interpolate: boolean;
darkTheme: boolean;
wsPort: number;
serialPort: string;
connectionStatus: MonitorConnectionStatus;
/**
* @deprecated This property is never get by IDE2 only set. This value is present to be backward compatible with the plotter app.
* IDE2 uses `MonitorState#connectionStatus`.
*/
connected: boolean;
}
export namespace MonitorState {
export interface Change<K extends keyof MonitorState> {
readonly property: K;
readonly value: MonitorState[K];
}
}
export type MonitorEOL = '' | '\n' | '\r' | '\r\n';
export namespace MonitorEOL {
export const DEFAULT: MonitorEOL = '\n';
}