1
1
import {
2
- CommandRegistry ,
2
+ ApplicationError ,
3
3
Disposable ,
4
4
Emitter ,
5
5
MessageService ,
6
6
nls ,
7
7
} from '@theia/core' ;
8
+ import { Deferred } from '@theia/core/lib/common/promise-util' ;
8
9
import { inject , injectable } from '@theia/core/shared/inversify' ;
10
+ import { NotificationManager } from '@theia/messages/lib/browser/notifications-manager' ;
11
+ import { MessageType } from '@theia/core/lib/common/message-service-protocol' ;
9
12
import { Board , Port } from '../common/protocol' ;
10
13
import {
11
14
Monitor ,
@@ -23,21 +26,31 @@ import { BoardsServiceProvider } from './boards/boards-service-provider';
23
26
export class MonitorManagerProxyClientImpl
24
27
implements MonitorManagerProxyClient
25
28
{
29
+ @inject ( MessageService )
30
+ private readonly messageService : MessageService ;
31
+ // This is necessary to call the backend methods from the frontend
32
+ @inject ( MonitorManagerProxyFactory )
33
+ private readonly server : MonitorManagerProxyFactory ;
34
+ @inject ( BoardsServiceProvider )
35
+ private readonly boardsServiceProvider : BoardsServiceProvider ;
36
+ @inject ( NotificationManager )
37
+ private readonly notificationManager : NotificationManager ;
38
+
26
39
// When pluggable monitor messages are received from the backend
27
40
// this event is triggered.
28
41
// Ideally a frontend component is connected to this event
29
42
// to update the UI.
30
- protected readonly onMessagesReceivedEmitter = new Emitter < {
43
+ private readonly onMessagesReceivedEmitter = new Emitter < {
31
44
messages : string [ ] ;
32
45
} > ( ) ;
33
46
readonly onMessagesReceived = this . onMessagesReceivedEmitter . event ;
34
47
35
- protected readonly onMonitorSettingsDidChangeEmitter =
48
+ private readonly onMonitorSettingsDidChangeEmitter =
36
49
new Emitter < MonitorSettings > ( ) ;
37
50
readonly onMonitorSettingsDidChange =
38
51
this . onMonitorSettingsDidChangeEmitter . event ;
39
52
40
- protected readonly onMonitorShouldResetEmitter = new Emitter ( ) ;
53
+ private readonly onMonitorShouldResetEmitter = new Emitter < void > ( ) ;
41
54
readonly onMonitorShouldReset = this . onMonitorShouldResetEmitter . event ;
42
55
43
56
// WebSocket used to handle pluggable monitor communication between
@@ -51,29 +64,16 @@ export class MonitorManagerProxyClientImpl
51
64
return this . wsPort ;
52
65
}
53
66
54
- constructor (
55
- @inject ( MessageService )
56
- protected messageService : MessageService ,
57
-
58
- // This is necessary to call the backend methods from the frontend
59
- @inject ( MonitorManagerProxyFactory )
60
- protected server : MonitorManagerProxyFactory ,
61
-
62
- @inject ( CommandRegistry )
63
- protected readonly commandRegistry : CommandRegistry ,
64
-
65
- @inject ( BoardsServiceProvider )
66
- protected readonly boardsServiceProvider : BoardsServiceProvider
67
- ) { }
68
-
69
67
/**
70
68
* Connects a localhost WebSocket using the specified port.
71
69
* @param addressPort port of the WebSocket
72
70
*/
73
71
async connect ( addressPort : number ) : Promise < void > {
74
- if ( ! ! this . webSocket ) {
75
- if ( this . wsPort === addressPort ) return ;
76
- else this . disconnect ( ) ;
72
+ if ( this . webSocket ) {
73
+ if ( this . wsPort === addressPort ) {
74
+ return ;
75
+ }
76
+ this . disconnect ( ) ;
77
77
}
78
78
try {
79
79
this . webSocket = new WebSocket ( `ws://localhost:${ addressPort } ` ) ;
@@ -87,6 +87,9 @@ export class MonitorManagerProxyClientImpl
87
87
return ;
88
88
}
89
89
90
+ const opened = new Deferred < void > ( ) ;
91
+ this . webSocket . onopen = ( ) => opened . resolve ( ) ;
92
+ this . webSocket . onerror = ( ) => opened . reject ( ) ;
90
93
this . webSocket . onmessage = ( message ) => {
91
94
const parsedMessage = JSON . parse ( message . data ) ;
92
95
if ( Array . isArray ( parsedMessage ) )
@@ -99,19 +102,26 @@ export class MonitorManagerProxyClientImpl
99
102
}
100
103
} ;
101
104
this . wsPort = addressPort ;
105
+ return opened . promise ;
102
106
}
103
107
104
108
/**
105
109
* Disconnects the WebSocket if connected.
106
110
*/
107
111
disconnect ( ) : void {
108
- if ( ! this . webSocket ) return ;
112
+ if ( ! this . webSocket ) {
113
+ return ;
114
+ }
109
115
this . onBoardsConfigChanged ?. dispose ( ) ;
110
116
this . onBoardsConfigChanged = undefined ;
111
117
try {
112
- this . webSocket ? .close ( ) ;
118
+ this . webSocket . close ( ) ;
113
119
this . webSocket = undefined ;
114
- } catch {
120
+ } catch ( err ) {
121
+ console . error (
122
+ 'Could not close the websocket connection for the monitor.' ,
123
+ err
124
+ ) ;
115
125
this . messageService . error (
116
126
nls . localize (
117
127
'arduino/monitor/unableToCloseWebSocket' ,
@@ -126,6 +136,7 @@ export class MonitorManagerProxyClientImpl
126
136
}
127
137
128
138
async startMonitor ( settings ?: PluggableMonitorSettings ) : Promise < void > {
139
+ await this . boardsServiceProvider . reconciled ;
129
140
this . lastConnectedBoard = {
130
141
selectedBoard : this . boardsServiceProvider . boardsConfig . selectedBoard ,
131
142
selectedPort : this . boardsServiceProvider . boardsConfig . selectedPort ,
@@ -150,11 +161,11 @@ export class MonitorManagerProxyClientImpl
150
161
? Port . keyOf ( this . lastConnectedBoard . selectedPort )
151
162
: undefined )
152
163
) {
153
- this . onMonitorShouldResetEmitter . fire ( null ) ;
154
164
this . lastConnectedBoard = {
155
165
selectedBoard : selectedBoard ,
156
166
selectedPort : selectedPort ,
157
167
} ;
168
+ this . onMonitorShouldResetEmitter . fire ( ) ;
158
169
} else {
159
170
// a board is plugged and it's the same as prev, rerun "this.startMonitor" to
160
171
// recreate the listener callback
@@ -167,7 +178,14 @@ export class MonitorManagerProxyClientImpl
167
178
const { selectedBoard, selectedPort } =
168
179
this . boardsServiceProvider . boardsConfig ;
169
180
if ( ! selectedBoard || ! selectedBoard . fqbn || ! selectedPort ) return ;
170
- await this . server ( ) . startMonitor ( selectedBoard , selectedPort , settings ) ;
181
+ try {
182
+ this . clearVisibleNotification ( ) ;
183
+ await this . server ( ) . startMonitor ( selectedBoard , selectedPort , settings ) ;
184
+ } catch ( err ) {
185
+ const message = ApplicationError . is ( err ) ? err . message : String ( err ) ;
186
+ this . previousNotificationId = this . notificationId ( message ) ;
187
+ this . messageService . error ( message ) ;
188
+ }
171
189
}
172
190
173
191
getCurrentSettings ( board : Board , port : Port ) : Promise < MonitorSettings > {
@@ -199,4 +217,24 @@ export class MonitorManagerProxyClientImpl
199
217
} )
200
218
) ;
201
219
}
220
+
221
+ /**
222
+ * This is the internal (Theia) ID of the notification that is currently visible.
223
+ * It's stored here as a field to be able to close it before starting a new monitor connection. It's a hack.
224
+ */
225
+ private previousNotificationId : string | undefined ;
226
+ private clearVisibleNotification ( ) : void {
227
+ if ( this . previousNotificationId ) {
228
+ this . notificationManager . clear ( this . previousNotificationId ) ;
229
+ this . previousNotificationId = undefined ;
230
+ }
231
+ }
232
+
233
+ private notificationId ( message : string , ...actions : string [ ] ) : string {
234
+ return this . notificationManager [ 'getMessageId' ] ( {
235
+ text : message ,
236
+ actions,
237
+ type : MessageType . Error ,
238
+ } ) ;
239
+ }
202
240
}
0 commit comments