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