1 ///
2 module modbus.protocol.slave.slave;
3 
4 import modbus.protocol.base;
5 public import modbus.types;
6 
7 import modbus.protocol.slave.model;
8 import modbus.protocol.slave.types;
9 
10 import modbus.cbuffer;
11 
12 /++ Base class for modbus slave devices
13 
14     Iteration and message parsing process
15 
16     Define types
17  +/
18 class ModbusSlave : Modbus
19 {
20 protected:
21     size_t readed;
22 
23     void[MAX_BUFFER] responseBuffer;
24     void[MAX_BUFFER*2] findMsgBuf;
25 
26     class MBSRW : ResponseWriter
27     {
28         override @property
29         {
30             Backend backend() { return this.outer.be; }
31             protected void[] buffer() { return responseBuffer; }
32         }
33     }
34 
35     MBSRW rw;
36 
37     StopWatch dt;
38 
39     ModbusSlaveModel model;
40 
41     ///
42     alias Reaction = ModbusSlaveModel.Reaction;
43 
44     /// process message and send result if needed
45     void processMessage(ref const Message msg)
46     {
47         import std.experimental.logger : errorf;
48 
49         Response res;
50         try
51         {
52             auto pm = model.checkDeviceNumber(msg.dev);
53             if (pm == Reaction.none) return;
54             res = model.onMessage(rw, msg);
55             if (pm == Reaction.processAndAnswer)
56                 this.writeS(msg.dev, msg.fnc | (res.error ? 0x80 : 0), msg.stamp, res.data);
57         }
58         catch (SlaveFuncProcessException e)
59         {
60             errorf("%s", e);
61             this.writeS(msg.dev, msg.fnc | 0x80, msg.stamp, e.code);
62         }
63         catch (Throwable e)
64         {
65             errorf("%s", e);
66             this.writeS(msg.dev, msg.fnc | 0x80, msg.stamp,
67                     FunctionErrorCode.slaveDeviceFailure);
68         }
69     }
70 
71     CBuffer cbuffer;
72 
73     enum MIN_MSG = 4;
74 
75     MessageFinder messageFinder;
76 
77 public:
78 
79     ///
80     static class MessageFinder
81     {
82         Backend backend;
83 
84         final bool testMessage(const(void)[] data, ref Message msg)
85         { return backend.parseMessage(data, msg) == backend.ParseResult.success; }
86 
87         ///
88         abstract ptrdiff_t[2] findMessage(const(void)[] data, ref Message msg);
89     }
90 
91     ///
92     static class SlaveMessageFinder : MessageFinder
93     {
94         override ptrdiff_t[2] findMessage(const(void)[] data, ref Message msg)
95         {
96             assert(backend !is null);
97             if (data.length >= MIN_MSG)
98                 foreach (s; 0 .. data.length - MIN_MSG + 1)
99                     if (testMessage(data[s..$], msg))
100                         return [s, data.length];
101             return [-1, -1];
102         }
103     }
104 
105     ///
106     static class SnifferMessageFinder : MessageFinder
107     {
108         override ptrdiff_t[2] findMessage(const(void)[] data, ref Message msg)
109         {
110             if (data.length >= MIN_MSG)
111                 foreach (s; 0 .. data.length - MIN_MSG + 1)
112                     foreach_reverse (n; s + MIN_MSG .. data.length + 1)
113                         if (testMessage(data[s..n], msg))
114                             return [s, n+1];
115             return [-1, -1];
116         }
117     }
118 
119     ///
120     this(ModbusSlaveModel mdl, Backend be, Connection con, MessageFinder mf=null)
121     {
122         super(be, con);
123         this.model = mdl;
124         con.readTimeout = 10.msecs;
125 
126         messageFinder = mf is null ? new SlaveMessageFinder : mf;
127         messageFinder.backend = be;
128 
129         rw = new MBSRW;
130         cbuffer = CBuffer(MAX_BUFFER*2);
131     }
132 
133     ///
134     void iterate()
135     {
136         auto rdd = con.read(buffer[], con.CanRead.zero);
137         auto df = cbuffer.capacity - cbuffer.length - rdd.length;
138         if (df < 0) cbuffer.popFrontN(-df);
139         cbuffer.put(rdd);
140         auto data = cbuffer.fill(findMsgBuf);
141 
142         Message msg;
143         auto se = messageFinder.findMessage(data, msg);
144         if (se[0] == -1) return;
145         processMessage(msg);
146         cbuffer.popFrontN(se[1]);
147     }
148 }