Skip to content
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
19 changes: 13 additions & 6 deletions ascii_over_tcp_client.go
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand All @@ -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,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since tcpTransporter doesnt only contain native values, do we need to add a Clone() method for that too?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure I understand the question (native values?). The purpose of this PR is to share the transporter. The private asciiTCPTransporter etc transporter fields have been converter to pointers to allow this. I didn't see a case for cloning a transporter.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@frzifus does that answer the question? Or is there anything specific to the tcpTransporter that I'm missing?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In other words: the tcpTransporter is a pointer now that gets copied.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

TCP is actually a special case that required adjustments to the packager too, see evcc-io#6

}
}

// asciiTCPTransporter implements Transporter interface.
type asciiTCPTransporter struct {
tcpTransporter
Expand Down
19 changes: 13 additions & 6 deletions asciiclient.go
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand All @@ -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
Expand Down
19 changes: 13 additions & 6 deletions rtu_over_tcp_client.go
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand All @@ -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
Expand Down
19 changes: 13 additions & 6 deletions rtuclient.go
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand All @@ -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
Expand Down
11 changes: 11 additions & 0 deletions serial.go
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand Down
36 changes: 28 additions & 8 deletions tcpclient.go
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand All @@ -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
}
Expand All @@ -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)
Expand Down Expand Up @@ -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
Expand Down
3 changes: 2 additions & 1 deletion tcpclient_prop_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"),
}

Expand Down
7 changes: 4 additions & 3 deletions tcpclient_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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}
Expand All @@ -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}

Expand Down