Skip to content

Commit

Permalink
serial-discovery now talks Pluggable Discovery protocol v1
Browse files Browse the repository at this point in the history
  • Loading branch information
cmaglie committed Jun 18, 2021
1 parent b831d21 commit 6120543
Show file tree
Hide file tree
Showing 8 changed files with 185 additions and 70 deletions.
1 change: 1 addition & 0 deletions arduino/cores/packagemanager/identify.go
Original file line number Diff line number Diff line change
Expand Up @@ -60,5 +60,6 @@ func (pm *PackageManager) IdentifyBoard(idProps *properties.Map) []*cores.Board
id++
}
}

return foundBoards
}
77 changes: 54 additions & 23 deletions arduino/discovery/discovery.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import (
"sync"
"time"

"github.com/arduino/arduino-cli/cli/globals"
"github.com/arduino/arduino-cli/executils"
"github.com/arduino/go-properties-orderedmap"
"github.com/pkg/errors"
Expand All @@ -30,7 +31,6 @@ import (
// with the boards.
type PluggableDiscovery struct {
id string
args []string
process *executils.Process
outgoingCommandsPipe io.Writer
incomingMessagesChan <-chan *discoveryMessage
Expand All @@ -45,20 +45,21 @@ type PluggableDiscovery struct {
}

type discoveryMessage struct {
EventType string `json:"eventType"`
Message string `json:"message"`
Ports []*Port `json:"ports"`
Port *Port `json:"port"`
EventType string `json:"eventType"`
Message string `json:"message"`
Error bool `json:"error"`
ProtocolVersion int `json:"protocolVersion"` // Used in HELLO command
Ports []*Port `json:"ports"` // Used in LIST command
Port *Port `json:"port"` // Used in add and remove events
}

// Port containts metadata about a port to connect to a board.
type Port struct {
Address string `json:"address"`
AddressLabel string `json:"label"`
Protocol string `json:"protocol"`
ProtocolLabel string `json:"protocolLabel"`
Properties *properties.Map `json:"prefs"`
IdentificationProperties *properties.Map `json:"identificationPrefs"`
Address string `json:"address"`
AddressLabel string `json:"label"`
Protocol string `json:"protocol"`
ProtocolLabel string `json:"protocolLabel"`
Properties *properties.Map `json:"properties"`
}

func (p *Port) String() string {
Expand Down Expand Up @@ -121,20 +122,20 @@ func (disc *PluggableDiscovery) jsonDecodeLoop(in io.Reader, outChan chan<- *dis
disc.statusMutex.Unlock()
close(outChan)
}

for {
var msg discoveryMessage
if err := decoder.Decode(&msg); err != nil {
closeAndReportError(err)
return
}

if msg.EventType == "add" {
if msg.Port == nil {
closeAndReportError(errors.New("invalid 'add' message: missing port"))
return
}
disc.statusMutex.Lock()
disc.cachedPorts[msg.Port.Address] = msg.Port
disc.cachedPorts[msg.Port.Address+"|"+msg.Port.Protocol] = msg.Port
if disc.eventChan != nil {
disc.eventChan <- &Event{"add", msg.Port}
}
Expand All @@ -145,7 +146,7 @@ func (disc *PluggableDiscovery) jsonDecodeLoop(in io.Reader, outChan chan<- *dis
return
}
disc.statusMutex.Lock()
delete(disc.cachedPorts, msg.Port.Address)
delete(disc.cachedPorts, msg.Port.Address+"|"+msg.Port.Protocol)
if disc.eventChan != nil {
disc.eventChan <- &Event{"remove", msg.Port}
}
Expand Down Expand Up @@ -187,13 +188,35 @@ func (disc *PluggableDiscovery) waitMessage(timeout time.Duration) (*discoveryMe
}

func (disc *PluggableDiscovery) sendCommand(command string) error {
if n, err := disc.outgoingCommandsPipe.Write([]byte(command)); err != nil {
data := []byte(command)
for {
n, err := disc.outgoingCommandsPipe.Write(data)
if err != nil {
return err
}
if n == len(data) {
return nil
}
data = data[n:]
}
}

// Hello sends the HELLO command to the discovery to agree on the pluggable discovery protocol. This
// must be the first command to run in the communication with the discovery.
func (disc *PluggableDiscovery) Hello() error {
if err := disc.sendCommand("HELLO 1 \"arduino-cli " + globals.VersionInfo.VersionString + "\"\n"); err != nil {
return err
} else if n < len(command) {
return disc.sendCommand(command[n:])
} else {
return nil
}
if msg, err := disc.waitMessage(time.Second * 10); err != nil {
return err
} else if msg.EventType != "hello" {
return errors.Errorf("communication out of sync, expected 'hello', received '%s'", msg.EventType)
} else if msg.Message != "OK" || msg.Error {
return errors.Errorf("command failed: %s", msg.Message)
} else if msg.ProtocolVersion > 1 {
return errors.Errorf("protocol version not supported: requested 1, got %d", msg.ProtocolVersion)
}
return nil
}

// Start initializes and start the discovery internal subroutines. This command must be
Expand All @@ -206,7 +229,7 @@ func (disc *PluggableDiscovery) Start() error {
return err
} else if msg.EventType != "start" {
return errors.Errorf("communication out of sync, expected 'start', received '%s'", msg.EventType)
} else if msg.Message != "OK" {
} else if msg.Message != "OK" || msg.Error {
return errors.Errorf("command failed: %s", msg.Message)
}
return nil
Expand All @@ -223,7 +246,7 @@ func (disc *PluggableDiscovery) Stop() error {
return err
} else if msg.EventType != "stop" {
return errors.Errorf("communication out of sync, expected 'stop', received '%s'", msg.EventType)
} else if msg.Message != "OK" {
} else if msg.Message != "OK" || msg.Error {
return errors.Errorf("command failed: %s", msg.Message)
}
return nil
Expand All @@ -238,7 +261,7 @@ func (disc *PluggableDiscovery) Quit() error {
return err
} else if msg.EventType != "quit" {
return errors.Errorf("communication out of sync, expected 'quit', received '%s'", msg.EventType)
} else if msg.Message != "OK" {
} else if msg.Message != "OK" || msg.Error {
return errors.Errorf("command failed: %s", msg.Message)
}
return nil
Expand All @@ -254,6 +277,8 @@ func (disc *PluggableDiscovery) List() ([]*Port, error) {
return nil, err
} else if msg.EventType != "list" {
return nil, errors.Errorf("communication out of sync, expected 'list', received '%s'", msg.EventType)
} else if msg.Error {
return nil, errors.Errorf("command failed: %s", msg.Message)
} else {
return msg.Ports, nil
}
Expand Down Expand Up @@ -285,7 +310,13 @@ func (disc *PluggableDiscovery) StartSync() error {
return err
}

// START_SYNC does not give any response
if msg, err := disc.waitMessage(time.Second * 10); err != nil {
return err
} else if msg.EventType != "start_sync" {
return errors.Errorf("communication out of sync, expected 'start_sync', received '%s'", msg.EventType)
} else if msg.Message != "OK" || msg.Error {
return errors.Errorf("command failed: %s", msg.Message)
}

disc.eventsMode = true
disc.cachedPorts = map[string]*Port{}
Expand Down
Loading

0 comments on commit 6120543

Please sign in to comment.