Skip to content

gRPC interface to monitors #286

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 9 commits into from
Jul 29, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Taskfile.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ tasks:
desc: Compile protobuf definitions
cmds:
- '{{ default "protoc" .PROTOC_BINARY }} --proto_path=rpc --go_out=plugins=grpc,paths=source_relative:rpc ./rpc/commands/*.proto'
- '{{ default "protoc" .PROTOC_BINARY }} --proto_path=rpc --go_out=plugins=grpc,paths=source_relative:rpc ./rpc/monitor/*.proto'

build:
desc: Build the project
Expand Down
4 changes: 2 additions & 2 deletions appveyor.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ stack: go 1.12
install:
# install the task executor
- curl -o task.zip -LO https://github.com/go-task/task/releases/download/v2.6.0/task_windows_amd64.zip
- 7z e task.zip -o%GOPATH%\bin
- 7z x task.zip -o%GOPATH%\bin
# golang dependencies needed at test time
- go get github.com/golangci/govet
- go get golang.org/x/lint/golint
Expand All @@ -30,7 +30,7 @@ install:
# because of this: https://github.com/protocolbuffers/protobuf/issues/3957
- go get github.com/golang/protobuf/protoc-gen-go
- curl -o protoc.zip -LO https://github.com/protocolbuffers/protobuf/releases/download/v3.4.0/protoc-3.4.0-win32.zip
- 7z e protoc.zip -o%PROTOC_PATH%
- 7z x protoc.zip -o%PROTOC_PATH%

test_script:
# Check if the Go code is properly formatted and run the linter
Expand Down
62 changes: 62 additions & 0 deletions arduino/monitors/serial.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
// This file is part of arduino-cli.
//
// Copyright 2019 ARDUINO SA (http://www.arduino.cc/)
//
// This software is released under the GNU General Public License version 3,
// which covers the main part of arduino-cli.
// The terms of this license can be found at:
// https://www.gnu.org/licenses/gpl-3.0.en.html
//
// You can be released from the requirements of the above licenses by purchasing
// a commercial license. Buying such a license is mandatory if you want to modify or
// otherwise use the software for commercial activities involving the Arduino
// software without disclosing the source code of your own applications. To purchase
// a commercial license, send an email to [email protected].

package monitors

import (
"github.com/pkg/errors"
serial "go.bug.st/serial.v1"
)

const (
defaultBaudRate = 9600
)

// SerialMonitor is a monitor for serial ports
type SerialMonitor struct {
port serial.Port
}

// OpenSerialMonitor creates a monitor instance for a serial port
func OpenSerialMonitor(portName string, baudRate int) (*SerialMonitor, error) {
// use default baud rate if not provided
if baudRate == 0 {
baudRate = defaultBaudRate
}

port, err := serial.Open(portName, &serial.Mode{BaudRate: baudRate})
if err != nil {
return nil, errors.Wrap(err, "error opening serial monitor")
}

return &SerialMonitor{
port: port,
}, nil
}

// Close the connection
func (mon *SerialMonitor) Close() error {
return mon.port.Close()
}

// Read bytes from the port
func (mon *SerialMonitor) Read(bytes []byte) (int, error) {
return mon.port.Read(bytes)
}

// Write bytes to the port
func (mon *SerialMonitor) Write(bytes []byte) (int, error) {
return mon.port.Write(bytes)
}
25 changes: 25 additions & 0 deletions arduino/monitors/types.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
// This file is part of arduino-cli.
//
// Copyright 2019 ARDUINO SA (http://www.arduino.cc/)
//
// This software is released under the GNU General Public License version 3,
// which covers the main part of arduino-cli.
// The terms of this license can be found at:
// https://www.gnu.org/licenses/gpl-3.0.en.html
//
// You can be released from the requirements of the above licenses by purchasing
// a commercial license. Buying such a license is mandatory if you want to modify or
// otherwise use the software for commercial activities involving the Arduino
// software without disclosing the source code of your own applications. To purchase
// a commercial license, send an email to [email protected].

package monitors

import (
"io"
)

