From fa1cc4f787e5582219cec7cbd52a2003bc41aa40 Mon Sep 17 00:00:00 2001 From: Pascal Masschelier Date: Sat, 8 Feb 2025 15:33:08 +0100 Subject: [PATCH 1/3] fix: avoid acceptSerialRequest to exit for a timeout reading requests when port.Read received serial.ErrTimeout, acceptSerialRequest exited, without notifing caller of ListenRTU --- servertu.go | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/servertu.go b/servertu.go index 80e050d..836a188 100644 --- a/servertu.go +++ b/servertu.go @@ -1,6 +1,7 @@ package mbserver import ( + "errors" "io" "log" @@ -18,7 +19,10 @@ func (s *Server) ListenRTU(serialConfig *serial.Config) (err error) { s.portsWG.Add(1) go func() { - defer s.portsWG.Done() + defer func(){ + s.portsWG.Done() + // TODO notify caller of ListenRTU we stop accepting requests for this serialConfig, possibliy by sending sth to a channel passed to ListenRTU + } s.acceptSerialRequests(port) }() @@ -37,6 +41,11 @@ func (s *Server) acceptSerialRequests(port serial.Port) { buffer := make([]byte, 512) bytesRead, err := port.Read(buffer) + + // dont exit loop for a timeout reading request + if errors.Is(err, serial.ErrTimeout) { + continue + } if err != nil { if err != io.EOF { log.Printf("serial read error %v\n", err) From d6e5bbcc7a49b44c74bd9ede05a17cf224055cc8 Mon Sep 17 00:00:00 2001 From: Pascal Masschelier Date: Sat, 8 Feb 2025 15:37:32 +0100 Subject: [PATCH 2/3] fix: defer call in ListenRTU goroutine --- servertu.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/servertu.go b/servertu.go index 836a188..9aaee7e 100644 --- a/servertu.go +++ b/servertu.go @@ -22,7 +22,7 @@ func (s *Server) ListenRTU(serialConfig *serial.Config) (err error) { defer func(){ s.portsWG.Done() // TODO notify caller of ListenRTU we stop accepting requests for this serialConfig, possibliy by sending sth to a channel passed to ListenRTU - } + }() s.acceptSerialRequests(port) }() From 469a43f3912bec477e38c82961aacb122b18b9d1 Mon Sep 17 00:00:00 2001 From: pa-m Date: Sat, 8 Feb 2025 16:11:08 +0100 Subject: [PATCH 3/3] NotifyExit option --- servertu.go | 43 +++++++++++++++++++++++++++++++------------ 1 file changed, 31 insertions(+), 12 deletions(-) diff --git a/servertu.go b/servertu.go index 9aaee7e..457bee5 100644 --- a/servertu.go +++ b/servertu.go @@ -4,13 +4,30 @@ import ( "errors" "io" "log" + "syscall" + "time" "github.com/goburrow/serial" ) +type ListenRTUOption func(options *rtuOptions) +type rtuOptions struct { + notifyExit chan error +} + +func NotifyExit(ch chan error) ListenRTUOption { + return func(s *rtuOptions) { + s.notifyExit = ch + } +} + // ListenRTU starts the Modbus server listening to a serial device. // For example: err := s.ListenRTU(&serial.Config{Address: "/dev/ttyUSB0"}) -func (s *Server) ListenRTU(serialConfig *serial.Config) (err error) { +func (s *Server) ListenRTU(serialConfig *serial.Config, options ...ListenRTUOption) (err error) { + var opts rtuOptions + for _, option := range options { + option(&opts) + } port, err := serial.Open(serialConfig) if err != nil { log.Fatalf("failed to open %s: %v\n", serialConfig.Address, err) @@ -19,38 +36,40 @@ func (s *Server) ListenRTU(serialConfig *serial.Config) (err error) { s.portsWG.Add(1) go func() { - defer func(){ - s.portsWG.Done() - // TODO notify caller of ListenRTU we stop accepting requests for this serialConfig, possibliy by sending sth to a channel passed to ListenRTU - }() - s.acceptSerialRequests(port) + defer s.portsWG.Done() + err := s.acceptSerialRequests(port) + if opts.notifyExit != nil { + opts.notifyExit <- err + } }() return err } -func (s *Server) acceptSerialRequests(port serial.Port) { - SkipFrameError: +func (s *Server) acceptSerialRequests(port serial.Port) error { +SkipFrameError: for { select { case <-s.portsCloseChan: - return + return nil default: } buffer := make([]byte, 512) bytesRead, err := port.Read(buffer) - - // dont exit loop for a timeout reading request if errors.Is(err, serial.ErrTimeout) { continue } if err != nil { + if errors.Is(err, serial.ErrTimeout) || errors.Is(err, syscall.EWOULDBLOCK) { + time.Sleep(10 * time.Millisecond) + continue + } if err != io.EOF { log.Printf("serial read error %v\n", err) } - return + return err } if bytesRead != 0 {