runtime/PLCObject.py
changeset 203 cb9901076a21
child 209 08dc3d064cb5
equal deleted inserted replaced
202:cd81a7a6e55c 203:cb9901076a21
       
     1 #!/usr/bin/env python
       
     2 # -*- coding: utf-8 -*-
       
     3 
       
     4 #This file is part of Beremiz, a Integrated Development Environment for
       
     5 #programming IEC 61131-3 automates supporting plcopen standard and CanFestival. 
       
     6 #
       
     7 #Copyright (C) 2007: Edouard TISSERANT and Laurent BESSARD
       
     8 #
       
     9 #See COPYING file for copyrights details.
       
    10 #
       
    11 #This library is free software; you can redistribute it and/or
       
    12 #modify it under the terms of the GNU General Public
       
    13 #License as published by the Free Software Foundation; either
       
    14 #version 2.1 of the License, or (at your option) any later version.
       
    15 #
       
    16 #This library is distributed in the hope that it will be useful,
       
    17 #but WITHOUT ANY WARRANTY; without even the implied warranty of
       
    18 #MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
       
    19 #General Public License for more details.
       
    20 #
       
    21 #You should have received a copy of the GNU General Public
       
    22 #License along with this library; if not, write to the Free Software
       
    23 #Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
       
    24 
       
    25 import Pyro.core as pyro
       
    26 from threading import Timer
       
    27 import ctypes, os, dl, commands
       
    28 #, sys
       
    29 #sys.setdlopenflags(dl.RTLD_NOW | dl.RTLD_GLOBAL)
       
    30 
       
    31 if os.name == ("nt", "ce"):
       
    32     from _ctypes import LoadLibrary as dlopen
       
    33     from _ctypes import FreeLibrary as dlclose
       
    34 elif os.name == "posix":
       
    35     from _ctypes import dlopen, dlclose
       
    36 
       
    37 import os,sys,traceback
       
    38 
       
    39 lib_ext ={
       
    40      "linux2":".so",
       
    41      "win32":".dll",
       
    42      }.get(sys.platform, "")
       
    43 
       
    44 class PLCObject(pyro.ObjBase):
       
    45     def __init__(self, workingdir, daemon):
       
    46         pyro.ObjBase.__init__(self)
       
    47         self.workingdir = workingdir
       
    48         self.PLCStatus = "Stopped"
       
    49         self.PLClibraryHandle = None
       
    50         # Creates fake C funcs proxies
       
    51         self._FreePLC()
       
    52         self.daemon = daemon
       
    53         
       
    54         # Get the last transfered PLC if connector must be restart
       
    55         try:
       
    56             self.CurrentPLCFilename=open(
       
    57                              self._GetMD5FileName(),
       
    58                              "r").read().strip() + lib_ext
       
    59         except Exception, e:
       
    60             self.PLCStatus = "Empty"
       
    61             self.CurrentPLCFilename=None
       
    62 
       
    63     def _GetMD5FileName(self):
       
    64         return os.path.join(self.workingdir, "lasttransferedPLC.md5")
       
    65 
       
    66     def _GetLibFileName(self):
       
    67         return os.path.join(self.workingdir,self.CurrentPLCFilename)
       
    68 
       
    69 
       
    70     def _LoadNewPLC(self):
       
    71         """
       
    72         Load PLC library
       
    73         Declare all functions, arguments and return values
       
    74         """
       
    75         print "Load PLC"
       
    76         try:
       
    77             self._PLClibraryHandle = dlopen(self._GetLibFileName())
       
    78             self.PLClibraryHandle = ctypes.CDLL(self.CurrentPLCFilename, handle=self._PLClibraryHandle)
       
    79     
       
    80             self._startPLC = self.PLClibraryHandle.startPLC
       
    81             self._startPLC.restype = ctypes.c_int
       
    82             self._startPLC.argtypes = [ctypes.c_int, ctypes.POINTER(ctypes.c_char_p)]
       
    83             
       
    84             self._stopPLC = self.PLClibraryHandle.stopPLC
       
    85             self._stopPLC.restype = None
       
    86     
       
    87             self._ResetDebugVariables = self.PLClibraryHandle.ResetDebugVariables
       
    88             self._ResetDebugVariables.restype = None
       
    89     
       
    90             self._RegisterDebugVariable = self.PLClibraryHandle.ResetDebugVariables
       
    91             self._RegisterDebugVariable.restype = None
       
    92     
       
    93             self._IterDebugData = self.PLClibraryHandle.IterDebugData
       
    94             self._IterDebugData.restype = ctypes.c_void_p
       
    95             self._IterDebugData.argtypes = [ctypes.POINTER(ctypes.c_int), ctypes.POINTER(ctypes.c_char_p)]
       
    96     
       
    97             self._FreeDebugData = self.PLClibraryHandle.FreeDebugData
       
    98             self._FreeDebugData.restype = None
       
    99             return True
       
   100         except:
       
   101             print traceback.format_exc()
       
   102             return False
       
   103 
       
   104     def _FreePLC(self):
       
   105         """
       
   106         Unload PLC library.
       
   107         This is also called by __init__ to create dummy C func proxies
       
   108         """
       
   109         # Forget all refs to library
       
   110         self._startPLC = lambda:None
       
   111         self._stopPLC = lambda:None
       
   112         self._ResetDebugVariables = lambda:None
       
   113         self._RegisterDebugVariable = lambda x:None
       
   114         self._IterDebugData = lambda x,y:None
       
   115         self._FreeDebugData = lambda:None
       
   116         self.PLClibraryHandle = None
       
   117         # Unload library explicitely
       
   118         if getattr(self,"_PLClibraryHandle",None) is not None:
       
   119             print "Unload PLC"
       
   120             dlclose(self._PLClibraryHandle)
       
   121             res = self._DetectDirtyLibs()
       
   122         else:
       
   123             res = False
       
   124 
       
   125         self._PLClibraryHandle = None
       
   126 
       
   127         return res
       
   128 
       
   129     def _DetectDirtyLibs(self):
       
   130         # Detect dirty libs
       
   131         # Get lib dependencies (for dirty lib detection)
       
   132         if os.name == "posix":
       
   133             # parasiting libs listed with ldd
       
   134             badlibs = [ toks.split()[0] for toks in commands.getoutput(
       
   135                             "ldd "+self._GetLibFileName()).splitlines() ]
       
   136             for badlib in badlibs:
       
   137                 if badlib[:6] in ["libwx_",
       
   138                                   "libwxs",
       
   139                                   "libgtk",
       
   140                                   "libgdk",
       
   141                                   "libatk",
       
   142                                   "libpan",
       
   143                                   "libX11",
       
   144                                   ]:
       
   145                     badhandle = dlopen(badlib, dl.RTLD_NOLOAD)
       
   146                     print "Dirty lib detected :" + badlib
       
   147                     #dlclose(badhandle)
       
   148                     return True
       
   149         return False
       
   150 
       
   151     
       
   152     def StartPLC(self):
       
   153         print "StartPLC"
       
   154         if self.CurrentPLCFilename is not None and self.PLCStatus == "Stopped":
       
   155             c_argv = ctypes.c_char_p * len(sys.argv)
       
   156             if self._LoadNewPLC() and self._startPLC(len(sys.argv),c_argv(*sys.argv)) == 0:
       
   157                 self.PLCStatus = "Started"
       
   158                 return True
       
   159             else:
       
   160                 print "_StartPLC did not return 0 !"
       
   161         return False
       
   162 
       
   163     def StopPLC(self):
       
   164         if self.PLCStatus == "Started":
       
   165             self._stopPLC()
       
   166             self.PLCStatus = "Stopped"
       
   167             if self._FreePLC():
       
   168                 self.PLCStatus = "Dirty"
       
   169             return True
       
   170         return False
       
   171 
       
   172     def _Reload(self):
       
   173         self.daemon.shutdown(True)
       
   174         self.daemon.sock.close()
       
   175         os.execv(sys.executable,[sys.executable]+sys.argv[:])
       
   176         # never reached
       
   177         return 0
       
   178 
       
   179     def ForceReload(self):
       
   180         # respawn python interpreter
       
   181         Timer(0.1,self._Reload).start()
       
   182         return True
       
   183 
       
   184     def GetPLCstatus(self):
       
   185         return self.PLCStatus
       
   186     
       
   187     def NewPLC(self, md5sum, data, extrafiles):
       
   188         print "NewPLC (%s)"%md5sum
       
   189         if self.PLCStatus in ["Stopped", "Empty", "Dirty"]:
       
   190             NewFileName = md5sum + lib_ext
       
   191             extra_files_log = os.path.join(self.workingdir,"extra_files.txt")
       
   192             try:
       
   193                 os.remove(os.path.join(self.workingdir,
       
   194                                        self.CurrentPLCFilename))
       
   195                 for filename in file(extra_files_log, "r").readlines() + extra_files_log:
       
   196                     try:
       
   197                         os.remove(os.path.join(self.workingdir, filename))
       
   198                     except:
       
   199                         pass
       
   200             except:
       
   201                 pass
       
   202                         
       
   203             try:
       
   204                 # Create new PLC file
       
   205                 open(os.path.join(self.workingdir,NewFileName),
       
   206                      'wb').write(data)
       
   207         
       
   208                 # Store new PLC filename based on md5 key
       
   209                 open(self._GetMD5FileName(), "w").write(md5sum)
       
   210         
       
   211                 # Then write the files
       
   212                 log = file(extra_files_log, "w")
       
   213                 for fname,fdata in extrafiles:
       
   214                     fpath = os.path.join(self.workingdir,fname)
       
   215                     open(fpath, "wb").write(fdata)
       
   216                     log.write(fname+'\n')
       
   217 
       
   218                 # Store new PLC filename
       
   219                 self.CurrentPLCFilename = NewFileName
       
   220             except:
       
   221                 print traceback.format_exc()
       
   222                 return False
       
   223             if self.PLCStatus == "Empty":
       
   224                 self.PLCStatus = "Stopped"
       
   225             return True
       
   226         return False
       
   227 
       
   228     def MatchMD5(self, MD5):
       
   229         try:
       
   230             last_md5 = open(self._GetMD5FileName(), "r").read()
       
   231             return last_md5 == MD5
       
   232         except:
       
   233             return False
       
   234     
       
   235     def SetTraceVariablesList(self, idxs):
       
   236         """
       
   237         Call ctype imported function to append 
       
   238         these indexes to registred variables in PLC debugger
       
   239         """
       
   240         # keep a copy of requested idx
       
   241         self._Idxs = idxs[:]
       
   242         self._ResetDebugVariables()
       
   243         for idx in idxs:
       
   244             self._RegisterDebugVariable(idx)
       
   245 
       
   246     def GetTraceVariables(self):
       
   247         """
       
   248         Return a list of variables, corresponding to the list of requiered idx
       
   249         """
       
   250         self._WaitDebugData()
       
   251 
       
   252         for idx in self._Idxs:
       
   253             buffer=self._IterDebugData()
       
   254         self._FreeDebugData()
       
   255         
       
   256         
       
   257 
       
   258