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