-
Notifications
You must be signed in to change notification settings - Fork 28
/
rtu_over_tcp_client.go
98 lines (86 loc) · 2.54 KB
/
rtu_over_tcp_client.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
// Copyright 2018 xft. All rights reserved.
// This software may be modified and distributed under the terms
// of the BSD license. See the LICENSE file for details.
package modbus
import (
"io"
"time"
)
// RTUOverTCPClientHandler implements Packager and Transporter interface.
type RTUOverTCPClientHandler struct {
rtuPackager
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
}
// RTUOverTCPClient creates RTU over TCP client with default handler and given connect string.
func RTUOverTCPClient(address string) Client {
handler := NewRTUOverTCPClientHandler(address)
return NewClient(handler)
}
// rtuTCPTransporter implements Transporter interface.
type rtuTCPTransporter struct {
tcpTransporter
}
// Send sends data to server and ensures adequate response for request type
func (mb *rtuTCPTransporter) Send(aduRequest []byte) (aduResponse []byte, err error) {
mb.mu.Lock()
defer mb.mu.Unlock()
// Establish a new connection if not connected
if err = mb.connect(); err != nil {
return
}
// Set timer to close when idle
mb.lastActivity = time.Now()
mb.startCloseTimer()
// Set write and read timeout
if mb.Timeout > 0 {
if err = mb.conn.SetDeadline(mb.lastActivity.Add(mb.Timeout)); err != nil {
return
}
}
// Send the request
mb.logf("modbus: send % x\n", aduRequest)
if _, err = mb.conn.Write(aduRequest); err != nil {
return
}
function := aduRequest[1]
functionFail := aduRequest[1] & 0x80
bytesToRead := calculateResponseLength(aduRequest)
var n, n1 int
var data [rtuMaxSize]byte
// We first read the minimum length and then read either the full package
// or the error package, depending on the error status (byte 2 of the response)
n, err = io.ReadAtLeast(mb.conn, data[:], rtuMinSize)
if err != nil {
return
}
// if the function is correct
if data[1] == function {
// we read the rest of the bytes
if n < bytesToRead {
if bytesToRead > rtuMinSize && bytesToRead <= rtuMaxSize {
n1, err = io.ReadFull(mb.conn, data[n:bytesToRead])
n += n1
}
}
} else if data[1] == functionFail {
// for error we need to read 5 bytes
if n < rtuExceptionSize {
n1, err = io.ReadFull(mb.conn, data[n:rtuExceptionSize])
}
n += n1
}
if err != nil {
return
}
aduResponse = data[:n]
mb.logf("modbus: recv % x\n", aduResponse)
return
}