@@ -2,7 +2,6 @@ import { injectable, inject, postConstruct } from 'inversify';
2
2
import { deepClone } from '@theia/core/lib/common/objects' ;
3
3
import { Emitter , Event } from '@theia/core/lib/common/event' ;
4
4
import { MessageService } from '@theia/core/lib/common/message-service' ;
5
- import { FrontendApplicationStateService } from '@theia/core/lib/browser/frontend-application-state' ;
6
5
import {
7
6
MonitorService ,
8
7
MonitorConfig ,
@@ -22,13 +21,8 @@ import { MonitorModel } from './monitor-model';
22
21
import { NotificationCenter } from '../notification-center' ;
23
22
import { ThemeService } from '@theia/core/lib/browser/theming' ;
24
23
25
- export enum SerialType {
26
- Monitor = 'Monitor' ,
27
- Plotter = 'Plotter' ,
28
- }
29
-
30
24
@injectable ( )
31
- export class MonitorConnection {
25
+ export class SerialConnectionManager {
32
26
@inject ( MonitorModel )
33
27
protected readonly monitorModel : MonitorModel ;
34
28
@@ -50,26 +44,19 @@ export class MonitorConnection {
50
44
@inject ( MessageService )
51
45
protected messageService : MessageService ;
52
46
53
- @inject ( FrontendApplicationStateService )
54
- protected readonly applicationState : FrontendApplicationStateService ;
55
-
56
- @inject ( MonitorModel )
57
- protected readonly model : MonitorModel ;
58
-
59
47
@inject ( ThemeService )
60
48
protected readonly themeService : ThemeService ;
61
-
62
- protected _state : MonitorConnection . State = [ ] ;
49
+ protected _state : Serial . State = [ ] ;
63
50
protected _connected : boolean ;
64
51
65
52
/**
66
53
* Note: The idea is to toggle this property from the UI (`Monitor` view)
67
54
* and the boards config and the boards attachment/detachment logic can be at on place, here.
68
55
*/
69
- protected readonly onConnectionChangedEmitter =
70
- new Emitter < MonitorConnection . State > ( ) ;
56
+ protected readonly onConnectionChangedEmitter = new Emitter < boolean > ( ) ;
57
+
71
58
/**
72
- * This emitter forwards all read events **iff ** the connection is established.
59
+ * This emitter forwards all read events **if ** the connection is established.
73
60
*/
74
61
protected readonly onReadEmitter = new Emitter < { messages : string [ ] } > ( ) ;
75
62
@@ -81,8 +68,13 @@ export class MonitorConnection {
81
68
protected monitorErrors : MonitorError [ ] = [ ] ;
82
69
protected reconnectTimeout ?: number ;
83
70
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
+ * */
84
74
protected wsPort ?: number ;
75
+
85
76
protected webSocket ?: WebSocket ;
77
+
86
78
protected config : Partial < MonitorConfig > = {
87
79
board : undefined ,
88
80
port : undefined ,
@@ -116,15 +108,21 @@ export class MonitorConnection {
116
108
} ) ;
117
109
}
118
110
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
+ */
119
117
protected setConfig ( newConfig : Partial < MonitorConfig > ) : void {
120
- let shouldReconnect = false ;
118
+ let configHasChanged = false ;
121
119
Object . keys ( this . config ) . forEach ( ( key : keyof MonitorConfig ) => {
122
120
if ( newConfig [ key ] && newConfig [ key ] !== this . config [ key ] ) {
123
- shouldReconnect = true ;
121
+ configHasChanged = true ;
124
122
this . config = { ...this . config , [ key ] : newConfig [ key ] } ;
125
123
}
126
124
} ) ;
127
- if ( shouldReconnect && this . isSerialOpen ( ) ) {
125
+ if ( configHasChanged && this . isSerialOpen ( ) ) {
128
126
this . disconnect ( ) . then ( ( ) => this . connect ( ) ) ;
129
127
}
130
128
}
@@ -133,10 +131,13 @@ export class MonitorConnection {
133
131
return this . wsPort ;
134
132
}
135
133
136
- handleWebSocketChanged ( wsPort : number ) : void {
134
+ protected handleWebSocketChanged ( wsPort : number ) : void {
137
135
this . wsPort = wsPort ;
138
136
}
139
137
138
+ /**
139
+ * When the serial monitor is open and the frontend is connected to the serial, we create the websocket here
140
+ */
140
141
protected createWsConnection ( ) : boolean {
141
142
if ( this . wsPort ) {
142
143
try {
@@ -153,10 +154,17 @@ export class MonitorConnection {
153
154
return false ;
154
155
}
155
156
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 > {
157
166
const oldState = deepClone ( this . _state ) ;
158
167
this . _state = s ;
159
- this . onConnectionChangedEmitter . fire ( this . _state ) ;
160
168
let status = Status . OK ;
161
169
162
170
if ( this . isSerialOpen ( oldState ) && ! this . isSerialOpen ( ) ) {
@@ -165,34 +173,14 @@ export class MonitorConnection {
165
173
status = await this . connect ( ) ;
166
174
}
167
175
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
-
188
176
return status ;
189
177
}
190
178
191
- protected get state ( ) : MonitorConnection . State {
179
+ protected get state ( ) : Serial . State {
192
180
return this . _state ;
193
181
}
194
182
195
- isSerialOpen ( state ?: MonitorConnection . State ) : boolean {
183
+ isSerialOpen ( state ?: Serial . State ) : boolean {
196
184
return ( state ? state : this . _state ) . length > 0 ;
197
185
}
198
186
@@ -208,19 +196,32 @@ export class MonitorConnection {
208
196
209
197
set connected ( c : boolean ) {
210
198
this . _connected = c ;
199
+ this . onConnectionChangedEmitter . fire ( this . _connected ) ;
211
200
}
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 > {
214
209
if ( this . state . includes ( type ) ) return Status . OK ;
215
210
const newState = deepClone ( this . state ) ;
216
211
newState . push ( type ) ;
217
212
const status = await this . setState ( newState ) ;
218
- if ( Status . isOK ( status ) && type === SerialType . Monitor )
213
+ if ( Status . isOK ( status ) && type === Serial . Type . Monitor )
219
214
this . createWsConnection ( ) ;
220
215
return status ;
221
216
}
222
217
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 > {
224
225
const index = this . state . indexOf ( type ) ;
225
226
let status = Status . OK ;
226
227
if ( index >= 0 ) {
@@ -229,7 +230,7 @@ export class MonitorConnection {
229
230
status = await this . setState ( newState ) ;
230
231
if (
231
232
Status . isOK ( status ) &&
232
- type === SerialType . Monitor &&
233
+ type === Serial . Type . Monitor &&
233
234
this . webSocket
234
235
) {
235
236
this . webSocket . close ( ) ;
@@ -239,6 +240,9 @@ export class MonitorConnection {
239
240
return status ;
240
241
}
241
242
243
+ /**
244
+ * Handles error on the MonitorServiceClient and try to reconnect, eventually
245
+ */
242
246
handleError ( error : MonitorError ) : void {
243
247
if ( ! this . connected ) return ;
244
248
const { code, config } = error ;
@@ -247,7 +251,7 @@ export class MonitorConnection {
247
251
switch ( code ) {
248
252
case MonitorError . ErrorCodes . CLIENT_CANCEL : {
249
253
console . debug (
250
- `Serial connection was canceled by client: ${ MonitorConnection . Config . toString (
254
+ `Serial connection was canceled by client: ${ Serial . Config . toString (
251
255
this . config
252
256
) } .`
253
257
) ;
@@ -375,14 +379,14 @@ export class MonitorConnection {
375
379
if ( Status . isOK ( status ) ) {
376
380
this . connected = false ;
377
381
console . log (
378
- `<<< Disposed serial connection. Was: ${ MonitorConnection . Config . toString (
382
+ `<<< Disposed serial connection. Was: ${ Serial . Config . toString (
379
383
this . config
380
384
) } `
381
385
) ;
382
386
this . wsPort = undefined ;
383
387
} else {
384
388
console . warn (
385
- `<<< Could not dispose serial connection. Activate connection: ${ MonitorConnection . Config . toString (
389
+ `<<< Could not dispose serial connection. Activate connection: ${ Serial . Config . toString (
386
390
this . config
387
391
) } `
388
392
) ;
@@ -407,7 +411,7 @@ export class MonitorConnection {
407
411
} ) ;
408
412
}
409
413
410
- get onConnectionChanged ( ) : Event < MonitorConnection . State > {
414
+ get onConnectionChanged ( ) : Event < boolean > {
411
415
return this . onConnectionChangedEmitter . event ;
412
416
}
413
417
@@ -440,8 +444,18 @@ export class MonitorConnection {
440
444
}
441
445
}
442
446
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 [ ] ;
445
459
446
460
export namespace Config {
447
461
export function toString ( config : Partial < MonitorConfig > ) : string {
0 commit comments