1 module modbus.backend.rtu; 2 3 import std.experimental.logger; 4 import std.exception : enforce; 5 import std.bitmanip; 6 7 import modbus.exception; 8 import modbus.iface; 9 import modbus.protocol; 10 11 version (unittest) static this() { sharedLog = new NullLogger; } 12 13 class RTU : Modbus.Backend 14 { 15 SerialPortIface com; 16 ubyte[256] buffer; 17 size_t idx; 18 19 enum minimumMsgLength = 5; 20 21 public: 22 23 this(SerialPortIface spi) 24 { 25 this.com = enforce(spi, "serial port is null"); 26 } 27 28 override 29 { 30 void start(ubyte dev, ubyte func) 31 { 32 this.write(dev); 33 this.write(func); 34 } 35 36 void append(byte v) { this.write(v); } 37 void append(ubyte v) { this.write(v); } 38 void append(short v) { this.write(v); } 39 void append(ushort v) { this.write(v); } 40 void append(int v) { this.write(v); } 41 void append(uint v) { this.write(v); }; 42 void append(long v) { this.write(v); }; 43 void append(ulong v) { this.write(v); }; 44 void append(float v) { this.write(v); }; 45 void append(double v) { this.write(v); }; 46 47 void append(const(void)[] v) 48 { 49 auto inc = v.length; 50 // CRC 51 enforce(inc + idx + 2 < buffer.length, "many args"); 52 buffer[idx..idx+inc] = cast(ubyte[])v; 53 idx += inc; 54 }; 55 56 bool messageComplite() const @property { return idx == 0; } 57 const(void)[] tempBuffer() const @property { return buffer[0..idx]; } 58 59 void send() 60 { 61 scope (exit) idx = 0; 62 append(cast(const(void)[])(crc16(buffer[0..idx])[])); 63 com.write(buffer[0..idx]); 64 .trace("write bytes: ", buffer[0..idx]); 65 } 66 67 Response read(size_t expectedBytes) 68 { 69 // dev fnc CRC 70 expectedBytes += 1 + 1 + 2; 71 72 Response res; 73 auto tmp = com.read(buffer[]); 74 75 .trace(" read bytes: ", cast(ubyte[])tmp); 76 77 enforce(tmp.length >= minimumMsgLength, 78 new ReadDataLengthException(tmp.length < 1 ? 0 : (cast(ubyte[])tmp)[0], 79 tmp.length < 2 ? 0 : (cast(ubyte[])tmp)[1], 80 expectedBytes, tmp.length)); 81 82 if (tmp.length > expectedBytes) 83 { 84 .warningf("receive more bytes what expected (%d): %(0x%02x %)", 85 expectedBytes, cast(ubyte[])tmp[expectedBytes..$]); 86 87 tmp = tmp[0..expectedBytes]; 88 } 89 90 res.dev = (cast(ubyte[])tmp)[0]; 91 res.fnc = (cast(ubyte[])tmp)[1]; 92 93 enforce(checkCRC(tmp), new CheckCRCException(res.dev, res.fnc)); 94 95 res.data = tmp[2..$-2]; 96 97 return res; 98 } 99 } 100 101 protected: 102 103 void write(T)(T v) 104 { 105 scope (failure) idx = 0; 106 // CRC 107 enforce(T.sizeof + idx + 2 < buffer.length, "many args"); 108 buffer[].write(v, &idx); 109 } 110 } 111 112 unittest 113 { 114 import std.algorithm; 115 116 void[] buf; 117 118 auto rtu = new RTU(new class SerialPortIface 119 { override: 120 void write(const(void)[] t) { buf = t.dup; } 121 void[] read(void[] buffer) 122 { 123 assert(buffer.length <= buf.length); 124 buffer[0..buf.length] = buf[]; 125 return buffer[0..buf.length]; 126 } 127 }); 128 129 enum C1 = ushort(10100); 130 enum C2 = ushort(12345); 131 rtu.start(1, 6); 132 rtu.append(C1); 133 rtu.append(C2); 134 assert(!rtu.messageComplite); 135 assert(rtu.tempBuffer.length == 2 + 2 + 2); 136 rtu.send(); 137 assert(rtu.messageComplite); 138 assert(rtu.tempBuffer.length == 0); 139 assert(equal(cast(ubyte[])buf[0..$-2], 140 cast(ubyte[])[1, 6] ~ nativeToBigEndian(C1) ~ nativeToBigEndian(C2))); 141 142 auto crc = cast(ubyte[])crc16(buf[0..$-2]); 143 assert(crc[0] == (cast(ubyte[])buf)[$-2]); 144 assert(crc[1] == (cast(ubyte[])buf)[$-1]); 145 } 146 147 bool checkCRC(const(void)[] data) 148 { 149 auto msg = cast(const(ubyte[]))data; 150 auto a = msg[$-2..$]; 151 auto b = crc16(msg[0..$-2]); 152 return a[0] == b[0] && a[1] == b[1]; 153 } 154 155 unittest 156 { 157 immutable ubyte[] data = [0x02, 0x03, 0x00, 0x00, 0x00, 0x05, 0x85, 0xFA]; 158 assert(checkCRC(data)); 159 } 160 161 ubyte[2] crc16(const(void)[] data) pure nothrow @trusted 162 { 163 164 ubyte hi = 0xFF; 165 ubyte lo = 0xFF; 166 167 size_t idx; 168 169 foreach (val; cast(ubyte[])data) 170 { 171 idx = lo ^ val; 172 lo = hi ^ tblHi[idx]; 173 hi = tblLo[idx]; 174 } 175 176 return [lo, hi]; 177 } 178 179 unittest 180 { 181 immutable ubyte[] data = [0x02, 0x03, 0x00, 0x00, 0x00, 0x05]; 182 assert(crc16(data)[1] == 0xFA); 183 assert(crc16(data)[0] == 0x85); 184 } 185 186 private: 187 enum ubyte[] tblHi = [ 188 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 189 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 190 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 191 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 192 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 193 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 194 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 195 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 196 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 197 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 198 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 199 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 200 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 201 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 202 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 203 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 204 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 205 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 206 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 207 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 208 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 209 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 210 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 211 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 212 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 213 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40]; 214 215 enum ubyte[] tblLo = [ 216 0x00, 0xC0, 0xC1, 0x01, 0xC3, 0x03, 0x02, 0xC2, 0xC6, 0x06, 217 0x07, 0xC7, 0x05, 0xC5, 0xC4, 0x04, 0xCC, 0x0C, 0x0D, 0xCD, 218 0x0F, 0xCF, 0xCE, 0x0E, 0x0A, 0xCA, 0xCB, 0x0B, 0xC9, 0x09, 219 0x08, 0xC8, 0xD8, 0x18, 0x19, 0xD9, 0x1B, 0xDB, 0xDA, 0x1A, 220 0x1E, 0xDE, 0xDF, 0x1F, 0xDD, 0x1D, 0x1C, 0xDC, 0x14, 0xD4, 221 0xD5, 0x15, 0xD7, 0x17, 0x16, 0xD6, 0xD2, 0x12, 0x13, 0xD3, 222 0x11, 0xD1, 0xD0, 0x10, 0xF0, 0x30, 0x31, 0xF1, 0x33, 0xF3, 223 0xF2, 0x32, 0x36, 0xF6, 0xF7, 0x37, 0xF5, 0x35, 0x34, 0xF4, 224 0x3C, 0xFC, 0xFD, 0x3D, 0xFF, 0x3F, 0x3E, 0xFE, 0xFA, 0x3A, 225 0x3B, 0xFB, 0x39, 0xF9, 0xF8, 0x38, 0x28, 0xE8, 0xE9, 0x29, 226 0xEB, 0x2B, 0x2A, 0xEA, 0xEE, 0x2E, 0x2F, 0xEF, 0x2D, 0xED, 227 0xEC, 0x2C, 0xE4, 0x24, 0x25, 0xE5, 0x27, 0xE7, 0xE6, 0x26, 228 0x22, 0xE2, 0xE3, 0x23, 0xE1, 0x21, 0x20, 0xE0, 0xA0, 0x60, 229 0x61, 0xA1, 0x63, 0xA3, 0xA2, 0x62, 0x66, 0xA6, 0xA7, 0x67, 230 0xA5, 0x65, 0x64, 0xA4, 0x6C, 0xAC, 0xAD, 0x6D, 0xAF, 0x6F, 231 0x6E, 0xAE, 0xAA, 0x6A, 0x6B, 0xAB, 0x69, 0xA9, 0xA8, 0x68, 232 0x78, 0xB8, 0xB9, 0x79, 0xBB, 0x7B, 0x7A, 0xBA, 0xBE, 0x7E, 233 0x7F, 0xBF, 0x7D, 0xBD, 0xBC, 0x7C, 0xB4, 0x74, 0x75, 0xB5, 234 0x77, 0xB7, 0xB6, 0x76, 0x72, 0xB2, 0xB3, 0x73, 0xB1, 0x71, 235 0x70, 0xB0, 0x50, 0x90, 0x91, 0x51, 0x93, 0x53, 0x52, 0x92, 236 0x96, 0x56, 0x57, 0x97, 0x55, 0x95, 0x94, 0x54, 0x9C, 0x5C, 237 0x5D, 0x9D, 0x5F, 0x9F, 0x9E, 0x5E, 0x5A, 0x9A, 0x9B, 0x5B, 238 0x99, 0x59, 0x58, 0x98, 0x88, 0x48, 0x49, 0x89, 0x4B, 0x8B, 239 0x8A, 0x4A, 0x4E, 0x8E, 0x8F, 0x4F, 0x8D, 0x4D, 0x4C, 0x8C, 240 0x44, 0x84, 0x85, 0x45, 0x87, 0x47, 0x46, 0x86, 0x82, 0x42, 241 0x43, 0x83, 0x41, 0x81, 0x80, 0x40];