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 }