diff --git a/ascii_over_tcp_client.go b/ascii_over_tcp_client.go index c5e1785..e0722be 100644 --- a/ascii_over_tcp_client.go +++ b/ascii_over_tcp_client.go @@ -11,16 +11,16 @@ import ( // ASCIIOverTCPClientHandler implements Packager and Transporter interface. type ASCIIOverTCPClientHandler struct { asciiPackager - asciiTCPTransporter + *asciiTCPTransporter } // NewASCIIOverTCPClientHandler allocates and initializes a ASCIIOverTCPClientHandler. func NewASCIIOverTCPClientHandler(address string) *ASCIIOverTCPClientHandler { - handler := &ASCIIOverTCPClientHandler{} - handler.Address = address - handler.Timeout = tcpTimeout - handler.IdleTimeout = tcpIdleTimeout - return handler + return &ASCIIOverTCPClientHandler{ + asciiTCPTransporter: &asciiTCPTransporter{ + defaultTCPTransporter(address), + }, + } } // ASCIIOverTCPClient creates ASCII over TCP client with default handler and given connect string. @@ -29,6 +29,13 @@ func ASCIIOverTCPClient(address string) Client { return NewClient(handler) } +// Clone creates a new client handler with the same underlying shared transport. +func (mb *ASCIIOverTCPClientHandler) Clone() *ASCIIOverTCPClientHandler { + return &ASCIIOverTCPClientHandler{ + asciiTCPTransporter: mb.asciiTCPTransporter, + } +} + // asciiTCPTransporter implements Transporter interface. type asciiTCPTransporter struct { tcpTransporter diff --git a/asciiclient.go b/asciiclient.go index d860fb3..0f3d7a0 100644 --- a/asciiclient.go +++ b/asciiclient.go @@ -25,16 +25,16 @@ var asciiStart = []string{":", ">"} // ASCIIClientHandler implements Packager and Transporter interface. type ASCIIClientHandler struct { asciiPackager - asciiSerialTransporter + *asciiSerialTransporter } // NewASCIIClientHandler allocates and initializes a ASCIIClientHandler. func NewASCIIClientHandler(address string) *ASCIIClientHandler { - handler := &ASCIIClientHandler{} - handler.Address = address - handler.Timeout = serialTimeout - handler.IdleTimeout = serialIdleTimeout - return handler + return &ASCIIClientHandler{ + asciiSerialTransporter: &asciiSerialTransporter{ + serialPort: defaultSerialPort(address), + }, + } } // ASCIIClient creates ASCII client with default handler and given connect string. @@ -43,6 +43,13 @@ func ASCIIClient(address string) Client { return NewClient(handler) } +// Clone creates a new client handler with the same underlying shared transport. +func (mb *ASCIIClientHandler) Clone() *ASCIIClientHandler { + return &ASCIIClientHandler{ + asciiSerialTransporter: mb.asciiSerialTransporter, + } +} + // asciiPackager implements Packager interface. type asciiPackager struct { SlaveID byte diff --git a/rtu_over_tcp_client.go b/rtu_over_tcp_client.go index 3b802ba..2c6c6bc 100644 --- a/rtu_over_tcp_client.go +++ b/rtu_over_tcp_client.go @@ -12,16 +12,16 @@ import ( // RTUOverTCPClientHandler implements Packager and Transporter interface. type RTUOverTCPClientHandler struct { rtuPackager - rtuTCPTransporter + *rtuTCPTransporter } // NewRTUOverTCPClientHandler allocates and initializes a RTUOverTCPClientHandler. func NewRTUOverTCPClientHandler(address string) *RTUOverTCPClientHandler { - handler := &RTUOverTCPClientHandler{} - handler.Address = address - handler.Timeout = tcpTimeout - handler.IdleTimeout = tcpIdleTimeout - return handler + return &RTUOverTCPClientHandler{ + rtuTCPTransporter: &rtuTCPTransporter{ + defaultTCPTransporter(address), + }, + } } // RTUOverTCPClient creates RTU over TCP client with default handler and given connect string. @@ -30,6 +30,13 @@ func RTUOverTCPClient(address string) Client { return NewClient(handler) } +// Clone creates a new client handler with the same underlying shared transport. +func (mb *RTUOverTCPClientHandler) Clone() *RTUOverTCPClientHandler { + return &RTUOverTCPClientHandler{ + rtuTCPTransporter: mb.rtuTCPTransporter, + } +} + // rtuTCPTransporter implements Transporter interface. type rtuTCPTransporter struct { tcpTransporter diff --git a/rtuclient.go b/rtuclient.go index 29b493f..4997955 100644 --- a/rtuclient.go +++ b/rtuclient.go @@ -46,16 +46,16 @@ const ( // RTUClientHandler implements Packager and Transporter interface. type RTUClientHandler struct { rtuPackager - rtuSerialTransporter + *rtuSerialTransporter } // NewRTUClientHandler allocates and initializes a RTUClientHandler. func NewRTUClientHandler(address string) *RTUClientHandler { - handler := &RTUClientHandler{} - handler.Address = address - handler.Timeout = serialTimeout - handler.IdleTimeout = serialIdleTimeout - return handler + return &RTUClientHandler{ + rtuSerialTransporter: &rtuSerialTransporter{ + serialPort: defaultSerialPort(address), + }, + } } // RTUClient creates RTU client with default handler and given connect string. @@ -64,6 +64,13 @@ func RTUClient(address string) Client { return NewClient(handler) } +// Clone creates a new client handler with the same underlying shared transport. +func (mb *RTUClientHandler) Clone() *RTUClientHandler { + return &RTUClientHandler{ + rtuSerialTransporter: mb.rtuSerialTransporter, + } +} + // rtuPackager implements Packager interface. type rtuPackager struct { SlaveID byte diff --git a/serial.go b/serial.go index 3f3e53b..d386f19 100644 --- a/serial.go +++ b/serial.go @@ -34,6 +34,17 @@ type serialPort struct { closeTimer *time.Timer } +// defaultSerialPort creates a serialPort with default configuration. +func defaultSerialPort(address string) serialPort { + return serialPort{ + Config: serial.Config{ + Address: address, + Timeout: serialTimeout, + }, + IdleTimeout: serialIdleTimeout, + } +} + func (mb *serialPort) Connect() (err error) { mb.mu.Lock() defer mb.mu.Unlock() diff --git a/tcpclient.go b/tcpclient.go index 6cc1878..9cae682 100644 --- a/tcpclient.go +++ b/tcpclient.go @@ -36,16 +36,16 @@ func (length ErrTCPHeaderLength) Error() string { // TCPClientHandler implements Packager and Transporter interface. type TCPClientHandler struct { tcpPackager - tcpTransporter + *tcpTransporter } // NewTCPClientHandler allocates a new TCPClientHandler. func NewTCPClientHandler(address string) *TCPClientHandler { - h := &TCPClientHandler{} - h.Address = address - h.Timeout = tcpTimeout - h.IdleTimeout = tcpIdleTimeout - return h + transport := defaultTCPTransporter(address) + return &TCPClientHandler{ + tcpPackager: tcpPackager{transactionID: &transport.transactionID}, + tcpTransporter: &transport, + } } // TCPClient creates TCP client with default handler and given connect string. @@ -54,10 +54,18 @@ func TCPClient(address string) Client { return NewClient(handler) } +// Clone creates a new client handler with the same underlying shared transport. +func (mb *TCPClientHandler) Clone() *TCPClientHandler { + return &TCPClientHandler{ + tcpPackager: tcpPackager{transactionID: &mb.tcpTransporter.transactionID}, + tcpTransporter: mb.tcpTransporter, + } +} + // tcpPackager implements Packager interface. type tcpPackager struct { // For synchronization between messages of server & client - transactionID uint32 + transactionID *uint32 // Broadcast address is 0 SlaveID byte } @@ -79,7 +87,7 @@ func (mb *tcpPackager) Encode(pdu *ProtocolDataUnit) (adu []byte, err error) { adu = make([]byte, tcpHeaderSize+1+len(pdu.Data)) // Transaction identifier - transactionID := atomic.AddUint32(&mb.transactionID, 1) + transactionID := atomic.AddUint32(mb.transactionID, 1) binary.BigEndian.PutUint16(adu, uint16(transactionID)) // Protocol identifier binary.BigEndian.PutUint16(adu[2:], tcpProtocolIdentifier) @@ -146,6 +154,18 @@ type tcpTransporter struct { lastAttemptedTransactionID uint16 lastSuccessfulTransactionID uint16 + + // For synchronization between messages of server & client + transactionID uint32 +} + +// defaultTCPTransporter creates a new tcpTransporter with default values. +func defaultTCPTransporter(address string) tcpTransporter { + return tcpTransporter{ + Address: address, + Timeout: tcpTimeout, + IdleTimeout: tcpIdleTimeout, + } } // helper value to signify what to do in Send diff --git a/tcpclient_prop_test.go b/tcpclient_prop_test.go index 530dd88..ff35efb 100644 --- a/tcpclient_prop_test.go +++ b/tcpclient_prop_test.go @@ -9,8 +9,9 @@ import ( func TestTCPEncodeDecode(t *testing.T) { rapid.Check(t, func(t *rapid.T) { + transactionID := rapid.Uint32().Draw(t, "transactionID") packager := &tcpPackager{ - transactionID: rapid.Uint32().Draw(t, "transactionID"), + transactionID: &transactionID, SlaveID: rapid.Byte().Draw(t, "SlaveID"), } diff --git a/tcpclient_test.go b/tcpclient_test.go index 4abb6f4..8a9c899 100644 --- a/tcpclient_test.go +++ b/tcpclient_test.go @@ -13,7 +13,8 @@ import ( ) func TestTCPEncoding(t *testing.T) { - packager := tcpPackager{} + var transactionID uint32 = 1 + packager := tcpPackager{transactionID: &transactionID} pdu := ProtocolDataUnit{} pdu.FunctionCode = 3 pdu.Data = []byte{0, 4, 0, 3} @@ -30,8 +31,8 @@ func TestTCPEncoding(t *testing.T) { } func TestTCPDecoding(t *testing.T) { - packager := tcpPackager{} - packager.transactionID = 1 + var transactionID uint32 = 1 + packager := tcpPackager{transactionID: &transactionID} packager.SlaveID = 17 adu := []byte{0, 1, 0, 0, 0, 6, 17, 3, 0, 120, 0, 3}