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