Skip to content

Commit 682c9ef

Browse files
committed
Moved functions into proper compilation unit
1 parent e66a6b2 commit 682c9ef

File tree

3 files changed

+175
-170
lines changed

3 files changed

+175
-170
lines changed

Diff for: commands/service_board_identify.go

+175
Original file line numberDiff line numberDiff line change
@@ -17,10 +17,26 @@ package commands
1717

1818
import (
1919
"context"
20+
"encoding/json"
21+
"errors"
22+
"fmt"
23+
"io"
24+
"net/http"
25+
"regexp"
26+
"sort"
27+
"strings"
28+
"time"
2029

30+
"github.com/arduino/arduino-cli/commands/cmderrors"
2131
"github.com/arduino/arduino-cli/commands/internal/instances"
32+
"github.com/arduino/arduino-cli/internal/arduino/cores/packagemanager"
33+
"github.com/arduino/arduino-cli/internal/cli/configuration"
34+
"github.com/arduino/arduino-cli/internal/i18n"
35+
"github.com/arduino/arduino-cli/internal/inventory"
36+
"github.com/arduino/arduino-cli/pkg/fqbn"
2237
rpc "github.com/arduino/arduino-cli/rpc/cc/arduino/cli/commands/v1"
2338
"github.com/arduino/go-properties-orderedmap"
39+
"github.com/sirupsen/logrus"
2440
)
2541

