1 ///
2 module modbus.backend.base;
3 
4 version (modbus_verbose)
5     public import std.experimental.logger;
6 
7 import modbus.protocol;
8 public import modbus.exception;
9 public import modbus.backend.connection;
10 public import modbus.backend.specrules;
11 
12 /++ Basic functionality of Modbus.Backend
13 
14     Params:
15     BUFFER_SIZE = static size of message buffer
16  +/
17 class BaseBackend(size_t BUFFER_SIZE) : Modbus.Backend
18 {
19 protected:
20     enum functionTypeSize = 1;
21     Connection conn;
22     SpecRules sr;
23 
24     ubyte[BUFFER_SIZE] buffer;
25     size_t idx;
26     immutable size_t minimumMsgLength;
27     immutable size_t devOffset;
28     immutable size_t serviceData;
29 
30 public:
31 
32     /++
33         Params:
34             c = connection
35             s = rules for pack N-byte data to sending package
36             serviceData = size of CRC for RTU, protocol id for TCP etc
37             deviceOffset = offset of device number (address) in message
38      +/
39     this(Connection c, SpecRules s, size_t serviceData, size_t deviceOffset)
40     {
41         if (c is null)
42             throw modbusException("connection is null");
43         conn = c;
44         sr = s !is null ? s : new BasicSpecRules;
45         this.serviceData = serviceData;
46         devOffset = deviceOffset;
47 
48         minimumMsgLength = serviceData + sr.deviceTypeSize + functionTypeSize;
49     }
50 
51     abstract override
52     {
53         void start(ulong dev, ubyte func);
54         void send();
55         Response read(size_t expectedBytes);
56     }
57 
58     override
59     {
60         void append(byte v) { append(sr.pack(v)); }
61         void append(short v) { append(sr.pack(v)); }
62         void append(int v) { append(sr.pack(v)); }
63         void append(long v) { append(sr.pack(v)); };
64         void append(float v) { append(sr.pack(v)); };
65         void append(double v) { append(sr.pack(v)); };
66 
67         void append(const(void)[] v)
68         {
69             scope (failure) idx = 0;
70             auto inc = v.length;
71             if (idx + inc + serviceData >= buffer.length)
72                 throw modbusException("many args");
73             buffer[idx..idx+inc] = cast(ubyte[])v;
74             idx += inc;
75             version (modbus_verbose)
76                 .trace("append msg buffer data: ", buffer[0..idx]);
77         }
78 
79         bool messageComplite() const @property { return idx == 0; }
80         const(void)[] tempBuffer() const @property { return buffer[0..idx]; }
81     }
82 
83 protected:
84 
85     void appendDF(ulong dev, ubyte fnc) { append(sr.packDF(dev, fnc)); }
86 
87     Response baseRead(size_t expectedBytes, bool allocateOnlyExpected=false)
88     {
89         expectedBytes += minimumMsgLength;
90         version (modbus_verbose) .tracef("start read %d bytes", expectedBytes);
91 
92         auto buf = buffer[];
93         if (allocateOnlyExpected) buf = buf[0..expectedBytes];
94         auto tmp = cast(ubyte[])conn.read(buf);
95         version (modbus_verbose) .trace(" readed bytes: ", tmp);
96 
97         if (tmp.length < devOffset+sr.deviceTypeSize+functionTypeSize)
98             throw readDataLengthException(0, 0, expectedBytes, tmp.length);
99 
100         Response res;
101         sr.peekDF(tmp[devOffset..$], res.dev, res.fnc);
102         res.data = tmp;
103 
104         if (tmp.length < minimumMsgLength+1)
105             throw readDataLengthException(res.dev, res.fnc, expectedBytes, tmp.length);
106 
107         if (res.data.length > expectedBytes)
108         {
109             version (modbus_verbose)
110                 .warningf("receive more bytes what expected (%d): %(0x%02x %)",
111                             expectedBytes, tmp[expectedBytes..$]);
112 
113             res.data = res.data[0..expectedBytes];
114         }
115 
116         return res;
117     }
118 }