├── README.md ├── setup.py └── libnodave └── __init__.py /README.md: -------------------------------------------------------------------------------- 1 | # Python interface to the libnodave C lib 2 | 3 | ## How to install 4 | 5 | Compile and install the libnodave c libs 6 | 7 | git clone git://github.com/netdata/libnodave.git 8 | cd libnodave 9 | make 10 | sudo make install 11 | 12 | Install the python wrapper to your system 13 | 14 | python setup.py install 15 | 16 | 17 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | import os 4 | from setuptools import setup 5 | 6 | def read(fname): 7 | return open(os.path.join(os.path.dirname(__file__), fname)).read() 8 | 9 | setup( 10 | name='libnodave', 11 | version='1.0', 12 | description='LibNoDave C lib wrapper', 13 | long_description=read('README.md'), 14 | author='Wouter DHaeseleer', 15 | author_email='info@netdata.be', 16 | url='http://netdata.be', 17 | dependency_links = ['https://github.com/netdata/libnodave/zipball/master'], 18 | packages=['libnodave'], 19 | ) 20 | -------------------------------------------------------------------------------- /libnodave/__init__.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | #libnodave.py 4 | 5 | """ 6 | ein Wrapper für die c- Bibliothek libnodave 7 | 8 | TODO: 9 | Methode um Merkerbyte als Dict. abzurufen 10 | 11 | """ 12 | import ctypes 13 | import os 14 | import code 15 | import readline 16 | import atexit 17 | 18 | 19 | # copied constants from nodave.h 20 | #Protocol types to be used with newInterface: 21 | daveProtoMPI = 0 # MPI for S7 300/400 22 | daveProtoMPI2 = 1 # MPI for S7 300/400, "Andrew's version" without STX 23 | daveProtoMPI3 = 2 # MPI for S7 300/400, Step 7 Version, not yet implemented 24 | daveProtoMPI4 = 3 # MPI for S7 300/400, "Andrew's version" with STX 25 | 26 | daveProtoPPI = 10 # PPI for S7 200 27 | daveProtoAS511 = 20 # S5 programming port protocol 28 | daveProtoS7online = 50 # use s7onlinx.dll for transport 29 | daveProtoISOTCP = 122 # ISO over TCP */ 30 | daveProtoISOTCP243 = 123 # ISO over TCP with CP243 */ 31 | daveProtoISOTCPR = 124 # ISO over TCP with Routing */ 32 | 33 | daveProtoMPI_IBH = 223 # MPI with IBH NetLink MPI to ethernet gateway */ 34 | daveProtoPPI_IBH = 224 # PPI with IBH NetLink PPI to ethernet gateway */ 35 | 36 | daveProtoNLpro = 230 # MPI with NetLink Pro MPI to ethernet gateway */ 37 | 38 | daveProtoUserTransport = 255 # Libnodave will pass the PDUs of S7 Communication to user */ 39 | # defined call back functions. */ 40 | 41 | #ProfiBus speed constants: 42 | daveSpeed9k = 0 43 | daveSpeed19k = 1 44 | daveSpeed187k = 2 45 | daveSpeed500k = 3 46 | daveSpeed1500k = 4 47 | daveSpeed45k = 5 48 | daveSpeed93k = 6 49 | 50 | # S7 specific constants: 51 | daveBlockType_OB = '8' 52 | daveBlockType_DB = 'A' 53 | daveBlockType_SDB = 'B' 54 | daveBlockType_FC = 'C' 55 | daveBlockType_SFC = 'D' 56 | daveBlockType_FB = 'E' 57 | daveBlockType_SFB = 'F' 58 | 59 | daveS5BlockType_DB = 0x01 60 | daveS5BlockType_SB = 0x02 61 | daveS5BlockType_PB = 0x04 62 | daveS5BlockType_FX = 0x05 63 | daveS5BlockType_FB = 0x08 64 | daveS5BlockType_DX = 0x0C 65 | daveS5BlockType_OB = 0x10 66 | 67 | 68 | #Use these constants for parameter "area" in daveReadBytes and daveWriteBytes 69 | daveSysInfo = 0x3 # System info of 200 family 70 | daveSysFlags = 0x5 # System flags of 200 family 71 | daveAnaIn = 0x6 # analog inputs of 200 family 72 | daveAnaOut = 0x7 # analog outputs of 200 family 73 | 74 | daveP = 0x80 # direct peripheral access 75 | daveInputs = 0x81 76 | daveOutputs = 0x82 77 | daveFlags = 0x83 78 | daveDB = 0x84 # data blocks 79 | daveDI = 0x85 # instance data blocks 80 | daveLocal = 0x86 # not tested 81 | daveV = 0x87 # don't know what it is 82 | daveCounter = 28 # S7 counters 83 | daveTimer = 29 # S7 timers 84 | daveCounter200 = 30 # IEC counters (200 family) 85 | daveTimer200 = 31 # IEC timers (200 family) 86 | daveSysDataS5 = 0x86 # system data area ? 87 | daveRawMemoryS5 = 0 # just the raw memory 88 | 89 | 90 | #locate the acutal location so we will later find the 91 | #libnodave-libs 92 | APPDIR = os.path.dirname(os.path.abspath(__file__)) 93 | 94 | if os.name == 'nt': 95 | DLL_LOC = os.path.join(APPDIR, 'libnodave', 'win', 'libnodave.dll') 96 | elif os.name == 'posix': 97 | DLL_LOC = 'libnodave.so' 98 | else: 99 | print 'only win and linux supportet yet' 100 | 101 | 102 | def int_to_bitarr(integer): 103 | """ 104 | aus einem übergebenen Integer ein 8-stelliges Array erstellen in dem die einzelnen 105 | Enthaltenen Bits aufzufinden sind 106 | im bitarr sind die Positionen im array gleich den Werten im Merkerbyte auf der SPS 107 | m0.0 ist 1. Bit im Merkerbyte (arr[0] 108 | """ 109 | string = bin(integer)[2:] 110 | arr = list() 111 | 112 | for bit in xrange(8 - len(string)): 113 | arr.append(0) 114 | 115 | for bit in string: 116 | arr.append(int(bit)) 117 | 118 | arr.reverse() 119 | return arr 120 | 121 | def bitarr_to_int(bitarr): 122 | """ 123 | eine liste die ein Byte repräsentiert in den passenden 124 | integer-Wert umrechnen 125 | """ 126 | str_bitarr = list() 127 | bitarr.reverse() 128 | for elem in bitarr: 129 | str_bitarr.append(str(elem)) 130 | print str_bitarr 131 | string = ''.join(str_bitarr) 132 | return int(string,2) 133 | 134 | 135 | #class to represent a c-struct 136 | class _daveOSserialType(ctypes.Structure): 137 | _fields_ = [("rfd", ctypes.c_int), 138 | ("wfd", ctypes.c_int)] 139 | 140 | 141 | class libnodave(object): 142 | def __init__(self): 143 | 144 | self. fds = _daveOSserialType() 145 | self.init_dll() 146 | 147 | self.buffer = ctypes.create_string_buffer('buffer') 148 | self.buffer_p = ctypes.pointer(self.buffer) 149 | 150 | def init_dll(self): 151 | """ 152 | initiate the os depending dll-File 153 | set argtypes and resttypes for used functions 154 | """ 155 | if os.name == 'nt': 156 | self.dave = ctypes.windll.LoadLibrary(DLL_LOC) 157 | else: 158 | self.dave = ctypes.cdll.LoadLibrary(DLL_LOC) 159 | 160 | self.dave.setPort.restype = ctypes.c_int 161 | self.dave.setPort.argtypes = [ctypes.c_char_p, 162 | ctypes.c_char_p, 163 | ctypes.c_char] 164 | 165 | self.dave.daveNewInterface.resttype = ctypes.c_void_p 166 | self.dave.daveNewInterface.argtypes = [_daveOSserialType, 167 | ctypes.c_char_p, 168 | ctypes.c_int, 169 | ctypes.c_int, 170 | ctypes.c_int] 171 | 172 | self.dave.openSocket.resttype = ctypes.c_int 173 | self.dave.openSocket.argtypes = [ 174 | ctypes.c_int, 175 | ctypes.c_char_p, 176 | ] 177 | 178 | self.dave.daveSetDebug.resttype = ctypes.c_void_p 179 | self.dave.daveSetDebug.argtypes = [ ctypes.c_int ] 180 | 181 | 182 | 183 | self.dave.daveInitAdapter.resttype = ctypes.c_void_p 184 | self.dave.daveInitAdapter.argtypes = [ctypes.c_void_p] 185 | 186 | self.dave.daveNewConnection.resttype = ctypes.c_void_p 187 | self.dave.daveNewConnection.argtypes = [ctypes.c_void_p, 188 | ctypes.c_int, 189 | ctypes.c_int, 190 | ctypes.c_int] 191 | 192 | self.dave.daveStop.resttype = ctypes.c_int 193 | self.dave.daveStop.argtypes = [ctypes.c_void_p] 194 | 195 | self.dave.daveConnectPLC.resttype = ctypes.c_int 196 | self.dave.daveConnectPLC.argtypes = [ctypes.c_void_p] 197 | 198 | self.dave.daveSetTimeout.resttype = ctypes.c_void_p 199 | self.dave.daveSetTimeout.argtypes = [ctypes.c_void_p, 200 | ctypes.c_int] 201 | 202 | self.dave.daveGetU8.resttype = ctypes.c_int 203 | self.dave.daveGetU8.argtypes = [ctypes.c_void_p] 204 | 205 | self.dave.daveDisconnectPLC.resttype = ctypes.c_int 206 | self.dave.daveDisconnectPLC.argtypes = [ctypes.c_void_p] 207 | 208 | self.dave.daveFree.resttype = None 209 | self.dave.daveFree.argtypes = [ctypes.c_void_p] 210 | 211 | self.dave.daveDisconnectAdapter.resttype = ctypes.c_int 212 | self.dave.daveDisconnectAdapter.argtypes = [ctypes.c_void_p] 213 | 214 | self.dave.daveReadBytes.resttype = ctypes.c_int 215 | self.dave.daveReadBytes.argtypes = [ctypes.c_void_p, 216 | ctypes.c_int, 217 | ctypes.c_int, 218 | ctypes.c_int, 219 | ctypes.c_int, 220 | ctypes.c_void_p] 221 | 222 | self.dave.daveGetCounterValue.resttype = ctypes.c_int 223 | self.dave.daveGetCounterValue.argtypes = [ctypes.c_void_p, 224 | ctypes.c_int, 225 | ctypes.c_int, 226 | ctypes.c_int, 227 | ctypes.c_int, 228 | ctypes.c_void_p] 229 | 230 | self.dave.daveWriteBytes.resttype = ctypes.c_int 231 | self.dave.daveWriteBytes.argtypes = [ctypes.c_void_p, 232 | ctypes.c_int, 233 | ctypes.c_int, 234 | ctypes.c_int, 235 | ctypes.c_int, 236 | ctypes.c_void_p] 237 | 238 | 239 | def set_port(self, port, baud='9600', parity = 'E'): 240 | """ 241 | set a serial connection port 242 | """ 243 | self.fds.rfd = self.dave.setPort(port, baud, parity) 244 | self.fds.wfd = self.fds.rfd 245 | 246 | def open_socket(self, ip, port=102): 247 | 248 | self.fds.rfd = self.dave.openSocket(port, ip) 249 | self.fds.wfd = self.fds.rfd 250 | 251 | def new_interface(self, name, localMPI, protocol, speed): 252 | """ 253 | EXPORTSPEC daveInterface * DECL2 daveNewInterface(_daveOSserialType nfd, 254 | char * nname, int localMPI, int protocol, int speed); 255 | """ 256 | self.di = self.dave.daveNewInterface(self.fds, name, localMPI, protocol, speed) 257 | 258 | def set_timeout(self, time): 259 | """ 260 | set a new timeout 261 | EXPORTSPEC void DECL2 daveSetTimeout(daveInterface * di, int tmo); 262 | """ 263 | self.dave.daveSetTimeout(self.di, time) 264 | 265 | def init_adapter(self): 266 | """ 267 | initiate the configurated adapter 268 | EXPORTSPEC int DECL2 _daveInitAdapterNLpro(daveInterface * di); 269 | """ 270 | self.dave.daveInitAdapter(self.di) 271 | 272 | def connect_plc(self, mpi, rack, slot): 273 | """ 274 | connect to the plc 275 | daveConnection * DECL2 daveNewConnection(daveInterface * di, int MPI,int rack, int slot); 276 | """ 277 | self.dc = self.dave.daveNewConnection(self.di, mpi, rack, slot) 278 | res = self.dave.daveConnectPLC(self.dc) 279 | 280 | def stop(self): 281 | """ 282 | connect to the plc 283 | daveConnection * DECL2 daveNewConnection(daveInterface * di, int MPI,int rack, int slot); 284 | """ 285 | res = self.dave.daveStop(self.dc) 286 | 287 | def SetDebug(self, level): 288 | """ 289 | connect to the plc 290 | daveConnection * DECL2 daveNewConnection(daveInterface * di, int MPI,int rack, int slot); 291 | """ 292 | res = self.dave.daveSetDebug(level) 293 | 294 | 295 | def disconnect(self): 296 | """ 297 | disconnect connection to PLC and Adapter 298 | """ 299 | self.dave.daveDisconnectPLC(self.dc) 300 | self.dave.daveFree(self.dc) 301 | self.dave.daveDisconnectAdapter(self.di) 302 | self.dave.daveFree(self.di) 303 | return True 304 | 305 | def read_bytes(self, area, db, start, len): 306 | """ 307 | int daveReadBytes(daveConnection * dc, int area, int DB, int start, int len, void * buffer); 308 | set the pointer to specified memory in the plc 309 | returns True if pointer is set 310 | """ 311 | res = self.dave.daveReadBytes(self.dc, area, db, start, len, self.buffer) 312 | if res == 0: 313 | return True 314 | return False 315 | 316 | def get_counter_value(self, counter_number): 317 | """ 318 | read a counter from the plc 319 | """ 320 | self.read_bytes(daveCounter, 0, 0, 1) 321 | counters = list() 322 | for val in xrange(16): 323 | counters.append(self.dave.daveGetCounterValue(self.dc)) 324 | return counters[counter_number] 325 | 326 | def get_counters(self): 327 | """ 328 | Liste mit allen Zählern der S5 auslesen und zurückgeben 329 | TODO: wird das wirklich gebraucht? 330 | """ 331 | if self.read_bytes(daveCounter, 0, 0, 1): 332 | counters = list() 333 | for val in xrange(16): 334 | counters.append(self.dave.daveGetCounterValue(self.dc)) 335 | return counters 336 | return False 337 | 338 | def get_marker_byte(self, marker): 339 | """ 340 | einen merkerbyte auslesen 341 | rückgabewert ist ein Integer. Sollen die einzelnen Bits untersucht werden 342 | muss der rückgabewert nach binär konvertiert werden -> bin(result) 343 | """ 344 | if self.read_bytes(daveFlags, 0, marker, 1): 345 | return self.dave.daveGetU8(self.dc) 346 | return -1 347 | 348 | def get_output_byte(self, output): 349 | 350 | if self.read_bytes(daveOutputs, 0, output, 1): 351 | return self.dave.daveGetU8(self.dc) 352 | return -1 353 | 354 | def get_marker(self, marker, byte): 355 | """ 356 | einen bestimmten Merker aus einem Merkerbyte auslesen 357 | """ 358 | m_byte = self.get_marker_byte(marker) 359 | if m_byte >= 0: 360 | byte_arr = int_to_bitarr(m_byte) 361 | return byte_arr[byte] 362 | return False 363 | 364 | def get_output(self, output, byte): 365 | """ 366 | einen bestimmten Merker aus einem Merkerbyte auslesen 367 | """ 368 | m_byte = self.get_output_byte(output) 369 | if m_byte >= 0: 370 | byte_arr = int_to_bitarr(m_byte) 371 | return byte_arr[byte] 372 | return False 373 | 374 | 375 | def get_marker_byte_list(self, marker): 376 | """ 377 | ein Merkerbyte als liste zurückgeben 378 | get a list with a bits representing all marker from read byte 379 | """ 380 | if self.read_bytes(daveFlags, 0, marker, 1): 381 | return int_to_bitarr(self.dave.daveGetU8(self.dc)) 382 | return False 383 | 384 | def get_marker_byte_dict(self, marker): 385 | """ 386 | ein Merkerbyte als Dict zurückgeben 387 | """ 388 | _l = self.get_marker_byte_list(marker) 389 | print 'libnodave - merkerbyte:', _l 390 | d = dict() 391 | for val in xrange(8): 392 | d[val]=_l[val] 393 | return d 394 | 395 | def write_marker_byte(self, marker, value): 396 | """ 397 | EXPORTSPEC int DECL2 daveWriteBytes(daveConnection * dc, int area, int DB, int start, 398 | int len, void * buffer); 399 | ein Merkerbyte in die SPS schreiben 400 | TODO: anpassen und testen 401 | """ 402 | buffer = ctypes.c_byte(int(value)) 403 | buffer_p = ctypes.pointer(buffer) 404 | self.dave.daveWriteBytes(self.dc, daveFlags, 0, marker, 1, buffer_p) 405 | 406 | def write_vm_byte(self, vm, value): 407 | """ 408 | EXPORTSPEC int DECL2 daveWriteBytes(daveConnection * dc, int area, int DB, int start, 409 | int len, void * buffer); 410 | ein Merkerbyte in die SPS schreiben 411 | TODO: anpassen und testen 412 | """ 413 | buffer = ctypes.c_byte(int(value)) 414 | buffer_p = ctypes.pointer(buffer) 415 | self.dave.daveWriteBytes(self.dc, daveDB, 1, vm, 1, buffer_p) 416 | 417 | def outputs(self): 418 | Q1 = self.get_output(0,0) 419 | Q2 = self.get_output(0,1) 420 | Q3 = self.get_output(0,2) 421 | Q4 = self.get_output(0,3) 422 | 423 | print "Padverlichting : " + str(Q1) 424 | print "Tuinhuisverlichting : " + str(Q2) 425 | print "Tuinhuis buiten : " + str(Q3) 426 | print "Terrasverlichting : " + str(Q4) 427 | 428 | 429 | # Calling this class will launch an interactive console 430 | class HistoryConsole(code.InteractiveConsole): 431 | def __init__(self, locals=None, filename="", histfile=os.path.expanduser("~/.console-history")): 432 | code.InteractiveConsole.__init__(self, locals, filename) 433 | try: 434 | import readline 435 | except ImportError: 436 | pass 437 | else: 438 | try: 439 | import rlcompleter 440 | readline.set_completer(rlcompleter.Completer(locals).complete) 441 | except ImportError: 442 | pass 443 | readline.parse_and_bind("tab: complete") 444 | self.init_history(histfile) 445 | 446 | def init_history(self, histfile): 447 | readline.parse_and_bind("tab: complete") 448 | if hasattr(readline, "read_history_file"): 449 | try: 450 | readline.read_history_file(histfile) 451 | except IOError: 452 | pass 453 | atexit.register(self.save_history, histfile) 454 | 455 | def save_history(self, histfile): 456 | readline.write_history_file(histfile) 457 | --------------------------------------------------------------------------------