diff -r cd81a7a6e55c -r cb9901076a21 runtime/PLCObject.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/runtime/PLCObject.py Wed Aug 20 00:11:40 2008 +0200 @@ -0,0 +1,258 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +#This file is part of Beremiz, a Integrated Development Environment for +#programming IEC 61131-3 automates supporting plcopen standard and CanFestival. +# +#Copyright (C) 2007: Edouard TISSERANT and Laurent BESSARD +# +#See COPYING file for copyrights details. +# +#This library is free software; you can redistribute it and/or +#modify it under the terms of the GNU General Public +#License as published by the Free Software Foundation; either +#version 2.1 of the License, or (at your option) any later version. +# +#This library is distributed in the hope that it will be useful, +#but WITHOUT ANY WARRANTY; without even the implied warranty of +#MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +#General Public License for more details. +# +#You should have received a copy of the GNU General Public +#License along with this library; if not, write to the Free Software +#Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +import Pyro.core as pyro +from threading import Timer +import ctypes, os, dl, commands +#, sys +#sys.setdlopenflags(dl.RTLD_NOW | dl.RTLD_GLOBAL) + +if os.name == ("nt", "ce"): + from _ctypes import LoadLibrary as dlopen + from _ctypes import FreeLibrary as dlclose +elif os.name == "posix": + from _ctypes import dlopen, dlclose + +import os,sys,traceback + +lib_ext ={ + "linux2":".so", + "win32":".dll", + }.get(sys.platform, "") + +class PLCObject(pyro.ObjBase): + def __init__(self, workingdir, daemon): + pyro.ObjBase.__init__(self) + self.workingdir = workingdir + self.PLCStatus = "Stopped" + self.PLClibraryHandle = None + # Creates fake C funcs proxies + self._FreePLC() + self.daemon = daemon + + # Get the last transfered PLC if connector must be restart + try: + self.CurrentPLCFilename=open( + self._GetMD5FileName(), + "r").read().strip() + lib_ext + except Exception, e: + self.PLCStatus = "Empty" + self.CurrentPLCFilename=None + + def _GetMD5FileName(self): + return os.path.join(self.workingdir, "lasttransferedPLC.md5") + + def _GetLibFileName(self): + return os.path.join(self.workingdir,self.CurrentPLCFilename) + + + def _LoadNewPLC(self): + """ + Load PLC library + Declare all functions, arguments and return values + """ + print "Load PLC" + try: + self._PLClibraryHandle = dlopen(self._GetLibFileName()) + self.PLClibraryHandle = ctypes.CDLL(self.CurrentPLCFilename, handle=self._PLClibraryHandle) + + self._startPLC = self.PLClibraryHandle.startPLC + self._startPLC.restype = ctypes.c_int + self._startPLC.argtypes = [ctypes.c_int, ctypes.POINTER(ctypes.c_char_p)] + + self._stopPLC = self.PLClibraryHandle.stopPLC + self._stopPLC.restype = None + + self._ResetDebugVariables = self.PLClibraryHandle.ResetDebugVariables + self._ResetDebugVariables.restype = None + + self._RegisterDebugVariable = self.PLClibraryHandle.ResetDebugVariables + self._RegisterDebugVariable.restype = None + + self._IterDebugData = self.PLClibraryHandle.IterDebugData + self._IterDebugData.restype = ctypes.c_void_p + self._IterDebugData.argtypes = [ctypes.POINTER(ctypes.c_int), ctypes.POINTER(ctypes.c_char_p)] + + self._FreeDebugData = self.PLClibraryHandle.FreeDebugData + self._FreeDebugData.restype = None + return True + except: + print traceback.format_exc() + return False + + def _FreePLC(self): + """ + Unload PLC library. + This is also called by __init__ to create dummy C func proxies + """ + # Forget all refs to library + self._startPLC = lambda:None + self._stopPLC = lambda:None + self._ResetDebugVariables = lambda:None + self._RegisterDebugVariable = lambda x:None + self._IterDebugData = lambda x,y:None + self._FreeDebugData = lambda:None + self.PLClibraryHandle = None + # Unload library explicitely + if getattr(self,"_PLClibraryHandle",None) is not None: + print "Unload PLC" + dlclose(self._PLClibraryHandle) + res = self._DetectDirtyLibs() + else: + res = False + + self._PLClibraryHandle = None + + return res + + def _DetectDirtyLibs(self): + # Detect dirty libs + # Get lib dependencies (for dirty lib detection) + if os.name == "posix": + # parasiting libs listed with ldd + badlibs = [ toks.split()[0] for toks in commands.getoutput( + "ldd "+self._GetLibFileName()).splitlines() ] + for badlib in badlibs: + if badlib[:6] in ["libwx_", + "libwxs", + "libgtk", + "libgdk", + "libatk", + "libpan", + "libX11", + ]: + badhandle = dlopen(badlib, dl.RTLD_NOLOAD) + print "Dirty lib detected :" + badlib + #dlclose(badhandle) + return True + return False + + + def StartPLC(self): + print "StartPLC" + if self.CurrentPLCFilename is not None and self.PLCStatus == "Stopped": + c_argv = ctypes.c_char_p * len(sys.argv) + if self._LoadNewPLC() and self._startPLC(len(sys.argv),c_argv(*sys.argv)) == 0: + self.PLCStatus = "Started" + return True + else: + print "_StartPLC did not return 0 !" + return False + + def StopPLC(self): + if self.PLCStatus == "Started": + self._stopPLC() + self.PLCStatus = "Stopped" + if self._FreePLC(): + self.PLCStatus = "Dirty" + return True + return False + + def _Reload(self): + self.daemon.shutdown(True) + self.daemon.sock.close() + os.execv(sys.executable,[sys.executable]+sys.argv[:]) + # never reached + return 0 + + def ForceReload(self): + # respawn python interpreter + Timer(0.1,self._Reload).start() + return True + + def GetPLCstatus(self): + return self.PLCStatus + + def NewPLC(self, md5sum, data, extrafiles): + print "NewPLC (%s)"%md5sum + if self.PLCStatus in ["Stopped", "Empty", "Dirty"]: + NewFileName = md5sum + lib_ext + extra_files_log = os.path.join(self.workingdir,"extra_files.txt") + try: + os.remove(os.path.join(self.workingdir, + self.CurrentPLCFilename)) + for filename in file(extra_files_log, "r").readlines() + extra_files_log: + try: + os.remove(os.path.join(self.workingdir, filename)) + except: + pass + except: + pass + + try: + # Create new PLC file + open(os.path.join(self.workingdir,NewFileName), + 'wb').write(data) + + # Store new PLC filename based on md5 key + open(self._GetMD5FileName(), "w").write(md5sum) + + # Then write the files + log = file(extra_files_log, "w") + for fname,fdata in extrafiles: + fpath = os.path.join(self.workingdir,fname) + open(fpath, "wb").write(fdata) + log.write(fname+'\n') + + # Store new PLC filename + self.CurrentPLCFilename = NewFileName + except: + print traceback.format_exc() + return False + if self.PLCStatus == "Empty": + self.PLCStatus = "Stopped" + return True + return False + + def MatchMD5(self, MD5): + try: + last_md5 = open(self._GetMD5FileName(), "r").read() + return last_md5 == MD5 + except: + return False + + def SetTraceVariablesList(self, idxs): + """ + Call ctype imported function to append + these indexes to registred variables in PLC debugger + """ + # keep a copy of requested idx + self._Idxs = idxs[:] + self._ResetDebugVariables() + for idx in idxs: + self._RegisterDebugVariable(idx) + + def GetTraceVariables(self): + """ + Return a list of variables, corresponding to the list of requiered idx + """ + self._WaitDebugData() + + for idx in self._Idxs: + buffer=self._IterDebugData() + self._FreeDebugData() + + + +