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