1 ///
2 module modbus.exception;
3 
4 import std..string : format;
5 import std.datetime : Duration;
6 
7 import modbus.types;
8 
9 public import serialport.exception;
10 
11 ///
12 class ModbusException : Exception
13 { private this() @safe pure nothrow @nogc { super(""); } }
14 
15 ///
16 class CloseTcpConnection : ModbusException
17 { private this() @safe pure nothrow @nogc { super(); } }
18 
19 ///
20 class ModbusIOException : ModbusException
21 {
22     ///
23     ulong dev;
24     ///
25     ubyte fnc;
26 
27     private this() @safe pure nothrow @nogc { super(); }
28 }
29 
30 ///
31 class ModbusDevException : ModbusIOException
32 {
33     ///
34     private ubyte[260] writeBuffer;
35     ///
36     private size_t writeLength;
37     ///
38     private ubyte[260] readBuffer;
39     ///
40     private size_t readLength;
41 
42     private this() @safe pure nothrow @nogc { super(); }
43 
44     @property
45     {
46         void writed(const(void)[] b)
47         {
48             auto ln = b.length;
49             writeBuffer[0..ln] = cast(ubyte[])(b[0..ln]);
50             writeLength = ln;
51         }
52 
53         const(void)[] writed() const
54         { return writeBuffer[0..writeLength]; }
55 
56         void readed(const(void)[] b)
57         {
58             auto ln = b.length;
59             readBuffer[0..ln] = cast(ubyte[])(b[0..ln]);
60             readLength = ln;
61         }
62 
63         const(void)[] readed() const
64         { return readBuffer[0..readLength]; }
65     }
66 }
67 
68 ///
69 class CheckFailException : ModbusDevException
70 { private this() @safe pure nothrow @nogc { super(); } }
71 
72 ///
73 class FunctionErrorException : ModbusDevException
74 {
75     ///
76     FunctionErrorCode code;
77 
78     private this() @safe pure nothrow @nogc { super(); }
79 }
80 
81 /// use this exception for throwing errors in modbus slave
82 class SlaveFuncProcessException : ModbusIOException
83 {
84     ///
85     FunctionErrorCode code;
86 
87     private this() @safe pure nothrow @nogc { super(); }
88 }
89 
90 ///
91 class ReadDataLengthException : ModbusDevException
92 {
93     ///
94     size_t expected, responseLength;
95 
96     private this() @safe pure nothrow @nogc { super(); }
97 }
98 
99 private E setFields(E: ModbusException)(E e, string msg, string file,
100                                             size_t line)
101 {
102     e.msg = msg;
103     e.file = file;
104     e.line = line;
105     return e;
106 }
107 
108 private string extraFields(E, string[] fields)()
109 {
110     static if (fields.length == 0) return "";
111     else
112     {
113         string ret;
114 
115         static foreach (field; fields)
116         {{
117             mixin(`alias ft = typeof(E.init.%s);`.format(field));
118             ret ~= `%s %s, `.format(ft.stringof, field);
119         }}
120 
121         return ret;
122     }
123 }
124 
125 unittest
126 {
127     static assert(extraFields!(ModbusIOException, ["dev", "fnc"]) ==
128                     "ulong dev, ubyte fnc, ");
129 }
130 
131 private string extraFieldsSet(string name, string[] fields)
132 {
133     if (fields.length == 0) return "";
134 
135     string ret;
136 
137     foreach (field; fields)
138         ret ~= "%1$s.%2$s = %2$s;\n".format(name, field);
139 
140     return ret;
141 }
142 
143 import std.format;
144 
145 enum preallocated;
146 
147 private mixin template throwExcMix(E, string[] fields=[])
148     if (is(E: ModbusException))
149 {
150     enum name = E.stringof;
151     mixin(`
152     @preallocated
153     private %1$s %2$s%1$s;
154     void throw%1$s(%3$s string msg="", string file=__FILE__,
155                         size_t line=__LINE__) @nogc
156     {
157         auto e = %2$s%1$s.setFields(msg, file, line);
158         %4$s
159         throw e;
160     }
161     `.format(name, "prealloc", extraFields!(E,fields),
162                 extraFieldsSet("e", fields))
163     );
164 }
165 
166 mixin throwExcMix!ModbusException;
167 mixin throwExcMix!CloseTcpConnection;
168 mixin throwExcMix!(ModbusIOException, ["dev", "fnc"]);
169 mixin throwExcMix!(ModbusDevException, ["dev", "fnc"]);
170 mixin throwExcMix!(CheckFailException, ["dev", "fnc"]);
171 mixin throwExcMix!(FunctionErrorException, ["dev", "fnc", "code"]);
172 mixin throwExcMix!(SlaveFuncProcessException, ["dev", "fnc", "code"]);
173 mixin throwExcMix!(ReadDataLengthException, ["dev", "fnc", "expected",
174                                                 "responseLength"]);
175 
176 static this()
177 {
178     import std.traits : getSymbolsByUDA;
179     static foreach (sym; getSymbolsByUDA!(mixin(__MODULE__), preallocated))
180         sym = new typeof(sym);
181 }