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