/
modbus-rtu.go
153 lines (137 loc) · 2.94 KB
/
modbus-rtu.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
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
package internal
import (
"errors"
"fmt"
"github.com/iot-master-contrib/modbus/define"
"github.com/zgwit/iot-master/v3/pkg/bin"
"time"
)
// RTU Modbus-RTU协议
type RTU struct {
messenger Messenger
buf []byte
}
func NewRTU(tunnel define.Conn, opts string) *RTU {
//TODO parse opts(yaml)
rtu := &RTU{
messenger: Messenger{Timeout: 5 * time.Second, tunnel: tunnel},
//slave: opts["slave"].(uint8),
buf: make([]byte, 256),
}
return rtu
}
func (m *RTU) execute(cmd []byte) ([]byte, error) {
l, err := m.messenger.AskAtLeast(cmd, m.buf, 5)
if err != nil {
return nil, err
}
//crc := bin.ParseUint16LittleEndian(m.buf[l-2:])
//if crc != CRC16(m.buf[:l-2]) {
// //检验错误
// return nil, errors.New("校验错误")
//}
//slave := buf[0]
fc := m.buf[1]
//解析错误码
if fc&0x80 > 0 {
return nil, fmt.Errorf("错误码:%d", m.buf[2])
}
//解析数据
length := 4
count := int(m.buf[2])
switch fc {
case 1, 2:
length += 1 + count/8
if count%8 != 0 {
length++
}
if l < length {
//长度不够,继续读
_, err = m.messenger.ReadAtLeast(m.buf[l:], length-l)
if err != nil {
return nil, err
}
l = len(m.buf)
}
b := m.buf[3 : l-2]
//数组解压
//b = bin.ExpandBool(b, count)
return bin.Dup(b), nil
case 3, 4, 23:
length += 1 + count
if l < length {
//长度不够,继续读
_, err = m.messenger.ReadAtLeast(m.buf[l:], length-l)
if err != nil {
return nil, err
}
l = len(m.buf)
//if n+l < length {
// return nil, errors.New("长度不足")
//}
}
b := m.buf[3 : l-2]
return bin.Dup(b), nil
case 5, 15, 6, 16:
//写指令不处理
return nil, nil
default:
return nil, errors.New("不支持的指令")
}
}
func (m *RTU) Read(slave uint8, code uint8, addr uint16, size uint16) ([]byte, error) {
b := make([]byte, 8)
b[0] = slave
b[1] = code
bin.WriteUint16(b[2:], addr)
bin.WriteUint16(b[4:], size)
bin.WriteUint16LittleEndian(b[6:], CRC16(b[:6]))
return m.execute(b)
}
func (m *RTU) Write(slave uint8, code uint8, addr uint16, buf []byte) error {
length := len(buf)
//如果是线圈,需要Shrink
switch code {
case 1:
if length == 1 {
code = 5
//数据 转成 0x0000 0xFF00
if buf[0] > 0 {
buf = []byte{0xFF, 0}
} else {
buf = []byte{0, 0}
}
} else {
code = 15 //0x0F
//数组压缩
b := bin.ShrinkBool(buf)
count := len(b)
buf = make([]byte, 3+count)
bin.WriteUint16(buf, uint16(length))
buf[2] = uint8(count)
copy(buf[3:], b)
}
case 3:
if length == 2 {
code = 6
} else {
code = 16 //0x10
b := make([]byte, 3+length)
bin.WriteUint16(b, uint16(length/2))
b[2] = uint8(length)
copy(b[3:], buf)
buf = b
}
default:
return errors.New("功能码不支持")
}
l := 6 + len(buf)
b := make([]byte, l)
b[0] = slave
b[1] = code
bin.WriteUint16(b[2:], addr)
copy(b[4:], buf)
bin.WriteUint16LittleEndian(b[l-2:], CRC16(b[:l-2]))
_, err := m.execute(b)
return err
}