Skip to content

Commit 75e09d8

Browse files
committed
serial-discovery now talks Pluggable Discovery protocol v1
1 parent b831d21 commit 75e09d8

File tree

8 files changed

+199
-70
lines changed

8 files changed

+199
-70
lines changed

arduino/cores/packagemanager/identify.go

+2
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ func (pm *PackageManager) IdentifyBoard(idProps *properties.Map) []*cores.Board
4141
}
4242
return false, true
4343
}
44+
// FIXME: TODO...
4445

4546
foundBoards := []*cores.Board{}
4647
for _, board := range pm.InstalledBoards() {
@@ -60,5 +61,6 @@ func (pm *PackageManager) IdentifyBoard(idProps *properties.Map) []*cores.Board
6061
id++
6162
}
6263
}
64+
6365
return foundBoards
6466
}

arduino/discovery/discovery.go

+67-23
Original file line numberDiff line numberDiff line change
@@ -17,20 +17,24 @@ package discovery
1717

1818
import (
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.
3136
type 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

4751
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
5258
}
5359

5460
// Port containts metadata about a port to connect to a board.
5561
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"`
6267
}
6368

6469
func (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

189200
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 {
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

Comments
 (0)