1 ///
2 module modbus;
3 
4 public:
5 import modbus.exception;
6 import modbus.protocol;
7 import modbus.facade;
8 import modbus.backend.connection;
9 import modbus.backend.specrules;
10 
11 unittest
12 {
13     static import std.bitmanip;
14     alias bwrite = std.bitmanip.write;
15     alias bread = std.bitmanip.read;
16     import modbus.backend;
17 
18     static class ModbusEmulator
19     {
20         align(1)
21         static struct DeviceData
22         {
23             align(1):
24             ushort[4] simpleRegister; // 0..3
25             int intValue; // 4
26             float floatValue; // 6
27         }
28 
29         SpecRules sr;
30         DeviceData[size_t] regs;
31         ubyte[256] res;
32         size_t idx;
33 
34         this(SpecRules sr)
35         {
36             regs[70000] = DeviceData([1234, 10405, 12, 42], 3^^12, 3.14);
37             regs[1] = DeviceData([2345, 50080, 34, 42], 7^^9, 2.71);
38             this.sr = sr;
39         }
40 
41         void write(const(void)[] msg)
42         {
43             idx = 0;
44             auto ubmsg = cast(const(ubyte)[])msg;
45             ulong dev;
46             ubyte fnc;
47             sr.peekDF(ubmsg, dev, fnc);
48             ubmsg = ubmsg[sr.deviceTypeSize+1..$];
49 
50             import std.stdio;
51 
52             if (dev !in regs) return;
53 
54             res[idx..idx+sr.deviceTypeSize] = cast(ubyte[])sr.packDF(dev, fnc)[0..sr.deviceTypeSize];
55             idx += sr.deviceTypeSize;
56 
57             if (!checkCRC(msg))
58                 storeFail(fnc, FunctionErrorCode.ILLEGAL_DATA_VALUE);
59             else
60             {
61                 bwrite(res[], fnc, &idx);
62                 
63                 switch (fnc)
64                 {
65                     case 4:
66                         auto d = (cast(ushort*)(dev in regs))[0..DeviceData.sizeof/2];
67                         auto st = bread!ushort(ubmsg);
68                         auto cnt = cast(ubyte)bread!ushort(ubmsg);
69                         bwrite(res[], cnt, &idx);
70                         foreach (i; 0 .. cnt)
71                             bwrite(res[], d[st+i], &idx);
72                         break;
73                     default:
74                         storeFail(fnc, FunctionErrorCode.ILLEGAL_DATA_VALUE);
75                         break;
76                 }
77             }
78 
79             storeCRC();
80         }
81 
82         void[] read(void[] buffer)
83         {
84             buffer[0..idx] = res[0..idx];
85             return buffer[0..idx];
86         }
87 
88         void storeFail(ubyte fnc, FunctionErrorCode c)
89         {
90             bwrite(res[], cast(ubyte)(fnc|0xF0), &idx);
91             bwrite(res[], cast(ubyte)c, &idx);
92         }
93 
94         void storeCRC()
95         {
96             auto crc = crc16(res[0..idx]);
97             bwrite(res[], crc[0], &idx);
98             bwrite(res[], crc[1], &idx);
99         }
100     }
101 
102     BasicSpecRules sr = new PilotBMSSpecRules;
103 
104     auto com = new ModbusEmulator(sr);
105 
106     auto mbus = new Modbus(new RTU(new class Connection{
107         override:
108             void write(const(void)[] msg) { com.write(msg); }
109             void[] read(void[] buffer) { return com.read(buffer); }
110         }, sr));
111 
112     assert(mbus.readInputRegisters(70000, 0, 1)[0] == 1234);
113     assert(equal(mbus.readInputRegisters(1, 0, 4), [2345, 50080, 34, 42]));
114 }