1 /**
2 *
3 * This module defines interfaces and classes that wrap nodave functios and procedures.
4 */
5 module dinodave.plc;
6 
7 import std.conv;
8 import std.datetime;
9 import std.socket;
10 import std.stdio;
11 import std..string;
12 
13 import dinodave.nodave;
14 import dinodave.helper;
15 
16 /**
17 * Representing the physical connection to a PLC
18 */
19 interface IPlc {
20    void openConnection(int slot = 2);
21    void closeConnection();
22    /**
23    * Reads a sequence of bytes from PLC memory.
24    *
25    * Params:
26    *  DB = The number of a data block
27    *  start = The address of the first byte in the block.
28    *  length = The number of bytes to read.
29    *
30    *
31    * Returns: The function returns 0 on success.
32    * Nonzero return codes may be passed to daveStrerror() to get a textual explanation of what happened.
33    * Generally, positive error codes represent errors reported by the PLC,
34    * while negative ones represent errors detected by LIBNODAVE, e.g. no response from the PLC.
35    */
36    void readBytes(in int DB, in int start, in int length);
37    ///
38    byte getS8();
39    ///
40    ubyte getU8();
41    ///
42    short getS16();
43    ///
44    ushort getU16();
45    ///
46    int getS32();
47    ///
48    uint getU32();
49    ///
50    float getFloat();
51 
52    ///
53    byte getS8At(int position);
54    ///
55    ubyte getU8At(int position);
56    ///
57    short getS16At(int position);
58    ///
59    ushort getU16At(int position);
60    ///
61    int getS32At(int position);
62    ///
63    uint getU32At(int position);
64    ///
65    float getFloatAt(int position);
66 
67    ///
68    void setBit(in int DB, in int byteAddress, in int bitAddress);
69    ///
70    void clearBit(in int DB, in int byteAddress, in int bitAddress);
71 
72    ///
73    void writeBytes(int DB, int start, int length, ubyte[] buffer);
74    ///
75    int readPLCTime();
76 }
77 
78 class IsoTcp: IPlc {
79    private immutable(string) ip;
80    private immutable(int) port;
81    this(in string ipAddress, in int port = 102) {
82       assert(ipAddress.length > 0);
83       ip = ipAddress;
84 
85       assert(port > 0);
86       this.port = port;
87    }
88 
89    private daveConnection* dc;
90    private _daveOSserialType fds;
91    void openConnection(int slot = 2) {
92       try {
93          auto sock = new TcpSocket(new InternetAddress(ip, to!(ushort)(port)));
94 
95          fds.rfd = sock.handle;
96          fds.wfd = fds.rfd;
97 
98          if (fds.rfd > 0) {
99             daveInterface* di = daveNewInterface(fds, "IF1", 0, daveProtoISOTCP, daveSpeed9k);
100             daveSetTimeout(di, 5_000_000);
101             enum int MPI = 0;
102             enum int RACK = 0;
103             dc = daveNewConnection(di, MPI, RACK, slot);
104             if (daveConnectPLC(dc) != 0) {
105                throw new NodaveException("Couldn't connect to PLC with ip " ~ ip);
106             }
107          } else {
108             throw new NodaveException("Couldn't open TCP port. Please make sure a CP is connected and the IP address is ok.");
109          }
110 
111       } catch(Exception e) {
112          throw new NodaveException("NoDave generic exception:" ~ e.msg);
113       }
114    }
115 
116    void closeConnection() {
117       //todo verificare se e' aperta
118       //closeSocket(fds.rfd);
119    }
120 
121    /**
122     * Reads a sequence of bytes from PLC memory.
123     *
124     * Params:
125     *    DB = The number of a data block
126     *    start = The address of the first byte in the block.
127     *    length = The number of bytes to read.
128     *
129     * Returns: The function returns 0 on success.
130     * Nonzero return codes may be passed to `strerror()` to get a textual explanation of what happened.
131     * Generally, positive error codes represent errors reported by the PLC,
132     * while negative ones represent errors detected by LIBNODAVE, e.g. no response from the PLC.
133     */
134    void readBytes(in int DB, in int start, in int length) {
135       const(int) err = daveReadBytes(dc, daveDB, DB, start, length, null);
136       if (err != 0) {
137          throw new NodaveException(err);
138       }
139    }
140 
141    byte getS8() { return to!byte(daveGetS8(dc)); }
142    ubyte getU8() { return to!ubyte(daveGetU8(dc)); }
143    short getS16() { return to!short(daveGetS16(dc)); }
144    ushort getU16() { return to!ushort(daveGetU16(dc)); }
145    int getS32() { return daveGetS32(dc); }
146    uint getU32() { return daveGetU32(dc); }
147 
148    float getFloat() { return daveGetFloat(dc); }
149 
150    byte getS8At(int position) { return to!byte(daveGetS8At(dc, position)); }
151    ubyte getU8At(int position) { return to!ubyte(daveGetU8At(dc, position)); }
152    short getS16At(int position) { return to!short(daveGetS16At(dc, position)); }
153    ushort getU16At(int position) { return to!ushort(daveGetU16At(dc, position)); }
154    int getS32At(int position) { return daveGetS32At(dc, position); }
155    uint getU32At(int position) { return to!uint(daveGetU32At(dc, position)); }
156    float getFloatAt(int position) { return daveGetFloatAt(dc, position); }
157 
158    void setBit(in int DB, in int byteAddress, in int bitAddress) {
159       int res = daveSetBit(dc, daveDB, DB, byteAddress, bitAddress);
160       if (res != 0) {
161          throw new NodaveException(res);
162       }
163    }
164 
165    void clearBit(in int DB, in int byteAddress, in int bitAddress) {
166       int res = daveClrBit(dc, daveDB, DB, byteAddress, bitAddress);
167       if (res != 0) {
168          throw new NodaveException(res);
169       }
170    }
171 
172    /**
173     * Write a sequence of bytes from a buffer to PLC memory.
174     * Params:
175     * DB = The number of a data block
176     * start = The address of the first byte in the block.
177     * length = The number of bytes to write.
178     * buffer = Buffer to write.
179     */
180    void writeBytes(int DB, int start, int length, ubyte[] buffer) {
181       int res = daveWriteBytes(dc, daveDB, DB, start, length, buffer.ptr);
182       if (res != 0) {
183          throw new NodaveException(res);
184       }
185    }
186 
187    /**
188     * Read PLC data time.
189     */
190    int readPLCTime() {
191       return daveReadPLCTime(dc);
192    }
193 
194    DateTime getPLCTime() {
195       int res = readPLCTime();
196       if (res != 0) {
197          throw new NodaveException(res);
198       }
199 
200       getU8(); //???
201       int year = getU8().fromBCD() * 100 + getU8().fromBCD();
202       int month = fromBCD(getU8());
203       int day = getU8().fromBCD;
204       int hour = getU8().fromBCD;
205       int minute = getU8().fromBCD;
206       int second = getU8().fromBCD;
207       return DateTime(year, month, day, hour, minute, second);
208    }
209 
210    int setPLCTimeToSystime() {
211       return daveSetPLCTimeToSystime(dc);
212    }
213 }
214 
215 /**
216 * NoDave exception
217 */
218 class NodaveException: Exception {
219    this(int errNo) {
220       string message = strerror(errNo);
221       this(message);
222    }
223 
224    this(string message, string file = __FILE__, size_t line = __LINE__, Throwable next = null) {
225       super(message, file, line, next);
226    }
227 }
228 
229 daveConnection* createConnection(in string ip, in int port = 102) {
230    daveConnection* dc;
231    try {
232       auto sock = new TcpSocket(new InternetAddress(ip, to!(ushort)(port)));
233       _daveOSserialType fds;
234       fds.wfd = fds.rfd = sock.handle;
235 
236       if (fds.rfd > 0) {
237          daveInterface* di = daveNewInterface(fds, "IF1", 0, daveProtoISOTCP, daveSpeed9k);
238          daveSetTimeout(di, 5_000_000);
239          enum int MPI = 0;
240          enum int RACK = 0;
241          enum int SLOT = 2;
242          dc = daveNewConnection(di, MPI, RACK, SLOT);
243          if (daveConnectPLC(dc) != 0) {
244             throw new NodaveException("Couldn't connect to PLC with ip " ~ ip);
245          }
246          return dc;
247       } else {
248          throw new NodaveException("Couldn't open TCP port. Please make sure a CP is connected and the IP address is ok.");
249       }
250 
251    } catch(Exception e) {
252       throw new NodaveException("Generic exception");
253    }
254 }
255