2642
// BoardIdentify identifies the board based on the provided properties
@@ -40,3 +56,162 @@ func (s *arduinoCoreServerImpl) BoardIdentify(ctx context.Context, req *rpc.Boar
4056
Boards: res,
4157
}, nil
4258
}
59+
60+
// identify returns a list of boards checking first the installed platforms or the Cloud API
61+
func identify(pme *packagemanager.Explorer, properties *properties.Map, settings *configuration.Settings, skipCloudAPI bool) ([]*rpc.BoardListItem, error) {
62+
if properties == nil {
63+
return nil, nil
64+
}
65+
66+
// first query installed cores through the Package Manager
67+
boards := []*rpc.BoardListItem{}
68+
logrus.Debug("Querying installed cores for board identification...")
69+
for _, board := range pme.IdentifyBoard(properties) {
70+
fqbn, err := fqbn.Parse(board.FQBN())
71+
if err != nil {
72+
return nil, &cmderrors.InvalidFQBNError{Cause: err}
73+
}
74+
fqbn.Configs = board.IdentifyBoardConfiguration(properties)
75+
76+
// We need the Platform maintaner for sorting so we set it here
77+
platform := &rpc.Platform{
78+
Metadata: &rpc.PlatformMetadata{
79+
Maintainer: board.PlatformRelease.Platform.Package.Maintainer,
80+
},
81+
}
82+
boards = append(boards, &rpc.BoardListItem{
83+
Name: board.Name(),
84+
Fqbn: fqbn.String(),
85+
IsHidden: board.IsHidden(),
86+
Platform: platform,
87+
})
88+
}
89+
90+
// if installed cores didn't recognize the board, try querying
91+
// the builder API if the board is a USB device port
92+
if len(boards) == 0 && !skipCloudAPI && !settings.SkipCloudApiForBoardDetection() {
93+
items, err := identifyViaCloudAPI(properties, settings)
94+
if err != nil {
95+
// this is bad, but keep going
96+
logrus.WithError(err).Debug("Error querying builder API")
97+
}
98+
boards = items
99+
}
100+
101+
// Sort by FQBN alphabetically
102+
sort.Slice(boards, func(i, j int) bool {
103+
return strings.ToLower(boards[i].GetFqbn()) < strings.ToLower(boards[j].GetFqbn())
104+
})
105+
106+
// Put Arduino boards before others in case there are non Arduino boards with identical VID:PID combination
107+
sort.SliceStable(boards, func(i, j int) bool {
108+
if boards[i].GetPlatform().GetMetadata().GetMaintainer() == "Arduino" && boards[j].GetPlatform().GetMetadata().GetMaintainer() != "Arduino" {
109+
return true
110+
}
111+
return false
112+
})
113+
114+
// We need the Board's Platform only for sorting but it shouldn't be present in the output
115+
for _, board := range boards {
116+
board.Platform = nil
117+
}
118+
119+
return boards, nil
120+
}
121+
122+
func identifyViaCloudAPI(props *properties.Map, settings *configuration.Settings) ([]*rpc.BoardListItem, error) {
123+
// If the port is not USB do not try identification via cloud
124+
if !props.ContainsKey("vid") || !props.ContainsKey("pid") {
125+
return nil, nil
126+
}
127+
128+
logrus.Debug("Querying builder API for board identification...")
129+
return cachedAPIByVidPid(props.Get("vid"), props.Get("pid"), settings)
130+
}
131+
132+
var (
133+
vidPidURL = "https://builder.arduino.cc/v3/boards/byVidPid"
134+
validVidPid = regexp.MustCompile(`0[xX][a-fA-F\d]{4}`)
135+
)
136+
137+
func cachedAPIByVidPid(vid, pid string, settings *configuration.Settings) ([]*rpc.BoardListItem, error) {
138+
var resp []*rpc.BoardListItem
139+
140+
cacheKey := fmt.Sprintf("cache.builder-api.v3/boards/byvid/pid/%s/%s", vid, pid)
141+
if cachedResp := inventory.Store.GetString(cacheKey + ".data"); cachedResp != "" {
142+
ts := inventory.Store.GetTime(cacheKey + ".ts")
143+
if time.Since(ts) < time.Hour*24 {
144+
// Use cached response
145+
if err := json.Unmarshal([]byte(cachedResp), &resp); err == nil {
146+
return resp, nil
147+
}
148+
}
149+
}
150+
151+
resp, err := apiByVidPid(vid, pid, settings) // Perform API requrest
152+
153+
if err == nil {
154+
if cachedResp, err := json.Marshal(resp); err == nil {
155+
inventory.Store.Set(cacheKey+".data", string(cachedResp))
156+
inventory.Store.Set(cacheKey+".ts", time.Now())
157+
inventory.WriteStore()
158+
}
159+
}
160+
return resp, err
161+
}
162+
163+
func apiByVidPid(vid, pid string, settings *configuration.Settings) ([]*rpc.BoardListItem, error) {
164+
// ensure vid and pid are valid before hitting the API
165+
if !validVidPid.MatchString(vid) {
166+
return nil, errors.New(i18n.Tr("Invalid vid value: '%s'", vid))
167+
}
168+
if !validVidPid.MatchString(pid) {
169+
return nil, errors.New(i18n.Tr("Invalid pid value: '%s'", pid))
170+
}
171+
172+
url := fmt.Sprintf("%s/%s/%s", vidPidURL, vid, pid)
173+
req, _ := http.NewRequest("GET", url, nil)
174+
req.Header.Set("Content-Type", "application/json")
175+
176+
httpClient, err := settings.NewHttpClient()
177+
if err != nil {
178+
return nil, fmt.Errorf("%s: %w", i18n.Tr("failed to initialize http client"), err)
179+
}
180+
181+
res, err := httpClient.Do(req)
182+
if err != nil {
183+
return nil, fmt.Errorf("%s: %w", i18n.Tr("error querying Arduino Cloud Api"), err)
184+
}
185+
if res.StatusCode == 404 {
186+
// This is not an error, it just means that the board is not recognized
187+
return nil, nil
188+
}
189+
if res.StatusCode >= 400 {
190+
return nil, errors.New(i18n.Tr("the server responded with status %s", res.Status))
191+
}
192+
193+
resp, err := io.ReadAll(res.Body)
194+
if err != nil {
195+
return nil, err
196+
}
197+
if err := res.Body.Close(); err != nil {
198+
return nil, err
199+
}
200+
201+
var dat map[string]interface{}
202+
if err := json.Unmarshal(resp, &dat); err != nil {
203+
return nil, fmt.Errorf("%s: %w", i18n.Tr("error processing response from server"), err)
204+
}
205+
name, nameFound := dat["name"].(string)
206+
fqbn, fbqnFound := dat["fqbn"].(string)
207+
if !nameFound || !fbqnFound {
208+
return nil, errors.New(i18n.Tr("wrong format in server response"))
209+
}
210+
211+
return []*rpc.BoardListItem{
212+
{
213+
Name: name,
214+
Fqbn: fqbn,
215+
},
216+
}, nil
217+
}
File renamed without changes.

