1 /// 2 module modbus.backend.specrules; 3 4 version (modbus_verbose) 5 import std.experimental.logger; 6 7 /// 8 interface SpecRules 9 { 10 pure @nogc: 11 /// 12 @property size_t deviceTypeSize(); 13 14 /// 15 const(void)[] packDF(ulong dev, ubyte fnc); 16 /// 17 int unpackDF(const(void)[] buf, ref ulong dev, ref ubyte fnc); 18 19 /// 20 const(void)[] pack(const(void)[]); 21 /// 22 final const(void)[] packT(T)(T value) 23 { return pack((cast(void*)&value)[0..T.sizeof]); } 24 25 /// 26 const(void)[] unpack(const(void)[] data); 27 /// 28 final T unpackT(T)(const(void)[] data) 29 { return (cast(T[])unpack(data))[0]; } 30 } 31 32 /// 33 class BasicSpecRules : SpecRules 34 { 35 import std.bitmanip : write; 36 import std.bitmanip : read; 37 38 @nogc: 39 40 protected ubyte[16] buffer; 41 42 private const(void)[] typedPack(T)(T val) 43 { 44 static if (T.sizeof <= ushort.sizeof) 45 buffer[].write(val, 0); 46 else 47 { 48 size_t i; 49 auto data = cast(ushort[])((cast(void*)&val)[0..T.sizeof]); 50 foreach (part; data) buffer[].write(part, &i); 51 } 52 53 return buffer[0..T.sizeof]; 54 } 55 56 private const(void)[] typedUnpack(T)(const(void)[] data) 57 { 58 import std.range : chunks, enumerate; 59 60 static if (T.sizeof == ubyte.sizeof) return data; 61 else 62 { 63 enum us = ushort.sizeof; 64 foreach (i, s; (cast(const(ubyte)[])data).chunks(us).enumerate) 65 { 66 auto tmp = s.read!ushort; 67 buffer[i*us..(i+1)*us] = (cast(ubyte*)&tmp)[0..us]; 68 } 69 return buffer[0..T.sizeof]; 70 } 71 } 72 73 import std.meta : AliasSeq; 74 alias Types = AliasSeq!(byte,short,int,long); 75 76 public pure override: 77 @property size_t deviceTypeSize() { return 1; } 78 79 const(void)[] packDF(ulong dev, ubyte fnc) 80 { 81 assert(dev <= 255, "device number can't be more 255"); 82 buffer[].write(cast(ubyte)dev, 0); 83 buffer[].write(fnc, 1); 84 return buffer[0..deviceTypeSize+1]; 85 } 86 87 int unpackDF(const(void)[] vbuf, ref ulong dev, ref ubyte fnc) 88 { 89 import std.bitmanip : peek; 90 auto buf = cast(const(ubyte)[])vbuf; 91 if (buf.length >= 1) dev = buf.peek!ubyte(0); 92 else return 2; 93 if (buf.length >= 2) fnc = buf.peek!ubyte(1); 94 else return 1; 95 return 0; 96 } 97 98 const(void)[] pack(const(void)[] data) 99 { 100 final switch (data.length) foreach (T; Types) 101 case T.sizeof: return typedPack((cast(T[])data)[0]); 102 } 103 104 const(void)[] unpack(const(void)[] data) 105 { 106 final switch (data.length) foreach (T; Types) 107 case T.sizeof: return typedUnpack!T(data); 108 } 109 } 110 111 version(unittest) 112 { 113 void[] tb(T)(T value) { return cast(void[])[value]; } 114 T bt(T)(const(void)[] data) { return (cast(T[])data)[0]; } 115 } 116 117 unittest 118 { 119 auto bsp = new BasicSpecRules; 120 assert(cast(ubyte[])bsp.packT(cast(ubyte)(0xAB)) == [0xAB]); 121 assert(cast(ubyte[])bsp.packT(cast(ushort)(0xA1B2)) == [0xA1, 0xB2]); 122 assert(cast(ubyte[])bsp.packT(cast(int)(0xA1B2C3D4)) == 123 [0xC3, 0xD4, 0xA1, 0xB2]); 124 assert(cast(ubyte[])bsp.pack(tb(0xA1B2C3D4E5F6A7B8)) == 125 [0xA7, 0xB8, 0xE5, 0xF6, 0xC3, 0xD4, 0xA1, 0xB2]); 126 127 assert(bt!ulong(bsp.unpack(cast(ubyte[])[0xA7,0xB8,0xE5,0xF6,0xC3,0xD4,0xA1,0xB2])) == 128 0xA1B2C3D4E5F6A7B8); 129 130 import std.random; 131 void test(T)() 132 { 133 auto val = cast(T)uniform(0, T.max); 134 assert(bsp.unpackT!T(bsp.packT(val)) == val); 135 } 136 foreach (ubyte i; 0 .. 256) 137 assert(bsp.unpackT!ubyte(bsp.packT(i)) == i); 138 139 foreach (i; 0 .. 10_000) test!ushort; 140 foreach (i; 0 .. 10_000) test!uint; 141 foreach (i; 0 .. 10_000) test!ulong; 142 }