From 5bd891093a631060be71bbb826544328173c5b9f Mon Sep 17 00:00:00 2001 From: Cristian Maglie Date: Wed, 23 Apr 2025 13:23:00 +0200 Subject: [PATCH 1/4] Synchronize multiple open commands coming together. --- serialport.go | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/serialport.go b/serialport.go index 0d386bbf..1cc0b9e9 100755 --- a/serialport.go +++ b/serialport.go @@ -20,6 +20,7 @@ import ( "encoding/base64" "io" "strconv" + "sync" "sync/atomic" "time" "unicode/utf8" @@ -273,7 +274,11 @@ func (p *serport) writerRaw() { h.broadcastSys <- []byte(msgstr) } +var spHandlerLock sync.Mutex + func spHandlerOpen(portname string, baud int, buftype string) { + spHandlerLock.Lock() + defer spHandlerLock.Unlock() log.Print("Inside spHandler") @@ -331,7 +336,6 @@ func spHandlerOpen(portname string, baud int, buftype string) { p.bufferwatcher = bw sh.Register(p) - defer sh.Unregister(p) serialPorts.MarkPortAsOpened(portname) serialPorts.List() @@ -342,10 +346,12 @@ func spHandlerOpen(portname string, baud int, buftype string) { go p.writerNoBuf() // this is thread to send to serial port but with base64 decoding go p.writerRaw() - - p.reader(buftype) - - serialPorts.List() + // this is the thread that reads from the serial port + go func() { + p.reader(buftype) + serialPorts.List() + sh.Unregister(p) + }() } func (p *serport) Close() { From ac3d1f9bc8adff371488020384ca09834ceaebf9 Mon Sep 17 00:00:00 2001 From: Cristian Maglie Date: Wed, 23 Apr 2025 18:28:40 +0200 Subject: [PATCH 2/4] Allow re-opening of already opened ports --- serial.go | 19 ++++++------------- serialport.go | 12 +++++++----- 2 files changed, 13 insertions(+), 18 deletions(-) diff --git a/serial.go b/serial.go index 64e5b8f7..1a43f364 100755 --- a/serial.go +++ b/serial.go @@ -31,7 +31,7 @@ import ( type serialhub struct { // Opened serial ports. - ports map[*serport]bool + ports map[string]*serport mu sync.Mutex } @@ -60,7 +60,7 @@ type SpPortItem struct { var serialPorts SerialPortList var sh = serialhub{ - ports: make(map[*serport]bool), + ports: make(map[string]*serport), } // Register serial ports from the connections. @@ -68,7 +68,7 @@ func (sh *serialhub) Register(port *serport) { sh.mu.Lock() //log.Print("Registering a port: ", p.portConf.Name) h.broadcastSys <- []byte("{\"Cmd\":\"Open\",\"Desc\":\"Got register/open on port.\",\"Port\":\"" + port.portConf.Name + "\",\"Baud\":" + strconv.Itoa(port.portConf.Baud) + ",\"BufferType\":\"" + port.BufferType + "\"}") - sh.ports[port] = true + sh.ports[port.portName] = port sh.mu.Unlock() } @@ -77,7 +77,7 @@ func (sh *serialhub) Unregister(port *serport) { sh.mu.Lock() //log.Print("Unregistering a port: ", p.portConf.Name) h.broadcastSys <- []byte("{\"Cmd\":\"Close\",\"Desc\":\"Got unregister/close on port.\",\"Port\":\"" + port.portConf.Name + "\",\"Baud\":" + strconv.Itoa(port.portConf.Baud) + "}") - delete(sh.ports, port) + delete(sh.ports, port.portName) close(port.sendBuffered) close(port.sendNoBuf) sh.mu.Unlock() @@ -86,15 +86,8 @@ func (sh *serialhub) Unregister(port *serport) { func (sh *serialhub) FindPortByName(portname string) (*serport, bool) { sh.mu.Lock() defer sh.mu.Unlock() - - for port := range sh.ports { - if strings.EqualFold(port.portConf.Name, portname) { - // we found our port - //spHandlerClose(port) - return port, true - } - } - return nil, false + port, ok := sh.ports[portname] + return port, ok } // List broadcasts a Json representation of the ports found diff --git a/serialport.go b/serialport.go index 1cc0b9e9..6704165f 100755 --- a/serialport.go +++ b/serialport.go @@ -300,11 +300,13 @@ func spHandlerOpen(portname string, baud int, buftype string) { sp, err := serial.Open(portname, mode) log.Print("Just tried to open port") if err != nil { - //log.Fatal(err) - log.Print("Error opening port " + err.Error()) - //h.broadcastSys <- []byte("Error opening port. " + err.Error()) - h.broadcastSys <- []byte("{\"Cmd\":\"OpenFail\",\"Desc\":\"Error opening port. " + err.Error() + "\",\"Port\":\"" + conf.Name + "\",\"Baud\":" + strconv.Itoa(conf.Baud) + "}") - + if existingPort, ok := sh.FindPortByName(portname); ok { + log.Print("Port already opened") + h.broadcastSys <- []byte("{\"Cmd\":\"Open\",\"Desc\":\"Port already opened.\",\"Port\":\"" + existingPort.portConf.Name + "\",\"Baud\":" + strconv.Itoa(existingPort.portConf.Baud) + ",\"BufferType\":\"" + existingPort.BufferType + "\"}") + } else { + log.Print("Error opening port " + err.Error()) + h.broadcastSys <- []byte("{\"Cmd\":\"OpenFail\",\"Desc\":\"Error opening port. " + err.Error() + "\",\"Port\":\"" + conf.Name + "\",\"Baud\":" + strconv.Itoa(conf.Baud) + "}") + } return } log.Print("Opened port successfully") From 59e8c0b794304410142ef1a3b8af6fc5406dd8d0 Mon Sep 17 00:00:00 2001 From: Cristian Maglie Date: Thu, 24 Apr 2025 12:45:44 +0200 Subject: [PATCH 3/4] Renamed mutex var and added comment --- serialport.go | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/serialport.go b/serialport.go index 6704165f..8176cde5 100755 --- a/serialport.go +++ b/serialport.go @@ -274,11 +274,13 @@ func (p *serport) writerRaw() { h.broadcastSys <- []byte(msgstr) } -var spHandlerLock sync.Mutex +// This lock is used to prevent multiple threads from trying to open the same port at the same time. +// It presents issues with the serial port driver on some OS's: https://github.com/arduino/arduino-create-agent/issues/1031 +var spHandlerOpenLock sync.Mutex func spHandlerOpen(portname string, baud int, buftype string) { - spHandlerLock.Lock() - defer spHandlerLock.Unlock() + spHandlerOpenLock.Lock() + defer spHandlerOpenLock.Unlock() log.Print("Inside spHandler") From fb2220fcf9b391e3e63944c5310a032697f58599 Mon Sep 17 00:00:00 2001 From: Cristian Maglie Date: Thu, 24 Apr 2025 15:24:35 +0200 Subject: [PATCH 4/4] Error out if baudrate and buftype of already opened port do not match --- serialport.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/serialport.go b/serialport.go index 8176cde5..b3418fe5 100755 --- a/serialport.go +++ b/serialport.go @@ -302,7 +302,8 @@ func spHandlerOpen(portname string, baud int, buftype string) { sp, err := serial.Open(portname, mode) log.Print("Just tried to open port") if err != nil { - if existingPort, ok := sh.FindPortByName(portname); ok { + existingPort, ok := sh.FindPortByName(portname) + if ok && existingPort.portConf.Baud == baud && existingPort.BufferType == buftype { log.Print("Port already opened") h.broadcastSys <- []byte("{\"Cmd\":\"Open\",\"Desc\":\"Port already opened.\",\"Port\":\"" + existingPort.portConf.Name + "\",\"Baud\":" + strconv.Itoa(existingPort.portConf.Baud) + ",\"BufferType\":\"" + existingPort.BufferType + "\"}") } else {