@@ -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 ,
@@ -23,13 +22,8 @@ import { NotificationCenter } from '../notification-center';
23
22
import { ThemeService } from '@theia/core/lib/browser/theming' ;
24
23
import { nls } from '@theia/core/lib/browser/nls' ;
25
24
26
- export enum SerialType {
27
- Monitor = 'Monitor' ,
28
- Plotter = 'Plotter' ,
29
- }
30
-
31
25
@injectable ( )
32
- export class MonitorConnection {
26
+ export class SerialConnectionManager {
33
27
@inject ( MonitorModel )
34
28
protected readonly monitorModel : MonitorModel ;
35
29
@@ -51,26 +45,19 @@ export class MonitorConnection {
51
45
@inject ( MessageService )
52
46
protected messageService : MessageService ;
53
47
54
- @inject ( FrontendApplicationStateService )
55
- protected readonly applicationState : FrontendApplicationStateService ;
56
-
57
- @inject ( MonitorModel )
58
- protected readonly model : MonitorModel ;
59
-
60
48
@inject ( ThemeService )
61
49
protected readonly themeService : ThemeService ;
62
-
63
- protected _state : MonitorConnection . State = [ ] ;
50
+ protected _state : Serial . State = [ ] ;
64
51
protected _connected : boolean ;
65
52
66
53
/**
67
54
* Note: The idea is to toggle this property from the UI (`Monitor` view)
68
55
* and the boards config and the boards attachment/detachment logic can be at on place, here.
69
56
*/
70
- protected readonly onConnectionChangedEmitter =
71
- new Emitter < MonitorConnection . State > ( ) ;
57
+ protected readonly onConnectionChangedEmitter = new Emitter < boolean > ( ) ;
58
+
72
59
/**
73
- * This emitter forwards all read events **iff ** the connection is established.
60
+ * This emitter forwards all read events **if ** the connection is established.
74
61
*/
75
62
protected readonly onReadEmitter = new Emitter < { messages : string [ ] } > ( ) ;
76
63
@@ -82,8 +69,13 @@ export class MonitorConnection {
82
69
protected monitorErrors : MonitorError [ ] = [ ] ;
83
70
protected reconnectTimeout ?: number ;
84
71
72
+ /**
73
+ * When the websocket server is up on the backend, we save the port here, so that the client knows how to connect to it
74
+ * */
85
75
protected wsPort ?: number ;
76
+
86
77
protected webSocket ?: WebSocket ;
78
+
87
79
protected config : Partial < MonitorConfig > = {
88
80
board : undefined ,
89
81
port : undefined ,
@@ -117,15 +109,21 @@ export class MonitorConnection {
117
109
} ) ;
118
110
}
119
111
112
+ /**
113
+ * Set the config passing only the properties that has changed. If some has changed and the serial is open,
114
+ * we try to reconnect
115
+ *
116
+ * @param newConfig the porperties of the config that has changed
117
+ */
120
118
protected setConfig ( newConfig : Partial < MonitorConfig > ) : void {
121
- let shouldReconnect = false ;
119
+ let configHasChanged = false ;
122
120
Object . keys ( this . config ) . forEach ( ( key : keyof MonitorConfig ) => {
123
121
if ( newConfig [ key ] && newConfig [ key ] !== this . config [ key ] ) {
124
- shouldReconnect = true ;
122
+ configHasChanged = true ;
125
123
this . config = { ...this . config , [ key ] : newConfig [ key ] } ;
126
124
}
127
125
} ) ;
128
- if ( shouldReconnect && this . isSerialOpen ( ) ) {
126
+ if ( configHasChanged && this . isSerialOpen ( ) ) {
129
127
this . disconnect ( ) . then ( ( ) => this . connect ( ) ) ;
130
128
}
131
129
}
@@ -134,10 +132,13 @@ export class MonitorConnection {
134
132
return this . wsPort ;
135
133
}
136
134
137
- handleWebSocketChanged ( wsPort : number ) : void {
135
+ protected handleWebSocketChanged ( wsPort : number ) : void {
138
136
this . wsPort = wsPort ;
139
137
}
140
138
139
+ /**
140
+ * When the serial monitor is open and the frontend is connected to the serial, we create the websocket here
141
+ */
141
142
protected createWsConnection ( ) : boolean {
142
143
if ( this . wsPort ) {
143
144
try {
@@ -154,10 +155,17 @@ export class MonitorConnection {
154
155
return false ;
155
156
}
156
157
157
- protected async setState ( s : MonitorConnection . State ) : Promise < Status > {
158
+ /**
159
+ * Sets the types of connections needed by the client.
160
+ *
161
+ * @param s The new types of connections (can be 'Monitor', 'Plotter', none or both).
162
+ * If the previuos state was empty and 's' is not, it tries to reconnect to the serial service
163
+ * If the provios state was NOT empty and now it is, it disconnects to the serial service
164
+ * @returns The status of the operation
165
+ */
166
+ protected async setState ( s : Serial . State ) : Promise < Status > {
158
167
const oldState = deepClone ( this . _state ) ;
159
168
this . _state = s ;
160
- this . onConnectionChangedEmitter . fire ( this . _state ) ;
161
169
let status = Status . OK ;
162
170
163
171
if ( this . isSerialOpen ( oldState ) && ! this . isSerialOpen ( ) ) {
@@ -166,34 +174,14 @@ export class MonitorConnection {
166
174
status = await this . connect ( ) ;
167
175
}
168
176
169
- // if (this.connected) {
170
- // switch (this.state.connected) {
171
- // case SerialType.All:
172
- // return Status.OK;
173
- // case SerialType.Plotter:
174
- // if (type === SerialType.Monitor) {
175
- // if (this.createWsConnection()) {
176
- // this.state = { ...this.state, connected: SerialType.All };
177
- // return Status.OK;
178
- // }
179
- // return Status.NOT_CONNECTED;
180
- // }
181
- // return Status.OK;
182
- // case SerialType.Monitor:
183
- // if (type === SerialType.Plotter)
184
- // this.state = { ...this.state, connected: SerialType.All };
185
- // return SerialType.All;
186
- // }
187
- // }
188
-
189
177
return status ;
190
178
}
191
179
192
- protected get state ( ) : MonitorConnection . State {
180
+ protected get state ( ) : Serial . State {
193
181
return this . _state ;
194
182
}
195
183
196
- isSerialOpen ( state ?: MonitorConnection . State ) : boolean {
184
+ isSerialOpen ( state ?: Serial . State ) : boolean {
197
185
return ( state ? state : this . _state ) . length > 0 ;
198
186
}
199
187
@@ -209,19 +197,32 @@ export class MonitorConnection {
209
197
210
198
set connected ( c : boolean ) {
211
199
this . _connected = c ;
200
+ this . onConnectionChangedEmitter . fire ( this . _connected ) ;
212
201
}
213
-
214
- async openSerial ( type : SerialType ) : Promise < Status > {
202
+ /**
203
+ * Called when a client opens the serial from the GUI
204
+ *
205
+ * @param type could be either 'Monitor' or 'Plotter'. If it's 'Monitor' we also connect to the websocket and
206
+ * listen to the message events
207
+ * @returns the status of the operation
208
+ */
209
+ async openSerial ( type : Serial . Type ) : Promise < Status > {
215
210
if ( this . state . includes ( type ) ) return Status . OK ;
216
211
const newState = deepClone ( this . state ) ;
217
212
newState . push ( type ) ;
218
213
const status = await this . setState ( newState ) ;
219
- if ( Status . isOK ( status ) && type === SerialType . Monitor )
214
+ if ( Status . isOK ( status ) && type === Serial . Type . Monitor )
220
215
this . createWsConnection ( ) ;
221
216
return status ;
222
217
}
223
218
224
- async closeSerial ( type : SerialType ) : Promise < Status > {
219
+ /**
220
+ * Called when a client closes the serial from the GUI
221
+ *
222
+ * @param type could be either 'Monitor' or 'Plotter'. If it's 'Monitor' we close the websocket connection
223
+ * @returns the status of the operation
224
+ */
225
+ async closeSerial ( type : Serial . Type ) : Promise < Status > {
225
226
const index = this . state . indexOf ( type ) ;
226
227
let status = Status . OK ;
227
228
if ( index >= 0 ) {
@@ -230,7 +231,7 @@ export class MonitorConnection {
230
231
status = await this . setState ( newState ) ;
231
232
if (
232
233
Status . isOK ( status ) &&
233
- type === SerialType . Monitor &&
234
+ type === Serial . Type . Monitor &&
234
235
this . webSocket
235
236
) {
236
237
this . webSocket . close ( ) ;
@@ -240,6 +241,9 @@ export class MonitorConnection {
240
241
return status ;
241
242
}
242
243
244
+ /**
245
+ * Handles error on the MonitorServiceClient and try to reconnect, eventually
246
+ */
243
247
handleError ( error : MonitorError ) : void {
244
248
if ( ! this . connected ) return ;
245
249
const { code, config } = error ;
@@ -248,7 +252,7 @@ export class MonitorConnection {
248
252
switch ( code ) {
249
253
case MonitorError . ErrorCodes . CLIENT_CANCEL : {
250
254
console . debug (
251
- `Serial connection was canceled by client: ${ MonitorConnection . Config . toString (
255
+ `Serial connection was canceled by client: ${ Serial . Config . toString (
252
256
this . config
253
257
) } .`
254
258
) ;
@@ -394,14 +398,14 @@ export class MonitorConnection {
394
398
if ( Status . isOK ( status ) ) {
395
399
this . connected = false ;
396
400
console . log (
397
- `<<< Disposed serial connection. Was: ${ MonitorConnection . Config . toString (
401
+ `<<< Disposed serial connection. Was: ${ Serial . Config . toString (
398
402
this . config
399
403
) } `
400
404
) ;
401
405
this . wsPort = undefined ;
402
406
} else {
403
407
console . warn (
404
- `<<< Could not dispose serial connection. Activate connection: ${ MonitorConnection . Config . toString (
408
+ `<<< Could not dispose serial connection. Activate connection: ${ Serial . Config . toString (
405
409
this . config
406
410
) } `
407
411
) ;
@@ -426,7 +430,7 @@ export class MonitorConnection {
426
430
} ) ;
427
431
}
428
432
429
- get onConnectionChanged ( ) : Event < MonitorConnection . State > {
433
+ get onConnectionChanged ( ) : Event < boolean > {
430
434
return this . onConnectionChangedEmitter . event ;
431
435
}
432
436
@@ -459,8 +463,18 @@ export class MonitorConnection {
459
463
}
460
464
}
461
465
462
- export namespace MonitorConnection {
463
- export type State = SerialType [ ] ;
466
+ export namespace Serial {
467
+ export enum Type {
468
+ Monitor = 'Monitor' ,
469
+ Plotter = 'Plotter' ,
470
+ }
471
+
472
+ /**
473
+ * The state represents which types of connections are needed by the client, and it should match whether the Serial Monitor
474
+ * 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
475
+ * them open
476
+ */
477
+ export type State = Serial . Type [ ] ;
464
478
465
479
export namespace Config {
466
480
export function toString ( config : Partial < MonitorConfig > ) : string {
0 commit comments