@@ -17,20 +17,24 @@ package discovery
1717
1818import (
1919 "encoding/json"
20+ "fmt"
2021 "io"
2122 "sync"
2223 "time"
2324
25+ "github.com/arduino/arduino-cli/cli/globals"
2426 "github.com/arduino/arduino-cli/executils"
2527 "github.com/arduino/go-properties-orderedmap"
2628 "github.com/pkg/errors"
2729)
2830
31+ // Set to true to enable debugging (only for development purposes)
32+ const debug = false
33+
2934// PluggableDiscovery is a tool that detects communication ports to interact
3035// with the boards.
3136type PluggableDiscovery struct {
3237 id string
33- args []string
3438 process * executils.Process
3539 outgoingCommandsPipe io.Writer
3640 incomingMessagesChan <- chan * discoveryMessage
@@ -45,20 +49,21 @@ type PluggableDiscovery struct {
4549}
4650
4751type 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
5258}
5359
5460// Port containts metadata about a port to connect to a board.
5561type 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"`
6267}
6368
6469func (p * Port ) String () string {
@@ -117,24 +122,30 @@ func (disc *PluggableDiscovery) jsonDecodeLoop(in io.Reader, outChan chan<- *dis
117122 closeAndReportError := func (err error ) {
118123 disc .statusMutex .Lock ()
119124 disc .alive = false
125+ if debug {
126+ fmt .Println ("ERR:" , err )
127+ }
120128 disc .incomingMessagesError = err
121129 disc .statusMutex .Unlock ()
122130 close (outChan )
123131 }
132+
124133 for {
125134 var msg discoveryMessage
126135 if err := decoder .Decode (& msg ); err != nil {
127136 closeAndReportError (err )
128137 return
129138 }
130-
139+ if debug {
140+ fmt .Println ("<" , msg )
141+ }
131142 if msg .EventType == "add" {
132143 if msg .Port == nil {
133144 closeAndReportError (errors .New ("invalid 'add' message: missing port" ))
134145 return
135146 }
136147 disc .statusMutex .Lock ()
137- disc .cachedPorts [msg .Port .Address ] = msg .Port
148+ disc .cachedPorts [msg .Port .Address + "|" + msg . Port . Protocol ] = msg .Port
138149 if disc .eventChan != nil {
139150 disc .eventChan <- & Event {"add" , msg .Port }
140151 }
@@ -145,7 +156,7 @@ func (disc *PluggableDiscovery) jsonDecodeLoop(in io.Reader, outChan chan<- *dis
145156 return
146157 }
147158 disc .statusMutex .Lock ()
148- delete (disc .cachedPorts , msg .Port .Address )
159+ delete (disc .cachedPorts , msg .Port .Address + "|" + msg . Port . Protocol )
149160 if disc .eventChan != nil {
150161 disc .eventChan <- & Event {"remove" , msg .Port }
151162 }
@@ -187,13 +198,38 @@ func (disc *PluggableDiscovery) waitMessage(timeout time.Duration) (*discoveryMe
187198}
188199
189200func (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 {
191221 return err
192- } else if n < len (command ) {
193- return disc .sendCommand (command [n :])
194- } else {
195- return nil
196222 }
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
197233}
198234
199235// Start initializes and start the discovery internal subroutines. This command must be
@@ -206,7 +242,7 @@ func (disc *PluggableDiscovery) Start() error {
206242 return err
207243 } else if msg .EventType != "start" {
208244 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 {
210246 return errors .Errorf ("command failed: %s" , msg .Message )
211247 }
212248 return nil
@@ -223,7 +259,7 @@ func (disc *PluggableDiscovery) Stop() error {
223259 return err
224260 } else if msg .EventType != "stop" {
225261 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 {
227263 return errors .Errorf ("command failed: %s" , msg .Message )
228264 }
229265 return nil
@@ -238,7 +274,7 @@ func (disc *PluggableDiscovery) Quit() error {
238274 return err
239275 } else if msg .EventType != "quit" {
240276 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 {
242278 return errors .Errorf ("command failed: %s" , msg .Message )
243279 }
244280 return nil
@@ -254,6 +290,8 @@ func (disc *PluggableDiscovery) List() ([]*Port, error) {
254290 return nil , err
255291 } else if msg .EventType != "list" {
256292 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 )
257295 } else {
258296 return msg .Ports , nil
259297 }
@@ -285,7 +323,13 @@ func (disc *PluggableDiscovery) StartSync() error {
285323 return err
286324 }
287325
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+ }
289333
290334 disc .eventsMode = true
291335 disc .cachedPorts = map [string ]* Port {}
0 commit comments