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