Skip to content
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

feat: add listenerName in frame for mult listeners #16

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
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
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,4 @@
ttyFOO
ttyBAR
go.sum
coverage.txt
1 change: 1 addition & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ sudo: required
go:
- 1.7.x
- 1.8.x
- 1.20.x

before_install:
- sudo apt-get install -y socat
Expand Down
1 change: 1 addition & 0 deletions frame.go
Original file line number Diff line number Diff line change
Expand Up @@ -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).
Expand Down
23 changes: 15 additions & 8 deletions framertu.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -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
Expand Down
18 changes: 15 additions & 3 deletions framertu_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
Expand All @@ -22,15 +22,15 @@ 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)
}
}

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)
}
Expand All @@ -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)
}
}
9 changes: 8 additions & 1 deletion frametcp.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (

// TCPFrame is the Modbus TCP frame.
type TCPFrame struct {
listenerName string
TransactionIdentifier uint16
ProtocolIdentifier uint16
Length uint16
Expand All @@ -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]),
Expand All @@ -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
Expand Down
8 changes: 8 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
@@ -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
)
9 changes: 6 additions & 3 deletions server.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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)
Expand Down
20 changes: 13 additions & 7 deletions servertu.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,21 +12,27 @@ 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 = 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:
Expand All @@ -49,10 +55,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
Expand Down
20 changes: 14 additions & 6 deletions servetcp.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand All @@ -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
Expand All @@ -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
}

Expand All @@ -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
}