// Monitor is the interface implemented by different device monitors
type Monitor interface {
io.ReadWriteCloser
}
11 changes: 9 additions & 2 deletions cli/daemon/daemon.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,8 @@ import (

"github.com/arduino/arduino-cli/cli/globals"
"github.com/arduino/arduino-cli/commands/daemon"
rpc "github.com/arduino/arduino-cli/rpc/commands"
srv_commands "github.com/arduino/arduino-cli/rpc/commands"
srv_monitor "github.com/arduino/arduino-cli/rpc/monitor"
"github.com/spf13/cobra"
"google.golang.org/grpc"
)
Expand Down Expand Up @@ -59,14 +60,20 @@ func runDaemonCommand(cmd *cobra.Command, args []string) {
globals.VersionInfo.VersionString, runtime.GOARCH, runtime.GOOS, runtime.Version(), globals.VersionInfo.Commit)
headers := http.Header{"User-Agent": []string{userAgentValue}}

// register the commands service
coreServer := daemon.ArduinoCoreServerImpl{
DownloaderHeaders: headers,
VersionString: globals.VersionInfo.VersionString,
Config: globals.Config,
}
rpc.RegisterArduinoCoreServer(s, &coreServer)
srv_commands.RegisterArduinoCoreServer(s, &coreServer)

// register the monitors service
srv_monitor.RegisterMonitorServer(s, &daemon.MonitorService{})

if err := s.Serve(lis); err != nil {
log.Fatalf("failed to serve: %v", err)
}

fmt.Println("Done serving")
}
134 changes: 134 additions & 0 deletions commands/daemon/monitor.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
// This file is part of arduino-cli.
//
// Copyright 2019 ARDUINO SA (http://www.arduino.cc/)
//
// This software is released under the GNU General Public License version 3,
// which covers the main part of arduino-cli.
// The terms of this license can be found at:
// https://www.gnu.org/licenses/gpl-3.0.en.html
//
// You can be released from the requirements of the above licenses by purchasing
// a commercial license. Buying such a license is mandatory if you want to
// modify or otherwise use the software for commercial activities involving the
// Arduino software without disclosing the source code of your own applications.
// To purchase a commercial license, send an email to [email protected].

package daemon

import (
"fmt"
"io"

"github.com/arduino/arduino-cli/arduino/monitors"
rpc "github.com/arduino/arduino-cli/rpc/monitor"
)

// MonitorService implements the `Monitor` service
type MonitorService struct{}

// StreamingOpen returns a stream response that can be used to fetch data from the
// monitor target. The first message passed through the `StreamingOpenReq` must
// contain monitor configuration params, not data.
func (s *MonitorService) StreamingOpen(stream rpc.Monitor_StreamingOpenServer) error {
// grab the first message
msg, err := stream.Recv()
if err != nil {
return err
}

// ensure it's a config message and not data
config := msg.GetMonitorConfig()
if config == nil {
return fmt.Errorf("first message must contain monitor configuration, not data")
}

// select which type of monitor we need
var mon monitors.Monitor
switch config.GetType() {
case rpc.MonitorConfig_SERIAL:
// grab port speed from additional config data
var baudRate float64
addCfg := config.GetAdditionalConfig()
for k, v := range addCfg.GetFields() {
if k == "BaudRate" {
baudRate = v.GetNumberValue()
break
}
}

// get the Monitor instance
var err error
if mon, err = monitors.OpenSerialMonitor(config.GetTarget(), int(baudRate)); err != nil {
return err
}
}

// we'll use these channels to communicate with the goroutines
// handling the stream and the target respectively
streamClosed := make(chan error)
targetClosed := make(chan error)

// now we can read the other messages and re-route to the monitor...
go func() {
for {
msg, err := stream.Recv()
if err == io.EOF {
// stream was closed
streamClosed <- nil
break
}

if err != nil {
// error reading from stream
streamClosed <- err
break
}

if _, err := mon.Write(msg.GetData()); err != nil {
// error writing to target
targetClosed <- err
break
}
}
}()

// ...and read from the monitor and forward to the output stream
go func() {
buf := make([]byte, 8)
for {
n, err := mon.Read(buf)
if err != nil {
// error reading from target
targetClosed <- err
break
}

if n == 0 {
// target was closed
targetClosed <- nil
break
}

if err = stream.Send(&rpc.StreamingOpenResp{
Data: buf[:n],
}); err != nil {
// error sending to stream
streamClosed <- err
break
}
}
}()

// let goroutines route messages from/to the monitor
// until either the client closes the stream or the
// monitor target is closed
for {
select {
case err := <-streamClosed:
mon.Close()
return err
case err := <-targetClosed:
return err
}
}
}
Loading