Skip to content

Commit 20169d5

Browse files
author
Alberto Iannaccone
committed
fix clearConsole + refactor monitor connection
1 parent 8a6b954 commit 20169d5

File tree

7 files changed

+91
-79
lines changed

7 files changed

+91
-79
lines changed

Diff for: arduino-ide-extension/src/browser/arduino-ide-frontend-module.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,7 @@ import {
8181
} from '../common/protocol/config-service';
8282
import { MonitorWidget } from './monitor/monitor-widget';
8383
import { MonitorViewContribution } from './monitor/monitor-view-contribution';
84-
import { MonitorConnection } from './monitor/monitor-connection';
84+
import { SerialConnectionManager } from './monitor/monitor-connection';
8585
import { MonitorModel } from './monitor/monitor-model';
8686
import { TabBarDecoratorService as TheiaTabBarDecoratorService } from '@theia/core/lib/browser/shell/tab-bar-decorator';
8787
import { TabBarDecoratorService } from './theia/core/tab-bar-decorator';
@@ -408,7 +408,7 @@ export default new ContainerModule((bind, unbind, isBound, rebind) => {
408408
return connection.createProxy(MonitorServicePath, client);
409409
})
410410
.inSingletonScope();
411-
bind(MonitorConnection).toSelf().inSingletonScope();
411+
bind(SerialConnectionManager).toSelf().inSingletonScope();
412412

413413
// Serial monitor service client to receive and delegate notifications from the backend.
414414
bind(MonitorServiceClient).to(MonitorServiceClientImpl).inSingletonScope();

Diff for: arduino-ide-extension/src/browser/contributions/burn-bootloader.ts

