@@ -17,6 +17,7 @@ package discovery
17
17
18
18
import (
19
19
"encoding/json"
20
+ "fmt"
20
21
"io"
21
22
"sync"
22
23
"time"
@@ -29,6 +30,17 @@ import (
29
30
"github.com/pkg/errors"
30
31
)
31
32
33
+ // To work correctly a Pluggable Discovery must respect the state machine specifed on the documentation:
34
+ // https://arduino.github.io/arduino-cli/latest/pluggable-discovery-specification/#state-machine
35
+ // States a PluggableDiscovery can be in
36
+ const (
37
+ Alive int = iota
38
+ Idling
39
+ Running
40
+ Syncing
41
+ Dead
42
+ )
43
+
32
44
// PluggableDiscovery is a tool that detects communication ports to interact
33
45
// with the boards.
34
46
type PluggableDiscovery struct {
@@ -40,8 +52,7 @@ type PluggableDiscovery struct {
40
52
// All the following fields are guarded by statusMutex
41
53
statusMutex sync.Mutex
42
54
incomingMessagesError error
43
- alive bool
44
- eventsMode bool
55
+ state int
45
56
eventChan chan <- * Event
46
57
cachedPorts map [string ]* Port
47
58
}
@@ -114,6 +125,7 @@ func New(id string, args ...string) (*PluggableDiscovery, error) {
114
125
process : proc ,
115
126
incomingMessagesChan : messageChan ,
116
127
outgoingCommandsPipe : stdin ,
128
+ state : Dead ,
117
129
}
118
130
go disc .jsonDecodeLoop (stdout , messageChan )
119
131
return disc , nil
@@ -132,7 +144,7 @@ func (disc *PluggableDiscovery) jsonDecodeLoop(in io.Reader, outChan chan<- *dis
132
144
decoder := json .NewDecoder (in )
133
145
closeAndReportError := func (err error ) {
134
146
disc .statusMutex .Lock ()
135
- disc .alive = false
147
+ disc .state = Dead
136
148
disc .incomingMessagesError = err
137
149
disc .statusMutex .Unlock ()
138
150
close (outChan )
@@ -173,19 +185,11 @@ func (disc *PluggableDiscovery) jsonDecodeLoop(in io.Reader, outChan chan<- *dis
173
185
}
174
186
}
175
187
176
- // IsAlive return true if the discovery process is running and so is able to receive commands
177
- // and produce events.
178
- func (disc * PluggableDiscovery ) IsAlive () bool {
179
- disc .statusMutex .Lock ()
180
- defer disc .statusMutex .Unlock ()
181
- return disc .alive
182
- }
183
-
184
- // IsEventMode return true if the discovery is in "events" mode
185
- func (disc * PluggableDiscovery ) IsEventMode () bool {
188
+ // State returns the current state of this PluggableDiscovery
189
+ func (disc * PluggableDiscovery ) State () int {
186
190
disc .statusMutex .Lock ()
187
191
defer disc .statusMutex .Unlock ()
188
- return disc .eventsMode
192
+ return disc .state
189
193
}
190
194
191
195
func (disc * PluggableDiscovery ) waitMessage (timeout time.Duration ) (* discoveryMessage , error ) {
@@ -199,7 +203,7 @@ func (disc *PluggableDiscovery) waitMessage(timeout time.Duration) (*discoveryMe
199
203
}
200
204
return msg , nil
201
205
case <- time .After (timeout ):
202
- return nil , errors . New ("timeout" )
206
+ return nil , fmt . Errorf ("timeout waiting for message from %s" , disc . id )
203
207
}
204
208
}
205
209
@@ -223,7 +227,7 @@ func (disc *PluggableDiscovery) runProcess() error {
223
227
}
224
228
disc .statusMutex .Lock ()
225
229
defer disc .statusMutex .Unlock ()
226
- disc .alive = true
230
+ disc .state = Alive
227
231
return nil
228
232
}
229
233
@@ -238,14 +242,17 @@ func (disc *PluggableDiscovery) Run() error {
238
242
return err
239
243
}
240
244
if msg , err := disc .waitMessage (time .Second * 10 ); err != nil {
241
- return err
245
+ return fmt . Errorf ( "calling HELLO: %w" , err )
242
246
} else if msg .EventType != "hello" {
243
247
return errors .Errorf (tr ("communication out of sync, expected 'hello', received '%s'" ), msg .EventType )
244
248
} else if msg .Message != "OK" || msg .Error {
245
249
return errors .Errorf (tr ("command failed: %s" ), msg .Message )
246
250
} else if msg .ProtocolVersion > 1 {
247
251
return errors .Errorf (tr ("protocol version not supported: requested 1, got %d" ), msg .ProtocolVersion )
248
252
}
253
+ disc .statusMutex .Lock ()
254
+ defer disc .statusMutex .Unlock ()
255
+ disc .state = Idling
249
256
return nil
250
257
}
251
258
@@ -256,12 +263,15 @@ func (disc *PluggableDiscovery) Start() error {
256
263
return err
257
264
}
258
265
if msg , err := disc .waitMessage (time .Second * 10 ); err != nil {
259
- return err
266
+ return fmt . Errorf ( "calling START: %w" , err )
260
267
} else if msg .EventType != "start" {
261
268
return errors .Errorf (tr ("communication out of sync, expected 'start', received '%s'" ), msg .EventType )
262
269
} else if msg .Message != "OK" || msg .Error {
263
270
return errors .Errorf (tr ("command failed: %s" ), msg .Message )
264
271
}
272
+ disc .statusMutex .Lock ()
273
+ defer disc .statusMutex .Unlock ()
274
+ disc .state = Running
265
275
return nil
266
276
}
267
277
@@ -273,19 +283,20 @@ func (disc *PluggableDiscovery) Stop() error {
273
283
return err
274
284
}
275
285
if msg , err := disc .waitMessage (time .Second * 10 ); err != nil {
276
- return err
286
+ return fmt . Errorf ( "calling STOP: %w" , err )
277
287
} else if msg .EventType != "stop" {
278
288
return errors .Errorf (tr ("communication out of sync, expected 'stop', received '%s'" ), msg .EventType )
279
289
} else if msg .Message != "OK" || msg .Error {
280
290
return errors .Errorf (tr ("command failed: %s" ), msg .Message )
281
291
}
282
292
disc .statusMutex .Lock ()
283
293
defer disc .statusMutex .Unlock ()
294
+ // TODO: Should we clear cached ports here?
284
295
if disc .eventChan != nil {
285
296
close (disc .eventChan )
286
297
disc .eventChan = nil
287
298
}
288
- disc .eventsMode = false
299
+ disc .state = Idling
289
300
return nil
290
301
}
291
302
@@ -295,7 +306,7 @@ func (disc *PluggableDiscovery) Quit() error {
295
306
return err
296
307
}
297
308
if msg , err := disc .waitMessage (time .Second * 10 ); err != nil {
298
- return err
309
+ return fmt . Errorf ( "calling QUIT: %w" , err )
299
310
} else if msg .EventType != "quit" {
300
311
return errors .Errorf (tr ("communication out of sync, expected 'quit', received '%s'" ), msg .EventType )
301
312
} else if msg .Message != "OK" || msg .Error {
@@ -307,7 +318,7 @@ func (disc *PluggableDiscovery) Quit() error {
307
318
close (disc .eventChan )
308
319
disc .eventChan = nil
309
320
}
310
- disc .alive = false
321
+ disc .state = Dead
311
322
return nil
312
323
}
313
324
@@ -318,7 +329,7 @@ func (disc *PluggableDiscovery) List() ([]*Port, error) {
318
329
return nil , err
319
330
}
320
331
if msg , err := disc .waitMessage (time .Second * 10 ); err != nil {
321
- return nil , err
332
+ return nil , fmt . Errorf ( "calling LIST: %w" , err )
322
333
} else if msg .EventType != "list" {
323
334
return nil , errors .Errorf (tr ("communication out of sync, expected 'list', received '%s'" ), msg .EventType )
324
335
} else if msg .Error {
@@ -349,25 +360,21 @@ func (disc *PluggableDiscovery) EventChannel(size int) <-chan *Event {
349
360
// After calling StartSync an initial burst of "add" events may be generated to
350
361
// report all the ports available at the moment of the start.
351
362
func (disc * PluggableDiscovery ) StartSync () error {
352
- disc .statusMutex .Lock ()
353
- defer disc .statusMutex .Unlock ()
354
-
355
- if disc .eventsMode {
356
- return errors .New (tr ("already in events mode" ))
357
- }
358
363
if err := disc .sendCommand ("START_SYNC\n " ); err != nil {
359
364
return err
360
365
}
361
366
362
367
if msg , err := disc .waitMessage (time .Second * 10 ); err != nil {
363
- return err
368
+ return fmt . Errorf ( "calling START_SYNC: %w" , err )
364
369
} else if msg .EventType != "start_sync" {
365
370
return errors .Errorf (tr ("communication out of sync, expected 'start_sync', received '%s'" ), msg .EventType )
366
371
} else if msg .Message != "OK" || msg .Error {
367
372
return errors .Errorf (tr ("command failed: %s" ), msg .Message )
368
373
}
369
374
370
- disc .eventsMode = true
375
+ disc .statusMutex .Lock ()
376
+ defer disc .statusMutex .Unlock ()
377
+ disc .state = Syncing
371
378
disc .cachedPorts = map [string ]* Port {}
372
379
return nil
373
380
}
0 commit comments