@@ -17,20 +17,24 @@ package discovery
17
17
18
18
import (
19
19
"encoding/json"
20
+ "fmt"
20
21
"io"
21
22
"sync"
22
23
"time"
23
24
25
+ "github.com/arduino/arduino-cli/cli/globals"
24
26
"github.com/arduino/arduino-cli/executils"
25
27
"github.com/arduino/go-properties-orderedmap"
26
28
"github.com/pkg/errors"
27
29
)
28
30
31
+ // Set to true to enable debugging (only for development purposes)
32
+ const debug = false
33
+
29
34
// PluggableDiscovery is a tool that detects communication ports to interact
30
35
// with the boards.
31
36
type PluggableDiscovery struct {
32
37
id string
33
- args []string
34
38
process * executils.Process
35
39
outgoingCommandsPipe io.Writer
36
40
incomingMessagesChan <- chan * discoveryMessage
@@ -45,20 +49,21 @@ type PluggableDiscovery struct {
45
49
}
46
50
47
51
type discoveryMessage struct {
48
- EventType string `json:"eventType"`
49
- Message string `json:"message"`
50
- Ports []* Port `json:"ports"`
51
- Port * Port `json:"port"`
52
+ EventType string `json:"eventType"`
53
+ Message string `json:"message"`
54
+ Error bool `json:"error"`
55
+ ProtocolVersion int `json:"protocolVersion"` // Used in HELLO command
56
+ Ports []* Port `json:"ports"` // Used in LIST command
57
+ Port * Port `json:"port"` // Used in add and remove events
52
58
}
53
59
54
60
// Port containts metadata about a port to connect to a board.
55
61
type Port struct {
56
- Address string `json:"address"`
57
- AddressLabel string `json:"label"`
58
- Protocol string `json:"protocol"`
59
- ProtocolLabel string `json:"protocolLabel"`
60
- Properties * properties.Map `json:"prefs"`
61
- IdentificationProperties * properties.Map `json:"identificationPrefs"`
62
+ Address string `json:"address"`
63
+ AddressLabel string `json:"label"`
64
+ Protocol string `json:"protocol"`
65
+ ProtocolLabel string `json:"protocolLabel"`
66
+ Properties * properties.Map `json:"properties"`
62
67
}
63
68
64
69
func (p * Port ) String () string {
@@ -117,24 +122,30 @@ func (disc *PluggableDiscovery) jsonDecodeLoop(in io.Reader, outChan chan<- *dis
117
122
closeAndReportError := func (err error ) {
118
123
disc .statusMutex .Lock ()
119
124
disc .alive = false
125
+ if debug {
126
+ fmt .Println ("ERR:" , err )
127
+ }
120
128
disc .incomingMessagesError = err
121
129
disc .statusMutex .Unlock ()
122
130
close (outChan )
123
131
}
132
+
124
133
for {
125
134
var msg discoveryMessage
126
135
if err := decoder .Decode (& msg ); err != nil {
127
136
closeAndReportError (err )
128
137
return
129
138
}
130
-
139
+ if debug {
140
+ fmt .Println ("<" , msg )
141
+ }
131
142
if msg .EventType == "add" {
132
143
if msg .Port == nil {
133
144
closeAndReportError (errors .New ("invalid 'add' message: missing port" ))
134
145
return
135
146
}
136
147
disc .statusMutex .Lock ()
137
- disc .cachedPorts [msg .Port .Address ] = msg .Port
148
+ disc .cachedPorts [msg .Port .Address + "|" + msg . Port . Protocol ] = msg .Port
138
149
if disc .eventChan != nil {
139
150
disc .eventChan <- & Event {"add" , msg .Port }
140
151
}
@@ -145,7 +156,7 @@ func (disc *PluggableDiscovery) jsonDecodeLoop(in io.Reader, outChan chan<- *dis
145
156
return
146
157
}
147
158
disc .statusMutex .Lock ()
148
- delete (disc .cachedPorts , msg .Port .Address )
159
+ delete (disc .cachedPorts , msg .Port .Address + "|" + msg . Port . Protocol )
149
160
if disc .eventChan != nil {
150
161
disc .eventChan <- & Event {"remove" , msg .Port }
151
162
}
@@ -187,13 +198,38 @@ func (disc *PluggableDiscovery) waitMessage(timeout time.Duration) (*discoveryMe
187
198
}
188
199
189
200
func (disc * PluggableDiscovery ) sendCommand (command string ) error {
190
- if n , err := disc .outgoingCommandsPipe .Write ([]byte (command )); err != nil {
201
+ if debug {
202
+ fmt .Println ("> " + command )
203
+ }
204
+ data := []byte (command )
205
+ for {
206
+ n , err := disc .outgoingCommandsPipe .Write (data )
207
+ if err != nil {
208
+ return err
209
+ }
210
+ if n == len (data ) {
211
+ return nil
212
+ }
213
+ data = data [n :]
214
+ }
215
+ }
216
+
217
+ // Hello sends the HELLO command to the discovery to agree on the pluggable discovery protocol. This
218
+ // must be the first command to run in the communication with the discovery.
219
+ func (disc * PluggableDiscovery ) Hello () error {
220
+ if err := disc .sendCommand ("HELLO 1 \" arduino-cli " + globals .VersionInfo .VersionString + "\" \n " ); err != nil {
191
221
return err
192
- } else if n < len (command ) {
193
- return disc .sendCommand (command [n :])
194
- } else {
195
- return nil
196
222
}
223
+ if msg , err := disc .waitMessage (time .Second * 10 ); err != nil {
224
+ return err
225
+ } else if msg .EventType != "hello" {
226
+ return errors .Errorf ("communication out of sync, expected 'hello', received '%s'" , msg .EventType )
227
+ } else if msg .Message != "OK" || msg .Error {
228
+ return errors .Errorf ("command failed: %s" , msg .Message )
229
+ } else if msg .ProtocolVersion > 1 {
230
+ return errors .Errorf ("protocol version not supported: requested 1, got %d" , msg .ProtocolVersion )
231
+ }
232
+ return nil
197
233
}
198
234
199
235
// Start initializes and start the discovery internal subroutines. This command must be
@@ -206,7 +242,7 @@ func (disc *PluggableDiscovery) Start() error {
206
242
return err
207
243
} else if msg .EventType != "start" {
208
244
return errors .Errorf ("communication out of sync, expected 'start', received '%s'" , msg .EventType )
209
- } else if msg .Message != "OK" {
245
+ } else if msg .Message != "OK" || msg . Error {
210
246
return errors .Errorf ("command failed: %s" , msg .Message )
211
247
}
212
248
return nil
@@ -223,7 +259,7 @@ func (disc *PluggableDiscovery) Stop() error {
223
259
return err
224
260
} else if msg .EventType != "stop" {
225
261
return errors .Errorf ("communication out of sync, expected 'stop', received '%s'" , msg .EventType )
226
- } else if msg .Message != "OK" {
262
+ } else if msg .Message != "OK" || msg . Error {
227
263
return errors .Errorf ("command failed: %s" , msg .Message )
228
264
}
229
265
return nil
@@ -238,7 +274,7 @@ func (disc *PluggableDiscovery) Quit() error {
238
274
return err
239
275
} else if msg .EventType != "quit" {
240
276
return errors .Errorf ("communication out of sync, expected 'quit', received '%s'" , msg .EventType )
241
- } else if msg .Message != "OK" {
277
+ } else if msg .Message != "OK" || msg . Error {
242
278
return errors .Errorf ("command failed: %s" , msg .Message )
243
279
}
244
280
return nil
@@ -254,6 +290,8 @@ func (disc *PluggableDiscovery) List() ([]*Port, error) {
254
290
return nil , err
255
291
} else if msg .EventType != "list" {
256
292
return nil , errors .Errorf ("communication out of sync, expected 'list', received '%s'" , msg .EventType )
293
+ } else if msg .Error {
294
+ return nil , errors .Errorf ("command failed: %s" , msg .Message )
257
295
} else {
258
296
return msg .Ports , nil
259
297
}
@@ -285,7 +323,13 @@ func (disc *PluggableDiscovery) StartSync() error {
285
323
return err
286
324
}
287
325
288
- // START_SYNC does not give any response
326
+ if msg , err := disc .waitMessage (time .Second * 10 ); err != nil {
327
+ return err
328
+ } else if msg .EventType != "start_sync" {
329
+ return errors .Errorf ("communication out of sync, expected 'start_sync', received '%s'" , msg .EventType )
330
+ } else if msg .Message != "OK" || msg .Error {
331
+ return errors .Errorf ("command failed: %s" , msg .Message )
332
+ }
289
333
290
334
disc .eventsMode = true
291
335
disc .cachedPorts = map [string ]* Port {}
0 commit comments