From 33defbef62c5fc1d5f8389f9194cf463a7e10926 Mon Sep 17 00:00:00 2001 From: hebl <2012hchw@gmail.com> Date: Thu, 21 Mar 2024 17:44:36 +0800 Subject: [PATCH 1/2] feat: add listenerName in frame for mult listeners --- .gitignore | 2 ++ .travis.yml | 1 + frame.go | 1 + framertu.go | 23 +++++++++++++++-------- framertu_test.go | 18 +++++++++++++++--- frametcp.go | 9 ++++++++- go.mod | 8 ++++++++ server.go | 9 ++++++--- servertu.go | 17 +++++++++++------ servetcp.go | 20 ++++++++++++++------ 10 files changed, 81 insertions(+), 27 deletions(-) create mode 100644 go.mod diff --git a/.gitignore b/.gitignore index 049fbf0..3ef8d5e 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,4 @@ ttyFOO ttyBAR +go.sum +coverage.txt diff --git a/.travis.yml b/.travis.yml index 2d8b33c..2185c2e 100644 --- a/.travis.yml +++ b/.travis.yml @@ -5,6 +5,7 @@ sudo: required go: - 1.7.x - 1.8.x + - 1.20.x before_install: - sudo apt-get install -y socat diff --git a/frame.go b/frame.go index a41fc6e..41e93eb 100644 --- a/frame.go +++ b/frame.go @@ -10,6 +10,7 @@ type Framer interface { GetFunction() uint8 SetException(exception *Exception) SetData(data []byte) + GetListenerName() string } // GetException retunrns the Modbus exception or Success (indicating not exception). diff --git a/framertu.go b/framertu.go index 75b0e11..75cf6de 100644 --- a/framertu.go +++ b/framertu.go @@ -7,14 +7,15 @@ import ( // RTUFrame is the Modbus TCP frame. type RTUFrame struct { - Address uint8 - Function uint8 - Data []byte - CRC uint16 + listenerName string + Address uint8 + Function uint8 + Data []byte + CRC uint16 } // NewRTUFrame converts a packet to a Modbus TCP frame. -func NewRTUFrame(packet []byte) (*RTUFrame, error) { +func NewRTUFrame(listenerName string, packet []byte) (*RTUFrame, error) { // Check the that the packet length. if len(packet) < 5 { return nil, fmt.Errorf("RTU Frame error: packet less than 5 bytes: %v", packet) @@ -29,14 +30,20 @@ func NewRTUFrame(packet []byte) (*RTUFrame, error) { } frame := &RTUFrame{ - Address: uint8(packet[0]), - Function: uint8(packet[1]), - Data: packet[2 : pLen-2], + listenerName: listenerName, + Address: uint8(packet[0]), + Function: uint8(packet[1]), + Data: packet[2 : pLen-2], } return frame, nil } +// GetListenerName returns the listener name. +func (frame *RTUFrame) GetListenerName() string { + return frame.listenerName +} + // Copy the RTUFrame. func (frame *RTUFrame) Copy() Framer { copy := *frame diff --git a/framertu_test.go b/framertu_test.go index 7333efe..79cf271 100644 --- a/framertu_test.go +++ b/framertu_test.go @@ -3,7 +3,7 @@ package mbserver import "testing" func TestNewRTUFrame(t *testing.T) { - frame, err := NewRTUFrame([]byte{0x01, 0x04, 0x02, 0xFF, 0xFF, 0xB8, 0x80}) + frame, err := NewRTUFrame("test", []byte{0x01, 0x04, 0x02, 0xFF, 0xFF, 0xB8, 0x80}) if !isEqual(nil, err) { t.Fatalf("expected %v, got %v", nil, err) } @@ -22,7 +22,7 @@ func TestNewRTUFrame(t *testing.T) { } func TestNewRTUFrameShortPacket(t *testing.T) { - _, err := NewRTUFrame([]byte{0x01, 0x04, 0xFF, 0xFF}) + _, err := NewRTUFrame("test", []byte{0x01, 0x04, 0xFF, 0xFF}) if err == nil { t.Fatalf("expected error not nil, got %v", err) } @@ -30,7 +30,7 @@ func TestNewRTUFrameShortPacket(t *testing.T) { func TestNewRTUFrameBadCRC(t *testing.T) { // Bad CRC: 0x81 (should be 0x80) - _, err := NewRTUFrame([]byte{0x01, 0x04, 0x02, 0xFF, 0xFF, 0xB8, 0x81}) + _, err := NewRTUFrame("test", []byte{0x01, 0x04, 0x02, 0xFF, 0xFF, 0xB8, 0x81}) if err == nil { t.Fatalf("expected error not nil, got %v", err) } @@ -49,3 +49,15 @@ func TestRTUFrameBytes(t *testing.T) { t.Errorf("expected %v, got %v", expect, got) } } + +func TestFrameRTUGetListenerName(t *testing.T) { + frame := &RTUFrame{ + listenerName: "test", + } + + got := frame.GetListenerName() + expect := "test" + if !isEqual(expect, got) { + t.Errorf("expected %v, got %v", expect, got) + } +} diff --git a/frametcp.go b/frametcp.go index 7c89d4b..f91f91d 100644 --- a/frametcp.go +++ b/frametcp.go @@ -7,6 +7,7 @@ import ( // TCPFrame is the Modbus TCP frame. type TCPFrame struct { + listenerName string TransactionIdentifier uint16 ProtocolIdentifier uint16 Length uint16 @@ -16,13 +17,14 @@ type TCPFrame struct { } // NewTCPFrame converts a packet to a Modbus TCP frame. -func NewTCPFrame(packet []byte) (*TCPFrame, error) { +func NewTCPFrame(listenerName string, packet []byte) (*TCPFrame, error) { // Check if the packet is too short. if len(packet) < 9 { return nil, fmt.Errorf("TCP Frame error: packet less than 9 bytes") } frame := &TCPFrame{ + listenerName: listenerName, TransactionIdentifier: binary.BigEndian.Uint16(packet[0:2]), ProtocolIdentifier: binary.BigEndian.Uint16(packet[2:4]), Length: binary.BigEndian.Uint16(packet[4:6]), @@ -39,6 +41,11 @@ func NewTCPFrame(packet []byte) (*TCPFrame, error) { return frame, nil } +// GetListenerName returns the listener name. +func (frame *TCPFrame) GetListenerName() string { + return frame.listenerName +} + // Copy the TCPFrame. func (frame *TCPFrame) Copy() Framer { copy := *frame diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..c2a1dc8 --- /dev/null +++ b/go.mod @@ -0,0 +1,8 @@ +module github.com/tbrandon/mbserver + +go 1.20 + +require ( + github.com/goburrow/modbus v0.1.0 + github.com/goburrow/serial v0.1.0 +) diff --git a/server.go b/server.go index 38fe520..5a0461b 100644 --- a/server.go +++ b/server.go @@ -13,8 +13,8 @@ import ( type Server struct { // Debug enables more verbose messaging. Debug bool - listeners []net.Listener - ports []serial.Port + listeners map[string]net.Listener + ports map[string]serial.Port portsWG sync.WaitGroup portsCloseChan chan struct{} requestChan chan *Request @@ -33,7 +33,10 @@ type Request struct { // NewServer creates a new Modbus server (slave). func NewServer() *Server { - s := &Server{} + s := &Server{ + listeners: make(map[string]net.Listener), + ports: make(map[string]serial.Port), + } // Allocate Modbus memory maps. s.DiscreteInputs = make([]byte, 65536) diff --git a/servertu.go b/servertu.go index 80e050d..043e041 100644 --- a/servertu.go +++ b/servertu.go @@ -14,19 +14,24 @@ func (s *Server) ListenRTU(serialConfig *serial.Config) (err error) { if err != nil { log.Fatalf("failed to open %s: %v\n", serialConfig.Address, err) } - s.ports = append(s.ports, port) + s.ports[serialConfig.Address] = port s.portsWG.Add(1) go func() { defer s.portsWG.Done() - s.acceptSerialRequests(port) + s.acceptSerialRequests(serialConfig.Address) }() return err } -func (s *Server) acceptSerialRequests(port serial.Port) { - SkipFrameError: +func (s *Server) acceptSerialRequests(serialAddr string) { + port, ok := s.ports[serialAddr] + if !ok { + log.Printf("serial port not found: %s\n", serialAddr) + return + } +SkipFrameError: for { select { case <-s.portsCloseChan: @@ -49,10 +54,10 @@ func (s *Server) acceptSerialRequests(port serial.Port) { // Set the length of the packet to the number of read bytes. packet := buffer[:bytesRead] - frame, err := NewRTUFrame(packet) + frame, err := NewRTUFrame(serialAddr, packet) if err != nil { log.Printf("bad serial frame error %v\n", err) - //The next line prevents RTU server from exiting when it receives a bad frame. Simply discard the erroneous + //The next line prevents RTU server from exiting when it receives a bad frame. Simply discard the erroneous //frame and wait for next frame by jumping back to the beginning of the 'for' loop. log.Printf("Keep the RTU server running!!\n") continue SkipFrameError diff --git a/servetcp.go b/servetcp.go index ac2bf6b..e946f06 100644 --- a/servetcp.go +++ b/servetcp.go @@ -2,13 +2,20 @@ package mbserver import ( "crypto/tls" + "fmt" "io" "log" "net" "strings" ) -func (s *Server) accept(listen net.Listener) error { +func (s *Server) accept(addressPort string) error { + listen, ok := s.listeners[addressPort] + if !ok { + err := fmt.Errorf("listener not found: %s", addressPort) + log.Printf("%v\n", err) + return err + } for { conn, err := listen.Accept() if err != nil { @@ -34,7 +41,7 @@ func (s *Server) accept(listen net.Listener) error { // Set the length of the packet to the number of read bytes. packet = packet[:bytesRead] - frame, err := NewTCPFrame(packet) + frame, err := NewTCPFrame(addressPort, packet) if err != nil { log.Printf("bad packet error %v\n", err) return @@ -55,8 +62,9 @@ func (s *Server) ListenTCP(addressPort string) (err error) { log.Printf("Failed to Listen: %v\n", err) return err } - s.listeners = append(s.listeners, listen) - go s.accept(listen) + + s.listeners[addressPort] = listen + go s.accept(addressPort) return err } @@ -67,7 +75,7 @@ func (s *Server) ListenTLS(addressPort string, config *tls.Config) (err error) { log.Printf("Failed to Listen on TLS: %v\n", err) return err } - s.listeners = append(s.listeners, listen) - go s.accept(listen) + s.listeners[addressPort] = listen + go s.accept(addressPort) return err } From a1e9175f466f3c85406855ffef1f4680ecf63963 Mon Sep 17 00:00:00 2001 From: hebl <2012hchw@gmail.com> Date: Sat, 11 May 2024 18:48:59 +0800 Subject: [PATCH 2/2] fix: rm log fault --- servertu.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/servertu.go b/servertu.go index 043e041..84d40b8 100644 --- a/servertu.go +++ b/servertu.go @@ -12,7 +12,8 @@ import ( func (s *Server) ListenRTU(serialConfig *serial.Config) (err error) { port, err := serial.Open(serialConfig) if err != nil { - log.Fatalf("failed to open %s: %v\n", serialConfig.Address, err) + log.Printf("failed to open %s: %v\n", serialConfig.Address, err) + return err } s.ports[serialConfig.Address] = port