1 ///
2 module modbus.backend.rtu;
3 
4 import modbus.backend.base;
5 
6 ///
7 class RTU : BaseBackend!256
8 {
9 protected:
10     enum lengthOfCRC = 2;
11 
12 public:
13     ///
14     this(Connection c, SpecRules s=null) { super(c, s, lengthOfCRC, 0); }
15 
16 override:
17     ///
18     void start(ulong dev, ubyte func) { appendDF(dev, func); }
19 
20     ///
21     void send()
22     {
23         scope (exit) idx = 0;
24         append(cast(const(void)[])(crc16(buffer[0..idx])[]));
25         conn.write(buffer[0..idx]);
26         version (modbusverbose)
27             .trace("write bytes: ", buffer[0..idx]);
28     }
29 
30     ///
31     Response read(size_t expectedBytes)
32     {
33         auto res = baseRead(expectedBytes);
34         if (!checkCRC(res.data))
35             throw checkCRCException(res.dev, res.fnc);
36         res.data = res.data[devOffset+sr.deviceTypeSize+functionTypeSize..$-lengthOfCRC];
37         return res;
38     }
39 }
40 
41 unittest
42 {
43     import std.algorithm;
44     import std.bitmanip;
45 
46     void[] buf;
47 
48     auto rtu = new RTU(new class Connection
49     { override:
50         void write(const(void)[] t) { buf = t.dup; }
51         void[] read(void[] buffer)
52         {
53             assert(buffer.length <= buf.length);
54             buffer[0..buf.length] = buf[];
55             return buffer[0..buf.length];
56         }
57     });
58 
59     enum C1 = ushort(10100);
60     enum C2 = ushort(12345);
61     rtu.start(1, 6);
62     rtu.append(C1);
63     rtu.append(C2);
64     assert(!rtu.messageComplite);
65     assert(rtu.tempBuffer.length == 2 + 2 + 2);
66     rtu.send();
67     assert(rtu.messageComplite);
68     assert(rtu.tempBuffer.length == 0);
69     assert(equal(cast(ubyte[])buf[0..$-2],
70                 cast(ubyte[])[1, 6] ~ nativeToBigEndian(C1) ~ nativeToBigEndian(C2)));
71 
72     auto crc = cast(ubyte[])crc16(buf[0..$-2]);
73     assert(crc[0] == (cast(ubyte[])buf)[$-2]);
74     assert(crc[1] == (cast(ubyte[])buf)[$-1]);
75 }
76 
77 /++ Check CRC16 of data
78     Params:
79     data = last two bytes used as CRC16
80  +/
81 bool checkCRC(const(void)[] data) pure nothrow @trusted @nogc
82 {
83     auto msg = cast(const(ubyte[]))data;
84     auto a = msg[$-2..$];
85     auto b = crc16(msg[0..$-2]);
86     return a[0] == b[0] && a[1] == b[1];
87 }
88 
89 @safe unittest
90 {
91     immutable ubyte[] data = [0x02, 0x03, 0x00, 0x00, 0x00, 0x05, 0x85, 0xFA];
92     assert(checkCRC(data));
93 }
94 
95 /++ Calculate CRC16
96     Params:
97     data = input data for calculation CRC16
98  +/
99 ubyte[2] crc16(const(void)[] data) pure nothrow @trusted @nogc
100 {
101 
102     ubyte hi = 0xFF;
103     ubyte lo = 0xFF;
104 
105     size_t idx;
106 
107     foreach (val; cast(ubyte[])data)
108     {
109         idx = lo ^ val;
110         lo = hi ^ tblHi[idx];
111         hi = tblLo[idx];
112     }
113 
114     return [lo, hi];
115 }
116 
117 @safe unittest
118 {
119     immutable ubyte[] data = [0x02, 0x03, 0x00, 0x00, 0x00, 0x05];
120     assert(crc16(data)[1] == 0xFA);
121     assert(crc16(data)[0] == 0x85);
122 }
123 
124 private:
125 enum ubyte[256] tblHi = [
126     0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0,
127     0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
128     0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0,
129     0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40,
130     0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1,
131     0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41,
132     0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1,
133     0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
134     0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0,
135     0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40,
136     0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1,
137     0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40,
138     0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0,
139     0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40,
140     0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0,
141     0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40,
142     0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0,
143     0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
144     0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0,
145     0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
146     0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0,
147     0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40,
148     0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1,
149     0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
150     0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0,
151     0x80, 0x41, 0x00, 0xC1, 0x81, 0x40];
152 
153 enum ubyte[256] tblLo = [
154     0x00, 0xC0, 0xC1, 0x01, 0xC3, 0x03, 0x02, 0xC2, 0xC6, 0x06,
155     0x07, 0xC7, 0x05, 0xC5, 0xC4, 0x04, 0xCC, 0x0C, 0x0D, 0xCD,
156     0x0F, 0xCF, 0xCE, 0x0E, 0x0A, 0xCA, 0xCB, 0x0B, 0xC9, 0x09,
157     0x08, 0xC8, 0xD8, 0x18, 0x19, 0xD9, 0x1B, 0xDB, 0xDA, 0x1A,
158     0x1E, 0xDE, 0xDF, 0x1F, 0xDD, 0x1D, 0x1C, 0xDC, 0x14, 0xD4,
159     0xD5, 0x15, 0xD7, 0x17, 0x16, 0xD6, 0xD2, 0x12, 0x13, 0xD3,
160     0x11, 0xD1, 0xD0, 0x10, 0xF0, 0x30, 0x31, 0xF1, 0x33, 0xF3,
161     0xF2, 0x32, 0x36, 0xF6, 0xF7, 0x37, 0xF5, 0x35, 0x34, 0xF4,
162     0x3C, 0xFC, 0xFD, 0x3D, 0xFF, 0x3F, 0x3E, 0xFE, 0xFA, 0x3A,
163     0x3B, 0xFB, 0x39, 0xF9, 0xF8, 0x38, 0x28, 0xE8, 0xE9, 0x29,
164     0xEB, 0x2B, 0x2A, 0xEA, 0xEE, 0x2E, 0x2F, 0xEF, 0x2D, 0xED,
165     0xEC, 0x2C, 0xE4, 0x24, 0x25, 0xE5, 0x27, 0xE7, 0xE6, 0x26,
166     0x22, 0xE2, 0xE3, 0x23, 0xE1, 0x21, 0x20, 0xE0, 0xA0, 0x60,
167     0x61, 0xA1, 0x63, 0xA3, 0xA2, 0x62, 0x66, 0xA6, 0xA7, 0x67,
168     0xA5, 0x65, 0x64, 0xA4, 0x6C, 0xAC, 0xAD, 0x6D, 0xAF, 0x6F,
169     0x6E, 0xAE, 0xAA, 0x6A, 0x6B, 0xAB, 0x69, 0xA9, 0xA8, 0x68,
170     0x78, 0xB8, 0xB9, 0x79, 0xBB, 0x7B, 0x7A, 0xBA, 0xBE, 0x7E,
171     0x7F, 0xBF, 0x7D, 0xBD, 0xBC, 0x7C, 0xB4, 0x74, 0x75, 0xB5,
172     0x77, 0xB7, 0xB6, 0x76, 0x72, 0xB2, 0xB3, 0x73, 0xB1, 0x71,
173     0x70, 0xB0, 0x50, 0x90, 0x91, 0x51, 0x93, 0x53, 0x52, 0x92,
174     0x96, 0x56, 0x57, 0x97, 0x55, 0x95, 0x94, 0x54, 0x9C, 0x5C,
175     0x5D, 0x9D, 0x5F, 0x9F, 0x9E, 0x5E, 0x5A, 0x9A, 0x9B, 0x5B,
176     0x99, 0x59, 0x58, 0x98, 0x88, 0x48, 0x49, 0x89, 0x4B, 0x8B,
177     0x8A, 0x4A, 0x4E, 0x8E, 0x8F, 0x4F, 0x8D, 0x4D, 0x4C, 0x8C,
178     0x44, 0x84, 0x85, 0x45, 0x87, 0x47, 0x46, 0x86, 0x82, 0x42,
179     0x43, 0x83, 0x41, 0x81, 0x80, 0x40];