1 ///
2 module modbus.exception;
3 
4 import std.string : format;
5 import std.datetime : Duration;
6 
7 enum MINIMUM_MODBUS_MSG_LENGTH = 5;
8 
9 ///
10 class ModbusException : Exception
11 {
12     ///
13     this(string msg, string file=__FILE__, size_t line=__LINE__)
14         @nogc @safe pure nothrow
15     { super(msg, file, line); }
16 }
17 
18 ///
19 class ModbusIOException : ModbusException
20 {
21     ///
22     ulong dev;
23     ///
24     ubyte fnc;
25 
26     this(string msg, ulong dev, ubyte fnc,
27             string file=__FILE__, size_t line=__LINE__)
28         @nogc @safe pure nothrow
29     {
30         super(msg, file, line);
31         this.dev = dev;
32         this.fnc = fnc;
33     }
34 }
35 
36 ///
37 class ModbusTimeoutException : ModbusIOException
38 {
39     ///
40     Duration dur;
41     ///
42     this(string msg, ulong dev, ubyte fnc, Duration dur,
43             string file=__FILE__, size_t line=__LINE__)
44         @nogc @safe pure nothrow
45     {
46         super(msg, dev, fnc, file, line);
47         this.dur = dur;
48     }
49 }
50 
51 ///
52 class ModbusDevException : ModbusIOException
53 {
54     ///
55     private ubyte[256] writeBuffer;
56     ///
57     private size_t writeLength;
58     ///
59     private ubyte[256] readBuffer;
60     ///
61     private size_t readLength;
62 
63     ///
64     this(ulong dev, ubyte fnc, string msg,
65          string file=__FILE__, size_t line=__LINE__)
66     { super(msg, dev, fnc, file, line); }
67 
68     @property
69     {
70         void writed(const(void)[] b)
71         {
72             auto ln = b.length;
73             writeBuffer[0..ln] = cast(ubyte[])(b[0..ln]);
74             writeLength = ln;
75         }
76 
77         const(void)[] writed() const
78         { return writeBuffer[0..writeLength]; }
79 
80         void readed(const(void)[] b)
81         {
82             auto ln = b.length;
83             readBuffer[0..ln] = cast(ubyte[])(b[0..ln]);
84             readLength = ln;
85         }
86 
87         const(void)[] readed() const
88         { return readBuffer[0..readLength]; }
89     }
90 }
91 
92 ///
93 class CheckFailException : ModbusDevException
94 {
95     ///
96     this(ulong dev, ubyte fnc,
97          string file=__FILE__, size_t line=__LINE__)
98     {
99         super(dev, fnc, format("dev %d fnc %d(0x%x) recive msg CRC check fails",
100                     dev, fnc, fnc), file, line);
101     }
102 }
103 
104 ///
105 class FunctionErrorException : ModbusDevException
106 {
107     ///
108     ubyte res;
109     ///
110     FunctionErrorCode code;
111 
112     ///
113     this(ulong dev, ubyte fnc, ubyte res, ubyte code,
114          string file=__FILE__, size_t line=__LINE__)
115     {
116         this.res = res;
117         this.code = cast(FunctionErrorCode)code;
118 
119         super(dev, fnc, format("dev %d fnc %d(0x%x) recive fnc %d(0x%x) with "~
120                                 "exception code %s (%d)", dev, fnc, fnc, res, res,
121                                 cast(FunctionErrorCode)code, code), file, line);
122     }
123 }
124 
125 ///
126 class ReadDataLengthException : ModbusDevException
127 {
128     size_t expected, responseLength;
129     ///
130     this(ulong dev, ubyte fnc, size_t exp, size_t res,
131          string file=__FILE__, size_t line=__LINE__)
132     {
133         expected = exp;
134         responseLength = res;
135         super(dev, fnc, format("dev %d fnc to %d(0x%x) recieves wrong"~
136                     " count of bytes (%d != expected %d or more what %d)",
137                     dev, fnc, fnc, res, exp, MINIMUM_MODBUS_MSG_LENGTH), file, line);
138     }
139 }
140 
141 ///
142 enum FunctionErrorCode : ubyte
143 {
144     ILLEGAL_FUNCTION     = 1, /// 1
145     ILLEGAL_DATA_ADDRESS = 2, /// 2
146     ILLEGAL_DATA_VALUE   = 3, /// 3
147     SLAVE_DEVICE_FAILURE = 4, /// 4
148     ACKNOWLEDGE          = 5, /// 5
149     SLAVE_DEVICE_BUSY    = 6, /// 6
150     MEMORY_PARITY_ERROR  = 8, /// 8
151     GATEWAY_PATH_UNAVAILABLE = 0xA, /// 0xA
152     GATEWAY_TARGET_DEVICE_FAILED_TO_RESPOND = 0xB, /// 0xB
153 }
154 
155 private version (modbus_use_prealloc_exceptions)
156 {
157     __gshared
158     {
159         auto preallocModbusException = new ModbusException("many args");
160         auto preallocModbusTimeoutException = new ModbusTimeoutException("many args", Duration.init);
161         auto preallocCheckFailException = new CheckFailException(0, 0);
162         auto preallocReadDataLengthException = new ReadDataLengthException(0,0,0,0);
163         auto preallocFunctionErrorException = new FunctionErrorException(0,0,0,0);
164     }
165 }
166 
167 /// Returns: preallocated exception with new values of fields
168 ModbusException modbusException()(string msg, string file=__FILE__, size_t line=__LINE__)
169 {
170     version (modbus_use_prealloc_exceptions)
171     {
172         preallocModbusException.msg = msg;
173         preallocModbusException.file = file;
174         preallocModbusException.line = line;
175         return preallocModbusException;
176     }
177     else return new ModbusException(msg, file, line);
178 }
179 
180 /// Returns: preallocated exception with new values of fields
181 ModbusTimeoutException modbusTimeoutException()(string msg, ulong dev, ubyte fnc, Duration dur, string file=__FILE__, size_t line=__LINE__)
182 {
183     version (modbus_use_prealloc_exceptions)
184     {
185         preallocModbusTimeoutException.msg = msg;
186         preallocModbusTimeoutException.dev = dev;
187         preallocModbusTimeoutException.fnc = fnc;
188         preallocModbusTimeoutException.dur = dur;
189         preallocModbusTimeoutException.file = file;
190         preallocModbusTimeoutException.line = line;
191         return preallocModbusTimeoutException;
192     }
193     else return new ModbusTimeoutException(msg, dev, fnc, dur, file, line);
194 }
195 
196 /// Returns: preallocated exception with new values of fields
197 CheckFailException checkFailException()(ulong dev, ubyte fnc,
198                                     string file=__FILE__, size_t line=__LINE__)
199 {
200     version (modbus_use_prealloc_exceptions)
201     {
202         preallocCheckFailException.msg = "check CRC fails";
203         preallocCheckFailException.dev = dev;
204         preallocCheckFailException.fnc = fnc;
205         preallocCheckFailException.file = file;
206         preallocCheckFailException.line = line;
207         return preallocCheckFailException;
208     }
209     else return new CheckFailException(dev, fnc, file, line);
210 }
211 
212 /// Returns: preallocated exception with new values of fields
213 FunctionErrorException functionErrorException()(ulong dev, ubyte fnc, ubyte res, ubyte code,
214                                                 string file=__FILE__, size_t line=__LINE__)
215 {
216     version (modbus_use_prealloc_exceptions)
217     {
218         preallocFunctionErrorException.msg = "error while read function response";
219         preallocFunctionErrorException.dev = dev;
220         preallocFunctionErrorException.fnc = fnc;
221         preallocFunctionErrorException.res = res;
222         preallocFunctionErrorException.code = cast(FunctionErrorCode)code;
223         preallocFunctionErrorException.file = file;
224         preallocFunctionErrorException.line = line;
225         return preallocFunctionErrorException;
226     }
227     else return new FunctionErrorException(dev, fnc, res, code, file, line);
228 }
229 
230 /// Returns: preallocated exception with new values of fields
231 ReadDataLengthException readDataLengthException()(ulong dev, ubyte fnc, size_t exp, size_t res,
232                                                 string file=__FILE__, size_t line=__LINE__)
233 {
234     version (modbus_use_prealloc_exceptions)
235     {
236         preallocReadDataLengthException.msg = "error while read function response: wrong length";
237         preallocReadDataLengthException.dev = dev;
238         preallocReadDataLengthException.fnc = fnc;
239         preallocReadDataLengthException.expected = exp;
240         preallocReadDataLengthException.responseLength = res;
241         preallocReadDataLengthException.file = file;
242         preallocReadDataLengthException.line = line;
243         return preallocReadDataLengthException;
244     }
245     else return new ReadDataLengthException(dev, fnc, exp, res, file, line);
246 }