+3-3
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import { OutputChannelManager } from '@theia/output/lib/common/output-channel';
33
import { CoreService } from '../../common/protocol';
44
import { ArduinoMenus } from '../menu/arduino-menus';
55
import { BoardsDataStore } from '../boards/boards-data-store';
6-
import { MonitorConnection } from '../monitor/monitor-connection';
6+
import { SerialConnectionManager } from '../monitor/monitor-connection';
77
import { BoardsServiceProvider } from '../boards/boards-service-provider';
88
import {
99
SketchContribution,
@@ -17,8 +17,8 @@ export class BurnBootloader extends SketchContribution {
1717
@inject(CoreService)
1818
protected readonly coreService: CoreService;
1919

20-
@inject(MonitorConnection)
21-
protected readonly monitorConnection: MonitorConnection;
20+
@inject(SerialConnectionManager)
21+
protected readonly monitorConnection: SerialConnectionManager;
2222

2323
@inject(BoardsDataStore)
2424
protected readonly boardsDataStore: BoardsDataStore;

Diff for: arduino-ide-extension/src/browser/contributions/upload-sketch.ts

+3-3
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import { CoreService } from '../../common/protocol';
44
import { ArduinoMenus } from '../menu/arduino-menus';
55
import { ArduinoToolbar } from '../toolbar/arduino-toolbar';
66
import { BoardsDataStore } from '../boards/boards-data-store';
7-
import { MonitorConnection } from '../monitor/monitor-connection';
7+
import { SerialConnectionManager } from '../monitor/monitor-connection';
88
import { BoardsServiceProvider } from '../boards/boards-service-provider';
99
import {
1010
SketchContribution,
@@ -20,8 +20,8 @@ export class UploadSketch extends SketchContribution {
2020
@inject(CoreService)
2121
protected readonly coreService: CoreService;
2222

23-
@inject(MonitorConnection)
24-
protected readonly monitorConnection: MonitorConnection;
23+
@inject(SerialConnectionManager)
24+
protected readonly monitorConnection: SerialConnectionManager;
2525

2626
@inject(BoardsDataStore)
2727
protected readonly boardsDataStore: BoardsDataStore;

Diff for: arduino-ide-extension/src/browser/monitor/monitor-connection.ts

+71-57
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@ import { injectable, inject, postConstruct } from 'inversify';
22
import { deepClone } from '@theia/core/lib/common/objects';
33
import { Emitter, Event } from '@theia/core/lib/common/event';
44
import { MessageService } from '@theia/core/lib/common/message-service';
5-
import { FrontendApplicationStateService } from '@theia/core/lib/browser/frontend-application-state';
65
import {
76
MonitorService,
87
MonitorConfig,
@@ -22,13 +21,8 @@ import { MonitorModel } from './monitor-model';
2221
import { NotificationCenter } from '../notification-center';
2322
import { ThemeService } from '@theia/core/lib/browser/theming';
2423

25-
export enum SerialType {
26-
Monitor = 'Monitor',
27-
Plotter = 'Plotter',
28-
}
29-
3024
@injectable()
31-
export class MonitorConnection {
25+
export class SerialConnectionManager {
3226
@inject(MonitorModel)
3327
protected readonly monitorModel: MonitorModel;
3428

@@ -50,26 +44,19 @@ export class MonitorConnection {
5044
@inject(MessageService)
5145
protected messageService: MessageService;
5246

53-
@inject(FrontendApplicationStateService)
54-
protected readonly applicationState: FrontendApplicationStateService;
55-
56-
@inject(MonitorModel)
57-
protected readonly model: MonitorModel;
58-
5947
@inject(ThemeService)
6048
protected readonly themeService: ThemeService;
61-
62-
protected _state: MonitorConnection.State = [];
49+
protected _state: Serial.State = [];
6350
protected _connected: boolean;
6451

6552
/**
6653
* Note: The idea is to toggle this property from the UI (`Monitor` view)
6754
* and the boards config and the boards attachment/detachment logic can be at on place, here.
6855
*/
69-
protected readonly onConnectionChangedEmitter =
70-
new Emitter<MonitorConnection.State>();
56+
protected readonly onConnectionChangedEmitter = new Emitter<boolean>();
57+
7158
/**
72-
* This emitter forwards all read events **iff** the connection is established.
59+
* This emitter forwards all read events **if** the connection is established.
7360
*/
7461
protected readonly onReadEmitter = new Emitter<{ messages: string[] }>();
7562

@@ -81,8 +68,13 @@ export class MonitorConnection {
8168
protected monitorErrors: MonitorError[] = [];
8269
protected reconnectTimeout?: number;
8370

71+
/**
72+
* When the websocket server is up on the backend, we save the port here, so that the client knows how to connect to it
73+
* */
8474
protected wsPort?: number;
75+
8576
protected webSocket?: WebSocket;
77+
8678
protected config: Partial<MonitorConfig> = {
8779
board: undefined,
8880
port: undefined,
@@ -116,15 +108,21 @@ export class MonitorConnection {
116108
});
117109
}
118110

111+
/**
112+
* Set the config passing only the properties that has changed. If some has changed and the serial is open,
113+
* we try to reconnect
114+
*
115+
* @param newConfig the porperties of the config that has changed
116+
*/
119117
protected setConfig(newConfig: Partial<MonitorConfig>): void {
120-
let shouldReconnect = false;
118+
let configHasChanged = false;
121119
Object.keys(this.config).forEach((key: keyof MonitorConfig) => {
122120
if (newConfig[key] && newConfig[key] !== this.config[key]) {
123-
shouldReconnect = true;
121+
configHasChanged = true;
124122
this.config = { ...this.config, [key]: newConfig[key] };
125123
}
126124
});
127-
if (shouldReconnect && this.isSerialOpen()) {
125+
if (configHasChanged && this.isSerialOpen()) {
128126
this.disconnect().then(() => this.connect());
129127
}
130128
}
@@ -133,10 +131,13 @@ export class MonitorConnection {
133131
return this.wsPort;
134132
}
135133

136-
handleWebSocketChanged(wsPort: number): void {
134+
protected handleWebSocketChanged(wsPort: number): void {
137135
this.wsPort = wsPort;
138136
}
139137

138+
/**
139+
* When the serial monitor is open and the frontend is connected to the serial, we create the websocket here
140+
*/
140141
protected createWsConnection(): boolean {
141142
if (this.wsPort) {
142143
try {
@@ -153,10 +154,17 @@ export class MonitorConnection {
153154
return false;
154155
}
155156

156-
protected async setState(s: MonitorConnection.State): Promise<Status> {
157+
/**
158+
* Sets the types of connections needed by the client.
159+
*
160+
* @param s The new types of connections (can be 'Monitor', 'Plotter', none or both).
161+
* If the previuos state was empty and 's' is not, it tries to reconnect to the serial service
162+
* If the provios state was NOT empty and now it is, it disconnects to the serial service
163+
* @returns The status of the operation
164+
*/
165+
protected async setState(s: Serial.State): Promise<Status> {
157166
const oldState = deepClone(this._state);
158167
this._state = s;
159-
this.onConnectionChangedEmitter.fire(this._state);
160168
let status = Status.OK;
161169

162170
if (this.isSerialOpen(oldState) && !this.isSerialOpen()) {
@@ -165,34 +173,14 @@ export class MonitorConnection {
165173
status = await this.connect();
166174
}
167175

168-
// if (this.connected) {
169-
// switch (this.state.connected) {
170-
// case SerialType.All:
171-
// return Status.OK;
172-
// case SerialType.Plotter:
173-
// if (type === SerialType.Monitor) {
174-
// if (this.createWsConnection()) {
175-
// this.state = { ...this.state, connected: SerialType.All };
176-
// return Status.OK;
177-
// }
178-
// return Status.NOT_CONNECTED;
179-
// }
180-
// return Status.OK;
181-
// case SerialType.Monitor:
182-
// if (type === SerialType.Plotter)
183-
// this.state = { ...this.state, connected: SerialType.All };
184-
// return SerialType.All;
185-
// }
186-
// }
187-
188176
return status;
189177
}
190178

191-
protected get state(): MonitorConnection.State {
179+
protected get state(): Serial.State {
192180
return this._state;
193181
}
194182

195-
isSerialOpen(state?: MonitorConnection.State): boolean {
183+
isSerialOpen(state?: Serial.State): boolean {
196184
return (state ? state : this._state).length > 0;
197185
}
198186

@@ -208,19 +196,32 @@ export class MonitorConnection {
208196

209197
set connected(c: boolean) {
210198
this._connected = c;
199+
this.onConnectionChangedEmitter.fire(this._connected);
211200
}
212-
213-
async openSerial(type: SerialType): Promise<Status> {
201+
/**
202+
* Called when a client opens the serial from the GUI
203+
*
204+
* @param type could be either 'Monitor' or 'Plotter'. If it's 'Monitor' we also connect to the websocket and
205+
* listen to the message events
206+
* @returns the status of the operation
207+
*/
208+
async openSerial(type: Serial.Type): Promise<Status> {
214209
if (this.state.includes(type)) return Status.OK;
215210
const newState = deepClone(this.state);
216211
newState.push(type);
217212
const status = await this.setState(newState);
218-
if (Status.isOK(status) && type === SerialType.Monitor)
213+
if (Status.isOK(status) && type === Serial.Type.Monitor)
219214
this.createWsConnection();
220215
return status;
221216
}
222217

223-
async closeSerial(type: SerialType): Promise<Status> {
218+
/**
219+
* Called when a client closes the serial from the GUI
220+
*
221+
* @param type could be either 'Monitor' or 'Plotter'. If it's 'Monitor' we close the websocket connection
222+
* @returns the status of the operation
223+
*/
224+
async closeSerial(type: Serial.Type): Promise<Status> {
224225
const index = this.state.indexOf(type);
225226
let status = Status.OK;
226227
if (index >= 0) {
@@ -229,7 +230,7 @@ export class MonitorConnection {
229230
status = await this.setState(newState);
230231
if (
231232
Status.isOK(status) &&
232-
type === SerialType.Monitor &&
233+
type === Serial.Type.Monitor &&
233234
this.webSocket
234235
) {
235236
this.webSocket.close();
@@ -239,6 +240,9 @@ export class MonitorConnection {
239240
return status;
240241
}
241242

243+
/**
244+
* Handles error on the MonitorServiceClient and try to reconnect, eventually
245+
*/
242246
handleError(error: MonitorError): void {
243247
if (!this.connected) return;
244248
const { code, config } = error;
@@ -247,7 +251,7 @@ export class MonitorConnection {
247251
switch (code) {
248252
case MonitorError.ErrorCodes.CLIENT_CANCEL: {
249253
console.debug(
250-
`Serial connection was canceled by client: ${MonitorConnection.Config.toString(
254+
`Serial connection was canceled by client: ${Serial.Config.toString(
251255
this.config
252256
)}.`
253257
);
@@ -375,14 +379,14 @@ export class MonitorConnection {
375379
if (Status.isOK(status)) {
376380
this.connected = false;
377381
console.log(
378-
`<<< Disposed serial connection. Was: ${MonitorConnection.Config.toString(
382+
`<<< Disposed serial connection. Was: ${Serial.Config.toString(
379383
this.config
380384
)}`
381385
);
382386
this.wsPort = undefined;
383387
} else {
384388
console.warn(
385-
`<<< Could not dispose serial connection. Activate connection: ${MonitorConnection.Config.toString(
389+
`<<< Could not dispose serial connection. Activate connection: ${Serial.Config.toString(
386390
this.config
387391
)}`
388392
);
@@ -407,7 +411,7 @@ export class MonitorConnection {
407411
});
408412
}
409413

410-
get onConnectionChanged(): Event<MonitorConnection.State> {
414+
get onConnectionChanged(): Event<boolean> {
411415
return this.onConnectionChangedEmitter.event;
412416
}
413417

@@ -440,8 +444,18 @@ export class MonitorConnection {
440444
}
441445
}
442446

443-
export namespace MonitorConnection {
444-
export type State = SerialType[];
447+
export namespace Serial {
448+
export enum Type {
449+
Monitor = 'Monitor',
450+
Plotter = 'Plotter',
451+
}
452+
453+
/**
454+
* The state represents which types of connections are needed by the client, and it should match whether the Serial Monitor
455+
* or the Serial Plotter are open or not in the GUI. It's an array cause it's possible to have both, none or only one of
456+
* them open
457+
*/
458+
export type State = Serial.Type[];
445459

446460
export namespace Config {
447461
export function toString(config: Partial<MonitorConfig>): string {

Diff for: arduino-ide-extension/src/browser/monitor/monitor-widget.tsx

+5-7
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ import {
1212
import { MonitorConfig } from '../../common/protocol/monitor-service';
1313
import { ArduinoSelect } from '../widgets/arduino-select';
1414
import { MonitorModel } from './monitor-model';
15-
import { MonitorConnection, SerialType } from './monitor-connection';
15+
import { Serial, SerialConnectionManager } from './monitor-connection';
1616
import { SerialMonitorSendInput } from './serial-monitor-send-input';
1717
import { SerialMonitorOutput } from './serial-monitor-send-output';
1818
import { BoardsServiceProvider } from '../boards/boards-service-provider';
@@ -24,8 +24,8 @@ export class MonitorWidget extends ReactWidget {
2424
@inject(MonitorModel)
2525
protected readonly monitorModel: MonitorModel;
2626

27-
@inject(MonitorConnection)
28-
protected readonly monitorConnection: MonitorConnection;
27+
@inject(SerialConnectionManager)
28+
protected readonly monitorConnection: SerialConnectionManager;
2929

3030
@inject(BoardsServiceProvider)
3131
protected readonly boardsServiceProvider: BoardsServiceProvider;
@@ -53,7 +53,7 @@ export class MonitorWidget extends ReactWidget {
5353
this.toDispose.push(this.clearOutputEmitter);
5454
this.toDispose.push(
5555
Disposable.create(() =>
56-
this.monitorConnection.closeSerial(SerialType.Monitor)
56+
this.monitorConnection.closeSerial(Serial.Type.Monitor)
5757
)
5858
);
5959
}
@@ -77,9 +77,7 @@ export class MonitorWidget extends ReactWidget {
7777

7878
protected onAfterAttach(msg: Message): void {
7979
super.onAfterAttach(msg);
80-
// const { boardsConfig } = this.boardsServiceProvider;
81-
82-
this.monitorConnection.openSerial(SerialType.Monitor);
80+
this.monitorConnection.openSerial(Serial.Type.Monitor);
8381
}
8482

8583
onCloseRequest(msg: Message): void {

Diff for: arduino-ide-extension/src/browser/monitor/serial-monitor-send-output.tsx

+2-2
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import { Event } from '@theia/core/lib/common/event';
33
import { DisposableCollection } from '@theia/core/lib/common/disposable';
44
import { areEqual, FixedSizeList as List } from 'react-window';
55
import { MonitorModel } from './monitor-model';
6-
import { MonitorConnection } from './monitor-connection';
6+
import { SerialConnectionManager } from './monitor-connection';
77
import dateFormat = require('dateformat');
88
import { messagesToLines, truncateLines } from './monitor-utils';
99

@@ -124,7 +124,7 @@ const Row = React.memo(_Row, areEqual);
124124
export namespace SerialMonitorOutput {
125125
export interface Props {
126126
readonly monitorModel: MonitorModel;
127-
readonly monitorConnection: MonitorConnection;
127+
readonly monitorConnection: SerialConnectionManager;
128128
readonly clearConsoleEvent: Event<void>;
129129
readonly height: number;
130130
}

0 commit comments

Comments
 (0)