Skip to content

Commit d94d178

Browse files
committed
Add flag to board list command to watch for connected and disconnected boards
1 parent 2931737 commit d94d178

File tree

3 files changed

+122
-28
lines changed

3 files changed

+122
-28
lines changed

Diff for: cli/board/list.go

+57
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ import (
2525
"github.com/arduino/arduino-cli/cli/errorcodes"
2626
"github.com/arduino/arduino-cli/cli/feedback"
2727
"github.com/arduino/arduino-cli/cli/instance"
28+
"github.com/arduino/arduino-cli/commands"
2829
"github.com/arduino/arduino-cli/commands/board"
2930
rpc "github.com/arduino/arduino-cli/rpc/commands"
3031
"github.com/arduino/arduino-cli/table"
@@ -43,15 +44,29 @@ func initListCommand() *cobra.Command {
4344

4445
listCommand.Flags().StringVar(&listFlags.timeout, "timeout", "0s",
4546
"The connected devices search timeout, raise it if your board doesn't show up (e.g. to 10s).")
47+
listCommand.Flags().BoolVarP(&listFlags.watch, "watch", "w", false,
48+
"Command keeps running and prints list of connected boards whenever there is a change.")
49+
4650
return listCommand
4751
}
4852

4953
var listFlags struct {
5054
timeout string // Expressed in a parsable duration, is the timeout for the list and attach commands.
55+
watch bool
5156
}
5257

5358
// runListCommand detects and lists the connected arduino boards
5459
func runListCommand(cmd *cobra.Command, args []string) {
60+
if listFlags.watch {
61+
inst, err := instance.CreateInstance()
62+
if err != nil {
63+
feedback.Errorf("Error detecting boards: %v", err)
64+
os.Exit(errorcodes.ErrGeneric)
65+
}
66+
watchList(inst)
67+
os.Exit(0)
68+
}
69+
5570
if timeout, err := time.ParseDuration(listFlags.timeout); err != nil {
5671
feedback.Errorf("Invalid timeout: %v", err)
5772
os.Exit(errorcodes.ErrBadArgument)
@@ -74,6 +89,48 @@ func runListCommand(cmd *cobra.Command, args []string) {
7489
feedback.PrintResult(result{ports})
7590
}
7691

92+
func watchList(inst *rpc.Instance) {
93+
pm := commands.GetPackageManager(inst.Id)
94+
eventsChan, err := commands.WatchListBoards(pm)
95+
if err != nil {
96+
feedback.Errorf("Error detecting boards: %v", err)
97+
os.Exit(errorcodes.ErrNetwork)
98+
}
99+
100+
boardPorts := map[string]*commands.BoardPort{}
101+
for event := range eventsChan {
102+
switch event.Type {
103+
case "add":
104+
boardPorts[event.Port.Address] = &commands.BoardPort{
105+
Address: event.Port.Address,
106+
Label: event.Port.AddressLabel,
107+
Prefs: event.Port.Properties,
108+
IdentificationPrefs: event.Port.IdentificationProperties,
109+
Protocol: event.Port.Protocol,
110+
ProtocolLabel: event.Port.ProtocolLabel,
111+
}
112+
case "remove":
113+
delete(boardPorts, event.Port.Address)
114+
}
115+
116+
ports := []*rpc.DetectedPort{}
117+
for _, p := range boardPorts {
118+
boardList, err := board.Identify(pm, p)
119+
if err != nil {
120+
feedback.Errorf("Error identifying board: %v", err)
121+
os.Exit(errorcodes.ErrNetwork)
122+
}
123+
ports = append(ports, &rpc.DetectedPort{
124+
Address: p.Address,
125+
Protocol: p.Protocol,
126+
ProtocolLabel: p.ProtocolLabel,
127+
Boards: boardList,
128+
})
129+
}
130+
feedback.PrintResult(result{ports})
131+
}
132+
}
133+
77134
// output from this command requires special formatting, let's create a dedicated
78135
// feedback.Result implementation
79136
type result struct {

Diff for: commands/board/list.go

+37-28
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ import (
2323
"regexp"
2424
"sync"
2525

26+
"github.com/arduino/arduino-cli/arduino/cores/packagemanager"
2627
"github.com/arduino/arduino-cli/commands"
2728
"github.com/arduino/arduino-cli/httpclient"
2829
rpc "github.com/arduino/arduino-cli/rpc/commands"
@@ -107,6 +108,38 @@ func identifyViaCloudAPI(port *commands.BoardPort) ([]*rpc.BoardListItem, error)
107108
return apiByVidPid(id.Get("vid"), id.Get("pid"))
108109
}
109110

111+
func Identify(pm *packagemanager.PackageManager, port *commands.BoardPort) ([]*rpc.BoardListItem, error) {
112+
boards := []*rpc.BoardListItem{}
113+
114+
// first query installed cores through the Package Manager
115+
logrus.Debug("Querying installed cores for board identification...")
116+
for _, board := range pm.IdentifyBoard(port.IdentificationPrefs) {
117+
boards = append(boards, &rpc.BoardListItem{
118+
Name: board.Name(),
119+
FQBN: board.FQBN(),
120+
})
121+
}
122+
123+
// if installed cores didn't recognize the board, try querying
124+
// the builder API if the board is a USB device port
125+
if len(boards) == 0 {
126+
items, err := identifyViaCloudAPI(port)
127+
if err == ErrNotFound {
128+
// the board couldn't be detected, print a warning
129+
logrus.Debug("Board not recognized")
130+
} else if err != nil {
131+
// this is bad, bail out
132+
return nil, errors.Wrap(err, "error getting board info from Arduino Cloud")
133+
}
134+
135+
// add a DetectedPort entry in any case: the `Boards` field will
136+
// be empty but the port will be shown anyways (useful for 3rd party
137+
// boards)
138+
boards = items
139+
}
140+
return boards, nil
141+
}
142+
110143
// List FIXMEDOC
111144
func List(instanceID int32) (r []*rpc.DetectedPort, e error) {
112145
m.Lock()
@@ -135,33 +168,9 @@ func List(instanceID int32) (r []*rpc.DetectedPort, e error) {
135168

136169
retVal := []*rpc.DetectedPort{}
137170
for _, port := range ports {
138-
b := []*rpc.BoardListItem{}
139-
140-
// first query installed cores through the Package Manager
141-
logrus.Debug("Querying installed cores for board identification...")
142-
for _, board := range pm.IdentifyBoard(port.IdentificationPrefs) {
143-
b = append(b, &rpc.BoardListItem{
144-
Name: board.Name(),
145-
FQBN: board.FQBN(),
146-
})
147-
}
148-
149-
// if installed cores didn't recognize the board, try querying
150-
// the builder API if the board is a USB device port
151-
if len(b) == 0 {
152-
items, err := identifyViaCloudAPI(port)
153-
if err == ErrNotFound {
154-
// the board couldn't be detected, print a warning
155-
logrus.Debug("Board not recognized")
156-
} else if err != nil {
157-
// this is bad, bail out
158-
return nil, errors.Wrap(err, "error getting board info from Arduino Cloud")
159-
}
160-
161-
// add a DetectedPort entry in any case: the `Boards` field will
162-
// be empty but the port will be shown anyways (useful for 3rd party
163-
// boards)
164-
b = items
171+
boards, err := Identify(pm, port)
172+
if err != nil {
173+
return nil, err
165174
}
166175

167176
// boards slice can be empty at this point if neither the cores nor the
@@ -170,7 +179,7 @@ func List(instanceID int32) (r []*rpc.DetectedPort, e error) {
170179
Address: port.Address,
171180
Protocol: port.Protocol,
172181
ProtocolLabel: port.ProtocolLabel,
173-
Boards: b,
182+
Boards: boards,
174183
}
175184
retVal = append(retVal, p)
176185
}

Diff for: commands/bundled_tools_serial_discovery.go

+28
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ import (
2323

2424
"github.com/arduino/arduino-cli/arduino/cores"
2525
"github.com/arduino/arduino-cli/arduino/cores/packagemanager"
26+
"github.com/arduino/arduino-cli/arduino/discovery"
2627
"github.com/arduino/arduino-cli/arduino/resources"
2728
"github.com/arduino/arduino-cli/executils"
2829
"github.com/arduino/go-properties-orderedmap"
@@ -193,6 +194,33 @@ func ListBoards(pm *packagemanager.PackageManager) ([]*BoardPort, error) {
193194
return retVal, finalError
194195
}
195196

197+
// WatchListBoards returns a channel that receives events from the bundled discovery tool
198+
func WatchListBoards(pm *packagemanager.PackageManager) (<-chan *discovery.Event, error) {
199+
t, err := getBuiltinSerialDiscoveryTool(pm)
200+
if err != nil {
201+
return nil, err
202+
}
203+
204+
if !t.IsInstalled() {
205+
return nil, fmt.Errorf("missing serial-discovery tool")
206+
}
207+
208+
disc, err := discovery.New("serial-discovery", t.InstallDir.Join(t.Tool.Name).String())
209+
if err != nil {
210+
return nil, err
211+
}
212+
213+
if err = disc.Start(); err != nil {
214+
return nil, fmt.Errorf("starting discovery: %v", err)
215+
}
216+
217+
if err = disc.StartSync(); err != nil {
218+
return nil, fmt.Errorf("starting sync: %v", err)
219+
}
220+
221+
return disc.EventChannel(10), nil
222+
}
223+
196224
func getBuiltinSerialDiscoveryTool(pm *packagemanager.PackageManager) (*cores.ToolRelease, error) {
197225
builtinPackage := pm.Packages.GetOrCreatePackage("builtin")
198226
ctagsTool := builtinPackage.GetOrCreateTool("serial-discovery")

0 commit comments

Comments
 (0)