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];