Skip to content

Commit 89d4024

Browse files
committed
Introduce arduino-cli commands (#7)
1 parent af47d21 commit 89d4024

File tree

6 files changed

+907
-0
lines changed

6 files changed

+907
-0
lines changed

arduino/commander.go

+13
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
package arduino
2+
3+
import (
4+
rpc "github.com/arduino/arduino-cli/rpc/cc/arduino/cli/commands/v1"
5+
)
6+
7+
// Commander of arduino package allows to call
8+
// the arduino-cli commands in a programmatic way
9+
type Commander interface {
10+
BoardList() ([]*rpc.DetectedPort, error)
11+
UploadBin(fqbn, path, port string) error
12+
Compile() error
13+
}

arduino/grpc/board.go

+26
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
package grpc
2+
3+
import (
4+
"context"
5+
"fmt"
6+
7+
rpc "github.com/arduino/arduino-cli/rpc/cc/arduino/cli/commands/v1"
8+
)
9+
10+
type boardHandler struct {
11+
*service
12+
}
13+
14+
// BoardList executes the 'arduino-cli board list' command
15+
// and returns its result.
16+
func (b boardHandler) BoardList() ([]*rpc.DetectedPort, error) {
17+
boardListResp, err := b.serviceClient.BoardList(context.Background(),
18+
&rpc.BoardListRequest{Instance: b.instance})
19+
20+
if err != nil {
21+
err = fmt.Errorf("%s: %w", "Board list error", err)
22+
return nil, err
23+
}
24+
25+
return boardListResp.GetPorts(), nil
26+
}

arduino/grpc/client.go

+97
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
package grpc
2+
3+
import (
4+
"context"
5+
"fmt"
6+
"io"
7+
"time"
8+
9+
rpc "github.com/arduino/arduino-cli/rpc/cc/arduino/cli/commands/v1"
10+
"github.com/arduino/arduino-cli/rpc/cc/arduino/cli/settings/v1"
11+
"github.com/bcmi-labs/iot-cloud-cli/arduino"
12+
"google.golang.org/grpc"
13+
)
14+
15+
type service struct {
16+
serviceClient rpc.ArduinoCoreServiceClient
17+
settingsClient settings.SettingsServiceClient
18+
instance *rpc.Instance
19+
}
20+
21+
type client struct {
22+
boardHandler
23+
compileHandler
24+
}
25+
26+
// NewClient instantiates and returns a new grpc client that allows to
27+
// programmatically call arduino-cli commands.
28+
// It exploits the grpc interface of the arduino-cli.
29+
// It returns: the client instance, a callback to close the client and an error
30+
func NewClient() (arduino.Commander, func() error, error) {
31+
// Establish a connection with the gRPC server, started with the command:
32+
// arduino-cli daemon
33+
conn, err := grpc.Dial("localhost:50051", grpc.WithInsecure(), grpc.WithBlock(), grpc.WithTimeout(time.Second))
34+
if err != nil {
35+
err = fmt.Errorf("%s: %w", "cannot connect to arduino-cli rpc server, you can start it by running `arduino-cli daemon`", err)
36+
return nil, func() error { return nil }, err
37+
}
38+
39+
serv := &service{}
40+
// Create an instance of the gRPC clients.
41+
serv.serviceClient = rpc.NewArduinoCoreServiceClient(conn)
42+
serv.settingsClient = settings.NewSettingsServiceClient(conn)
43+
serv.instance, err = initInstance(serv.serviceClient)
44+
if err != nil {
45+
conn.Close()
46+
err = fmt.Errorf("%s: %w", "creating arduino-cli instance", err)
47+
return nil, func() error { return nil }, err
48+
}
49+
50+
cl := &client{}
51+
cl.boardHandler = boardHandler{serv}
52+
cl.compileHandler = compileHandler{serv}
53+
54+
return cl, conn.Close, nil
55+
}
56+
57+
func initInstance(client rpc.ArduinoCoreServiceClient) (*rpc.Instance, error) {
58+
initRespStream, err := client.Init(context.Background(), &rpc.InitRequest{})
59+
if err != nil {
60+
err = fmt.Errorf("%s: %w", "Error creating server instance", err)
61+
return nil, err
62+
}
63+
64+
var instance *rpc.Instance
65+
// Loop and consume the server stream until all the setup procedures are done.
66+
for {
67+
initResp, err := initRespStream.Recv()
68+
// The server is done.
69+
if err == io.EOF {
70+
break
71+
}
72+
73+
// There was an error.
74+
if err != nil {
75+
err = fmt.Errorf("%s: %w", "init error", err)
76+
return nil, err
77+
}
78+
79+
// The server sent us a valid instance, let's print its ID.
80+
if initResp.GetInstance() != nil {
81+
instance = initResp.GetInstance()
82+
//fmt.Printf("Got a new instance with ID: %v", instance.GetId())
83+
}
84+
85+
// When a download is ongoing, log the progress
86+
if initResp.GetDownloadProgress() != nil {
87+
fmt.Printf("DOWNLOAD: %s", initResp.GetDownloadProgress())
88+
}
89+
90+
// When an overall task is ongoing, log the progress
91+
if initResp.GetTaskProgress() != nil {
92+
fmt.Printf("TASK: %s", initResp.GetTaskProgress())
93+
}
94+
}
95+
96+
return instance, nil
97+
}

arduino/grpc/compile.go

+57
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
package grpc
2+
3+
import (
4+
"context"
5+
"fmt"
6+
"io"
7+
"path/filepath"
8+
9+
rpc "github.com/arduino/arduino-cli/rpc/cc/arduino/cli/commands/v1"
10+
)
11+
12+
type compileHandler struct {
13+
*service
14+
}
15+
16+
// Compile executes the 'arduino-cli compile' command
17+
// and returns its result.
18+
func (c compileHandler) Compile() error {
19+
return nil
20+
}
21+
22+
// Upload executes the 'arduino-cli upload -i' command
23+
// and returns its result.
24+
func (c compileHandler) UploadBin(fqbn, bin, port string) error {
25+
stream, err := c.serviceClient.Upload(context.Background(),
26+
&rpc.UploadRequest{
27+
Instance: c.instance,
28+
Fqbn: fqbn,
29+
SketchPath: filepath.Dir(bin),
30+
ImportFile: bin,
31+
Port: port,
32+
Verbose: true,
33+
})
34+
35+
if err != nil {
36+
err = fmt.Errorf("%s: %w", "uploading", err)
37+
return err
38+
}
39+
40+
// Wait for the upload to complete
41+
for {
42+
resp, err := stream.Recv()
43+
if err != nil {
44+
if err == io.EOF {
45+
break
46+
}
47+
err = fmt.Errorf("%s: %w", "errors during upload", err)
48+
return err
49+
}
50+
if resp.ErrStream != nil {
51+
err = fmt.Errorf("%s: %w", "errors during upload", err)
52+
return err
53+
}
54+
}
55+
56+
return nil
57+
}

go.mod

+13
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,16 @@
11
module github.com/bcmi-labs/iot-cloud-cli
22

33
go 1.15
4+
5+
require (
6+
github.com/arduino/arduino-cli v0.0.0-20210607095659-16f41352eac3
7+
github.com/bcmi-labs/oniudra-cli v0.15.8
8+
github.com/eclipse/paho.mqtt.golang v1.3.2
9+
github.com/juju/errors v0.0.0-20200330140219-3fe23663418f
10+
github.com/sirupsen/logrus v1.8.1
11+
github.com/spf13/cobra v1.1.3
12+
golang.org/x/net v0.0.0-20210505024714-0287a6fb4125 // indirect
13+
golang.org/x/sys v0.0.0-20210503173754-0981d6026fa6 // indirect
14+
google.golang.org/genproto v0.0.0-20210504143626-3b2ad6ccc450 // indirect
15+
google.golang.org/grpc v1.39.0
16+
)

0 commit comments

Comments
 (0)