-
Notifications
You must be signed in to change notification settings - Fork 2
/
Copy pathTCPPackager.go
112 lines (93 loc) · 2.5 KB
/
TCPPackager.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
package modbus
import (
"encoding/binary"
"errors"
"log"
"net"
"time"
)
// TCPPackager implements the Packager interface for Modbus TCP.
type TCPPackager struct {
packagerSettings
net.Conn
transactionID uint16
timeout time.Duration
}
// NewTCPPackager returns a new, ready to use TCPPackager with the given
// ConnectionSettings.
func NewTCPPackager(c ConnectionSettings) (*TCPPackager, error) {
addr, err := net.ResolveTCPAddr("tcp", c.Host)
// attempt to connect to the slave device (server)
conn, err := net.DialTCP("tcp", nil, addr)
if err != nil {
return nil, err
}
conn.SetKeepAlive(true)
return &TCPPackager{
Conn: conn,
timeout: c.Timeout,
packagerSettings: packagerSettings{
Debug: c.Debug,
},
}, nil
}
func (pkgr *TCPPackager) generateADU(q Query) ([]byte, error) {
data, err := q.data()
if err != nil {
return nil, err
}
packetLen := len(data) + 8
packet := make([]byte, packetLen)
packet[0] = byte(pkgr.transactionID >> 8) // Transaction ID (High Byte)
packet[1] = byte(pkgr.transactionID & 0xff) // (Low Byte)
packet[2] = 0x00 // Protocol ID (2 bytes) -- always 00
packet[3] = 0x00
packet[4] = byte((len(data) + 2) >> 8) // Length of remaining packet (High Byte)
packet[5] = byte((len(data) + 2) & 0xff) // (Low Byte)
packet[6] = q.SlaveID
packet[7] = byte(q.FunctionCode)
copy(packet[8:], data)
return packet, nil
}
// Send sends the Query and returns the result or and error code.
func (pkgr *TCPPackager) Send(q Query) ([]byte, error) {
adu, err := pkgr.generateADU(q)
if err != nil {
return nil, err
}
defer func() { pkgr.transactionID++ }()
if pkgr.Debug {
log.Printf("Tx: %x\n", adu)
}
pkgr.SetDeadline(time.Now().Add(pkgr.timeout))
_, err = pkgr.Write(adu)
if err != nil {
return nil, err
}
pkgr.SetDeadline(time.Now().Add(pkgr.timeout))
response := make([]byte, MaxTCPSize)
n, err := pkgr.Read(response)
if err != nil {
return nil, err
}
if pkgr.Debug {
log.Printf("Rx Full: %x\n", response)
}
// Check for matching transactionID
if binary.BigEndian.Uint16(response[0:2]) != pkgr.transactionID {
return nil, errors.New("Mismatched transactionID")
}
response = response[6:n]
if pkgr.Debug {
log.Printf("Rx: %x\n", response)
}
// Check the validity of the response
if valid, err := q.isValidResponse(response); !valid {
return nil, err
}
if isReadFunction(q.FunctionCode) {
return response[3:], nil
}
// return only the number of bytes read
return response[2:], nil
}