1
1
import { ILogger } from '@theia/core' ;
2
2
import { inject , injectable , named } from '@theia/core/shared/inversify' ;
3
- import { Board , Port , Status } from '../common/protocol' ;
3
+ import { Board , BoardsService , Port , Status } from '../common/protocol' ;
4
4
import { CoreClientAware } from './core-client-provider' ;
5
5
import { MonitorService } from './monitor-service' ;
6
6
import { MonitorServiceFactory } from './monitor-service-factory' ;
@@ -11,16 +11,34 @@ import {
11
11
12
12
type MonitorID = string ;
13
13
14
+ type UploadState = 'uploadInProgress' | 'pausedForUpload' | 'disposedForUpload' ;
15
+ type MonitorIDsByUploadState = Record < UploadState , MonitorID [ ] > ;
16
+
14
17
export const MonitorManagerName = 'monitor-manager' ;
15
18
16
19
@injectable ( )
17
20
export class MonitorManager extends CoreClientAware {
21
+ @inject ( BoardsService )
22
+ protected boardsService : BoardsService ;
23
+
18
24
// Map of monitor services that manage the running pluggable monitors.
19
25
// Each service handles the lifetime of one, and only one, monitor.
20
26
// If either the board or port managed changes, a new service must
21
27
// be started.
22
28
private monitorServices = new Map < MonitorID , MonitorService > ( ) ;
23
29
30
+ private monitorIDsByUploadState : MonitorIDsByUploadState = {
31
+ uploadInProgress : [ ] ,
32
+ pausedForUpload : [ ] ,
33
+ disposedForUpload : [ ] ,
34
+ } ;
35
+
36
+ private monitorServiceStartQueue : {
37
+ monitorID : string ;
38
+ serviceStartParams : [ Board , Port ] ;
39
+ connectToClient : ( status : Status ) => void ;
40
+ } [ ] = [ ] ;
41
+
24
42
@inject ( MonitorServiceFactory )
25
43
private monitorServiceFactory : MonitorServiceFactory ;
26
44
@@ -48,6 +66,33 @@ export class MonitorManager extends CoreClientAware {
48
66
return false ;
49
67
}
50
68
69
+ private uploadIsInProgress ( ) : boolean {
70
+ return this . monitorIDsByUploadState . uploadInProgress . length > 0 ;
71
+ }
72
+
73
+ private addToMonitorIDsByUploadState (
74
+ state : UploadState ,
75
+ monitorID : string
76
+ ) : void {
77
+ this . monitorIDsByUploadState [ state ] . push ( monitorID ) ;
78
+ }
79
+
80
+ private removeFromMonitorIDsByUploadState (
81
+ state : UploadState ,
82
+ monitorID : string
83
+ ) : void {
84
+ this . monitorIDsByUploadState [ state ] = this . monitorIDsByUploadState [
85
+ state
86
+ ] . filter ( ( id ) => id !== monitorID ) ;
87
+ }
88
+
89
+ private monitorIDIsInUploadState (
90
+ state : UploadState ,
91
+ monitorID : string
92
+ ) : boolean {
93
+ return this . monitorIDsByUploadState [ state ] . includes ( monitorID ) ;
94
+ }
95
+
51
96
/**
52
97
* Start a pluggable monitor that receives and sends messages
53
98
* to the specified board and port combination.
@@ -56,13 +101,34 @@ export class MonitorManager extends CoreClientAware {
56
101
* @returns a Status object to know if the process has been
57
102
* started or if there have been errors.
58
103
*/
59
- async startMonitor ( board : Board , port : Port ) : Promise < Status > {
104
+ async startMonitor (
105
+ board : Board ,
106
+ port : Port ,
107
+ connectToClient : ( status : Status ) => void
108
+ ) : Promise < void > {
60
109
const monitorID = this . monitorID ( board , port ) ;
110
+
61
111
let monitor = this . monitorServices . get ( monitorID ) ;
62
112
if ( ! monitor ) {
63
113
monitor = this . createMonitor ( board , port ) ;
64
114
}
65
- return await monitor . start ( ) ;
115
+
116
+ if ( this . uploadIsInProgress ( ) ) {
117
+ this . monitorServiceStartQueue = this . monitorServiceStartQueue . filter (
118
+ ( request ) => request . monitorID !== monitorID
119
+ ) ;
120
+
121
+ this . monitorServiceStartQueue . push ( {
122
+ monitorID,
123
+ serviceStartParams : [ board , port ] ,
124
+ connectToClient,
125
+ } ) ;
126
+
127
+ return ;
128
+ }
129
+
130
+ const result = await monitor . start ( ) ;
131
+ connectToClient ( result ) ;
66
132
}
67
133
68
134
/**
@@ -111,14 +177,18 @@ export class MonitorManager extends CoreClientAware {
111
177
// to retrieve if we don't have this information.
112
178
return ;
113
179
}
180
+
114
181
const monitorID = this . monitorID ( board , port ) ;
182
+ this . addToMonitorIDsByUploadState ( 'uploadInProgress' , monitorID ) ;
183
+
115
184
const monitor = this . monitorServices . get ( monitorID ) ;
116
185
if ( ! monitor ) {
117
186
// There's no monitor running there, bail
118
187
return ;
119
188
}
120
- monitor . setUploadInProgress ( true ) ;
121
- return await monitor . pause ( ) ;
189
+
190
+ this . addToMonitorIDsByUploadState ( 'pausedForUpload' , monitorID ) ;
191
+ return monitor . pause ( ) ;
122
192
}
123
193
124
194
/**
@@ -130,19 +200,69 @@ export class MonitorManager extends CoreClientAware {
130
200
* started or if there have been errors.
131
201
*/
132
202
async notifyUploadFinished ( board ?: Board , port ?: Port ) : Promise < Status > {
133
- if ( ! board || ! port ) {
134
- // We have no way of knowing which monitor
135
- // to retrieve if we don't have this information.
136
- return Status . NOT_CONNECTED ;
203
+ let status : Status = Status . NOT_CONNECTED ;
204
+ let portDidChangeOnUpload = false ;
205
+
206
+ // We have no way of knowing which monitor
207
+ // to retrieve if we don't have this information.
208
+ if ( board && port ) {
209
+ const monitorID = this . monitorID ( board , port ) ;
210
+ this . removeFromMonitorIDsByUploadState ( 'uploadInProgress' , monitorID ) ;
211
+
212
+ const monitor = this . monitorServices . get ( monitorID ) ;
213
+ if ( monitor ) {
214
+ status = await monitor . start ( ) ;
215
+ }
216
+
217
+ // this monitorID will only be present in "disposedForUpload"
218
+ // if the upload changed the board port
219
+ portDidChangeOnUpload = this . monitorIDIsInUploadState (
220
+ 'disposedForUpload' ,
221
+ monitorID
222
+ ) ;
223
+ if ( portDidChangeOnUpload ) {
224
+ this . removeFromMonitorIDsByUploadState ( 'disposedForUpload' , monitorID ) ;
225
+ }
226
+
227
+ // in case a service was paused but not disposed
228
+ this . removeFromMonitorIDsByUploadState ( 'pausedForUpload' , monitorID ) ;
137
229
}
138
- const monitorID = this . monitorID ( board , port ) ;
139
- const monitor = this . monitorServices . get ( monitorID ) ;
140
- if ( ! monitor ) {
141
- // There's no monitor running there, bail
142
- return Status . NOT_CONNECTED ;
230
+
231
+ await this . startQueuedServices ( portDidChangeOnUpload ) ;
232
+ return status ;
233
+ }
234
+
235
+ async startQueuedServices ( portDidChangeOnUpload : boolean ) : Promise < void > {
236
+ // if the port changed during upload with the monitor open, "startMonitorPendingRequests"
237
+ // will include a request for our "upload port', most likely at index 0.
238
+ // We remove it, as this port was to be used exclusively for the upload
239
+ const queued = portDidChangeOnUpload
240
+ ? this . monitorServiceStartQueue . slice ( 1 )
241
+ : this . monitorServiceStartQueue ;
242
+ this . monitorServiceStartQueue = [ ] ;
243
+
244
+ for ( const {
245
+ monitorID,
246
+ serviceStartParams : [ _ , port ] ,
247
+ connectToClient,
248
+ } of queued ) {
249
+ const boardsState = await this . boardsService . getState ( ) ;
250
+ const boardIsStillOnPort = Object . keys ( boardsState )
251
+ . map ( ( connection : string ) => {
252
+ const portAddress = connection . split ( '|' ) [ 0 ] ;
253
+ return portAddress ;
254
+ } )
255
+ . some ( ( portAddress : string ) => port . address === portAddress ) ;
256
+
257
+ if ( boardIsStillOnPort ) {
258
+ const monitorService = this . monitorServices . get ( monitorID ) ;
259
+
260
+ if ( monitorService ) {
261
+ const result = await monitorService . start ( ) ;
262
+ connectToClient ( result ) ;
263
+ }
264
+ }
143
265
}
144
- monitor . setUploadInProgress ( false ) ;
145
- return await monitor . start ( ) ;
146
266
}
147
267
148
268
/**
@@ -202,6 +322,18 @@ export class MonitorManager extends CoreClientAware {
202
322
this . monitorServices . set ( monitorID , monitor ) ;
203
323
monitor . onDispose (
204
324
( ( ) => {
325
+ // if a service is disposed during upload and
326
+ // we paused it beforehand we know it was disposed
327
+ // of because the upload changed the board port
328
+ if (
329
+ this . uploadIsInProgress ( ) &&
330
+ this . monitorIDIsInUploadState ( 'pausedForUpload' , monitorID )
331
+ ) {
332
+ this . removeFromMonitorIDsByUploadState ( 'pausedForUpload' , monitorID ) ;
333
+
334
+ this . addToMonitorIDsByUploadState ( 'disposedForUpload' , monitorID ) ;
335
+ }
336
+
205
337
this . monitorServices . delete ( monitorID ) ;
206
338
} ) . bind ( this )
207
339
) ;
0 commit comments