Diff for: commands/service_board_list.go

-170
Original file line numberDiff line numberDiff line change
@@ -17,188 +17,18 @@ package commands
1717

1818
import (
1919
"context"
20-
"encoding/json"
2120
"errors"
22-
"fmt"
23-
"io"
24-
"net/http"
25-
"regexp"
26-
"sort"
27-
"strings"
2821
"time"
2922

3023
"github.com/arduino/arduino-cli/commands/cmderrors"
3124
"github.com/arduino/arduino-cli/commands/internal/instances"
3225
f "github.com/arduino/arduino-cli/internal/algorithms"
33-
"github.com/arduino/arduino-cli/internal/arduino/cores/packagemanager"
34-
"github.com/arduino/arduino-cli/internal/cli/configuration"
3526
"github.com/arduino/arduino-cli/internal/i18n"
36-
"github.com/arduino/arduino-cli/internal/inventory"
3727
"github.com/arduino/arduino-cli/pkg/fqbn"
3828
rpc "github.com/arduino/arduino-cli/rpc/cc/arduino/cli/commands/v1"
39-
"github.com/arduino/go-properties-orderedmap"
4029
"github.com/sirupsen/logrus"
4130
)
4231

43-
var (
44-
vidPidURL = "https://builder.arduino.cc/v3/boards/byVidPid"
45-
validVidPid = regexp.MustCompile(`0[xX][a-fA-F\d]{4}`)
46-
)
47-
48-
func cachedAPIByVidPid(vid, pid string, settings *configuration.Settings) ([]*rpc.BoardListItem, error) {
49-
var resp []*rpc.BoardListItem
50-
51-
cacheKey := fmt.Sprintf("cache.builder-api.v3/boards/byvid/pid/%s/%s", vid, pid)
52-
if cachedResp := inventory.Store.GetString(cacheKey + ".data"); cachedResp != "" {
53-
ts := inventory.Store.GetTime(cacheKey + ".ts")
54-
if time.Since(ts) < time.Hour*24 {
55-
// Use cached response
56-
if err := json.Unmarshal([]byte(cachedResp), &resp); err == nil {
57-
return resp, nil
58-
}
59-
}
60-
}
61-
62-
resp, err := apiByVidPid(vid, pid, settings) // Perform API requrest
63-
64-
if err == nil {
65-
if cachedResp, err := json.Marshal(resp); err == nil {
66-
inventory.Store.Set(cacheKey+".data", string(cachedResp))
67-
inventory.Store.Set(cacheKey+".ts", time.Now())
68-
inventory.WriteStore()
69-
}
70-
}
71-
return resp, err
72-
}
73-
74-
func apiByVidPid(vid, pid string, settings *configuration.Settings) ([]*rpc.BoardListItem, error) {
75-
// ensure vid and pid are valid before hitting the API
76-
if !validVidPid.MatchString(vid) {
77-
return nil, errors.New(i18n.Tr("Invalid vid value: '%s'", vid))
78-
}
79-
if !validVidPid.MatchString(pid) {
80-
return nil, errors.New(i18n.Tr("Invalid pid value: '%s'", pid))
81-
}
82-
83-
url := fmt.Sprintf("%s/%s/%s", vidPidURL, vid, pid)
84-
req, _ := http.NewRequest("GET", url, nil)
85-
req.Header.Set("Content-Type", "application/json")
86-
87-
httpClient, err := settings.NewHttpClient()
88-
if err != nil {
89-
return nil, fmt.Errorf("%s: %w", i18n.Tr("failed to initialize http client"), err)
90-
}
91-
92-
res, err := httpClient.Do(req)
93-
if err != nil {
94-
return nil, fmt.Errorf("%s: %w", i18n.Tr("error querying Arduino Cloud Api"), err)
95-
}
96-
if res.StatusCode == 404 {
97-
// This is not an error, it just means that the board is not recognized
98-
return nil, nil
99-
}
100-
if res.StatusCode >= 400 {
101-
return nil, errors.New(i18n.Tr("the server responded with status %s", res.Status))
102-
}
103-
104-
resp, err := io.ReadAll(res.Body)
105-
if err != nil {
106-
return nil, err
107-
}
108-
if err := res.Body.Close(); err != nil {
109-
return nil, err
110-
}
111-
112-
var dat map[string]interface{}
113-
if err := json.Unmarshal(resp, &dat); err != nil {
114-
return nil, fmt.Errorf("%s: %w", i18n.Tr("error processing response from server"), err)
115-
}
116-
name, nameFound := dat["name"].(string)
117-
fqbn, fbqnFound := dat["fqbn"].(string)
118-
if !nameFound || !fbqnFound {
119-
return nil, errors.New(i18n.Tr("wrong format in server response"))
120-
}
121-
122-
return []*rpc.BoardListItem{
123-
{
124-
Name: name,
125-
Fqbn: fqbn,
126-
},
127-
}, nil
128-
}
129-
130-
func identifyViaCloudAPI(props *properties.Map, settings *configuration.Settings) ([]*rpc.BoardListItem, error) {
131-
// If the port is not USB do not try identification via cloud
132-
if !props.ContainsKey("vid") || !props.ContainsKey("pid") {
133-
return nil, nil
134-
}
135-
136-
logrus.Debug("Querying builder API for board identification...")
137-
return cachedAPIByVidPid(props.Get("vid"), props.Get("pid"), settings)
138-
}
139-
140-
// identify returns a list of boards checking first the installed platforms or the Cloud API
141-
func identify(pme *packagemanager.Explorer, properties *properties.Map, settings *configuration.Settings, skipCloudAPI bool) ([]*rpc.BoardListItem, error) {
142-
if properties == nil {
143-
return nil, nil
144-
}
145-
146-
// first query installed cores through the Package Manager
147-
boards := []*rpc.BoardListItem{}
148-
logrus.Debug("Querying installed cores for board identification...")
149-
for _, board := range pme.IdentifyBoard(properties) {
150-
fqbn, err := fqbn.Parse(board.FQBN())
151-
if err != nil {
152-
return nil, &cmderrors.InvalidFQBNError{Cause: err}
153-
}
154-
fqbn.Configs = board.IdentifyBoardConfiguration(properties)
155-
156-
// We need the Platform maintaner for sorting so we set it here
157-
platform := &rpc.Platform{
158-
Metadata: &rpc.PlatformMetadata{
159-
Maintainer: board.PlatformRelease.Platform.Package.Maintainer,
160-
},
161-
}
162-
boards = append(boards, &rpc.BoardListItem{
163-
Name: board.Name(),
164-
Fqbn: fqbn.String(),
165-
IsHidden: board.IsHidden(),
166-
Platform: platform,
167-
})
168-
}
169-
170-
// if installed cores didn't recognize the board, try querying
171-
// the builder API if the board is a USB device port
172-
if len(boards) == 0 && !skipCloudAPI && !settings.SkipCloudApiForBoardDetection() {
173-
items, err := identifyViaCloudAPI(properties, settings)
174-
if err != nil {
175-
// this is bad, but keep going
176-
logrus.WithError(err).Debug("Error querying builder API")
177-
}
178-
boards = items
179-
}
180-
181-
// Sort by FQBN alphabetically
182-
sort.Slice(boards, func(i, j int) bool {
183-
return strings.ToLower(boards[i].GetFqbn()) < strings.ToLower(boards[j].GetFqbn())
184-
})
185-
186-
// Put Arduino boards before others in case there are non Arduino boards with identical VID:PID combination
187-
sort.SliceStable(boards, func(i, j int) bool {
188-
if boards[i].GetPlatform().GetMetadata().GetMaintainer() == "Arduino" && boards[j].GetPlatform().GetMetadata().GetMaintainer() != "Arduino" {
189-
return true
190-
}
191-
return false
192-
})
193-
194-
// We need the Board's Platform only for sorting but it shouldn't be present in the output
195-
for _, board := range boards {
196-
board.Platform = nil
197-
}
198-
199-
return boards, nil
200-
}
201-
20232
// BoardList returns a list of boards found by the loaded discoveries.
20333
// In case of errors partial results from discoveries that didn't fail
20434
// are returned.

0 commit comments

Comments
 (0)