Skip to content

Implementation of port discovery through Pluggable discovery #900

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 22 commits into from
Mar 20, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
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
Prev Previous commit
Next Next commit
Implemented serial-discovery
  • Loading branch information
cmaglie committed Mar 20, 2024
commit aba64eb4d22d1a2e5801a6439ec3ab57dfadc010
1 change: 1 addition & 0 deletions .licensed.yml
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ allowed:
# The following are based on: https://www.gnu.org/licenses/license-list.html#GPLCompatibleLicenses
- gpl-1.0-or-later
- gpl-1.0+ # Deprecated ID for `gpl-1.0-or-later`
- gpl-2.0
- gpl-2.0-or-later
- gpl-2.0+ # Deprecated ID for `gpl-2.0-or-later`
- gpl-3.0-only
Expand Down

Large diffs are not rendered by default.

Large diffs are not rendered by default.

2 changes: 2 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ require (
github.com/ProtonMail/go-crypto v1.1.0-alpha.0
github.com/arduino/go-paths-helper v1.12.0
github.com/arduino/go-serial-utils v0.1.2
github.com/arduino/pluggable-discovery-protocol-handler/v2 v2.2.0
github.com/blang/semver v3.5.1+incompatible
github.com/codeclysm/extract/v3 v3.1.1
github.com/gin-contrib/cors v1.5.0
Expand All @@ -28,6 +29,7 @@ require (

require (
github.com/AnatolyRugalev/goregen v0.1.0 // indirect
github.com/arduino/go-properties-orderedmap v1.8.0 // indirect
github.com/bytedance/sonic v1.10.1 // indirect
github.com/chenzhuoyu/base64x v0.0.0-20230717121745-296ad89f973d // indirect
github.com/chenzhuoyu/iasm v0.9.0 // indirect
Expand Down
5 changes: 5 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,15 @@ github.com/AnatolyRugalev/goregen v0.1.0 h1:xrdXkLaskMnbxW0x4FWNj2yoednv0X2bcTBW
github.com/AnatolyRugalev/goregen v0.1.0/go.mod h1:sVlY1tjcirqLBRZnCcIq1+7/Lwmqz5g7IK8AStjOVzI=
github.com/ProtonMail/go-crypto v1.1.0-alpha.0 h1:nHGfwXmFvJrSR9xu8qL7BkO4DqTHXE9N5vPhgY2I+j0=
github.com/ProtonMail/go-crypto v1.1.0-alpha.0/go.mod h1:rA3QumHc/FZ8pAHreoekgiAbzpNsfQAosU5td4SnOrE=
github.com/arduino/go-paths-helper v1.0.1/go.mod h1:HpxtKph+g238EJHq4geEPv9p+gl3v5YYu35Yb+w31Ck=
github.com/arduino/go-paths-helper v1.12.0 h1:xizOQtI9iHdl19qXd1EmWg5i9W//2bOCOYwlNv8F61E=
github.com/arduino/go-paths-helper v1.12.0/go.mod h1:jcpW4wr0u69GlXhTYydsdsqAjLaYK5n7oWHfKqOG6LM=
github.com/arduino/go-properties-orderedmap v1.8.0 h1:wEfa6hHdpezrVOh787OmClsf/Kd8qB+zE3P2Xbrn0CQ=
github.com/arduino/go-properties-orderedmap v1.8.0/go.mod h1:DKjD2VXY/NZmlingh4lSFMEYCVubfeArCsGPGDwb2yk=
github.com/arduino/go-serial-utils v0.1.2 h1:MRFwME4w/uaVkJ1R+wzz4KSbI9cF9IDVrYorazvjpTk=
github.com/arduino/go-serial-utils v0.1.2/go.mod h1:kzIsNPgz8DFAd1sAFKve4ubxrdGcwQ4XzvRLlztsgnE=
github.com/arduino/pluggable-discovery-protocol-handler/v2 v2.2.0 h1:v7og6LpskewFabmaShKVzWXl5MXbmsxaRP3yo4dJta8=
github.com/arduino/pluggable-discovery-protocol-handler/v2 v2.2.0/go.mod h1:1dgblsmK2iBx3L5iNTyRIokeaxbTLUrYiUbHBK6yC3Y=
github.com/blang/semver v3.5.1+incompatible h1:cQNTCjp13qL8KC3Nbxr/y2Bqb63oX6wdnnjpJbkM4JQ=
github.com/blang/semver v3.5.1+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk=
github.com/bytedance/sonic v1.5.0/go.mod h1:ED5hyg4y6t3/9Ku1R6dU/4KyJ48DZ4jPhfY1O2AihPM=
Expand Down
12 changes: 2 additions & 10 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,6 @@ import (
"github.com/arduino/arduino-create-agent/systray"
"github.com/arduino/arduino-create-agent/tools"
"github.com/arduino/arduino-create-agent/updater"
"github.com/arduino/arduino-create-agent/upload"
v2 "github.com/arduino/arduino-create-agent/v2"
paths "github.com/arduino/go-paths-helper"
cors "github.com/gin-contrib/cors"
Expand Down Expand Up @@ -343,22 +342,15 @@ func loop() {
}
}

// launch the discoveries for the running system
go serialPorts.Run()
// launch the hub routine which is the singleton for the websocket server
go h.run()
// launch our serial port routine
go sh.run()
// launch our dummy data routine
//go d.run()

go func() {
for {
if !upload.Busy {
serialPorts.Update()
}
time.Sleep(2 * time.Second)
}
}()

r := gin.New()

socketHandler := wsHandler().ServeHTTP
Expand Down
155 changes: 107 additions & 48 deletions serial.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,14 @@ package main

import (
"encoding/json"
"slices"
"strconv"
"strings"
"sync"
"time"

discovery "github.com/arduino/pluggable-discovery-protocol-handler/v2"
"github.com/sirupsen/logrus"
"go.bug.st/serial/enumerator"
)

type writeRequest struct {
Expand All @@ -51,9 +53,8 @@ type serialhub struct {

// SerialPortList is the serial port list
type SerialPortList struct {
Ports []SpPortItem
portsLock sync.Mutex
enumerationLock sync.Mutex
Ports []*SpPortItem
portsLock sync.Mutex
}

// SpPortItem is the serial port item
Expand Down Expand Up @@ -146,60 +147,118 @@ func (sp *SerialPortList) List() {
}
}

func (sp *SerialPortList) Update() {
if !sp.enumerationLock.TryLock() {
// already enumerating...
return
// Run is the main loop for port discovery and management
func (sp *SerialPortList) Run() {
for retries := 0; retries < 10; retries++ {
sp.runSerialDiscovery()

logrus.Errorf("Serial discovery stopped working, restarting it in 10 seconds...")
time.Sleep(10 * time.Second)
}
defer sp.enumerationLock.Unlock()
logrus.Errorf("Failed restarting serial discovery. Giving up...")
}

livePorts, _ := enumerator.GetDetailedPortsList()
// TODO: report error?
func (sp *SerialPortList) runSerialDiscovery() {
// First ensure that all the discoveries are available
if err := Tools.Download("builtin", "serial-discovery", "latest", "keep"); err != nil {
logrus.Errorf("Error downloading serial-discovery: %s", err)
panic(err)
}
sd, err := Tools.GetLocation("serial-discovery")
if err != nil {
logrus.Errorf("Error downloading serial-discovery: %s", err)
panic(err)
}
d := discovery.NewClient("serial", sd+"/serial-discovery")
dLogger := logrus.WithField("discovery", "serial")
if *verbose {
d.SetLogger(dLogger)
}
d.SetUserAgent("arduino-create-agent/" + version)
if err := d.Run(); err != nil {
logrus.Errorf("Error running serial-discovery: %s", err)
panic(err)
}
defer d.Quit()

var ports []SpPortItem
for _, livePort := range livePorts {
if !livePort.IsUSB {
continue
}
vid, pid := "0x"+livePort.VID, "0x"+livePort.PID
if vid == "0x0000" || pid == "0x0000" {
continue
}
if portsFilter != nil && !portsFilter.MatchString(livePort.Name) {
logrus.Debugf("ignoring port not matching filter. port: %v\n", livePort)
continue
}
events, err := d.StartSync(10)
if err != nil {
logrus.Errorf("Error downloading serial-discovery: %s", err)
panic(err)
}

port := SpPortItem{
Name: livePort.Name,
SerialNumber: livePort.SerialNumber,
VendorID: vid,
ProductID: pid,
Ver: version,
IsOpen: false,
IsPrimary: false,
Baud: 0,
BufferAlgorithm: "",
logrus.Infof("Serial discovery started, watching for events")
for ev := range events {
logrus.WithField("event", ev).Debugf("Serial discovery event")
switch ev.Type {
case "add":
sp.add(ev.Port)
case "remove":
sp.remove(ev.Port)
}
}

// we have a full clean list of ports now. iterate thru them
// to append the open/close state, baud rates, etc to make
// a super clean nice list to send back to browser
sp.reset()
logrus.Errorf("Serial discovery stopped.")
}

// figure out if port is open
if myport, isFound := sh.FindPortByName(port.Name); isFound {
// and update data with the open port parameters
port.IsOpen = true
port.Baud = myport.portConf.Baud
port.BufferAlgorithm = myport.BufferType
}
func (sp *SerialPortList) reset() {
sp.portsLock.Lock()
defer sp.portsLock.Unlock()
sp.Ports = []*SpPortItem{}
}

func (sp *SerialPortList) add(addedPort *discovery.Port) {
if addedPort.Protocol != "serial" {
return
}
props := addedPort.Properties
if !props.ContainsKey("vid") {
return
}
vid, pid := props.Get("vid"), props.Get("pid")
if vid == "0x0000" || pid == "0x0000" {
return
}
if portsFilter != nil && !portsFilter.MatchString(addedPort.Address) {
logrus.Debugf("ignoring port not matching filter. port: %v\n", addedPort.Address)
return
}

ports = append(ports, port)
sp.portsLock.Lock()
defer sp.portsLock.Unlock()

// If the port is already in the list, just update the metadata...
for _, oldPort := range sp.Ports {
if oldPort.Name == addedPort.Address {
oldPort.SerialNumber = props.Get("serialNumber")
oldPort.VendorID = vid
oldPort.ProductID = pid
return
}
}
// ...otherwise, add it to the list
sp.Ports = append(sp.Ports, &SpPortItem{
Name: addedPort.Address,
SerialNumber: props.Get("serialNumber"),
VendorID: vid,
ProductID: pid,
Ver: version,
IsOpen: false,
IsPrimary: false,
Baud: 0,
BufferAlgorithm: "",
})
}

func (sp *SerialPortList) remove(removedPort *discovery.Port) {
sp.portsLock.Lock()
defer sp.portsLock.Unlock()

serialPorts.portsLock.Lock()
serialPorts.Ports = ports
serialPorts.portsLock.Unlock()
// Remove the port from the list
sp.Ports = slices.DeleteFunc(sp.Ports, func(oldPort *SpPortItem) bool {
return oldPort.Name == removedPort.Address
})
}

func spErr(err string) {
Expand Down
4 changes: 0 additions & 4 deletions serialport.go
Original file line number Diff line number Diff line change
Expand Up @@ -232,7 +232,6 @@ func (p *serport) writerNoBuf() {
log.Println(msgstr)
h.broadcastSys <- []byte(msgstr)
p.portIo.Close()
serialPorts.Update()
serialPorts.List()
}

Expand Down Expand Up @@ -327,7 +326,6 @@ func spHandlerOpen(portname string, baud int, buftype string) {
sh.register <- p
defer func() { sh.unregister <- p }()

serialPorts.Update()
serialPorts.List()

// this is internally buffered thread to not send to serial port if blocked
Expand All @@ -339,7 +337,6 @@ func spHandlerOpen(portname string, baud int, buftype string) {

p.reader(buftype)

serialPorts.Update()
serialPorts.List()
}

Expand All @@ -352,6 +349,5 @@ func spHandlerClose(p *serport) {
func spCloseReal(p *serport) {
p.bufferwatcher.Close()
p.portIo.Close()
serialPorts.Update()
serialPorts.List()
}