runtime/PLCObject.py
changeset 1784 64beb9e9c749
parent 1783 3311eea28d56
child 1831 56b48961cc68
equal deleted inserted replaced
1729:31e63e25b4cc 1784:64beb9e9c749
    21 # License along with this library; if not, write to the Free Software
    21 # License along with this library; if not, write to the Free Software
    22 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
    22 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
    23 
    23 
    24 import Pyro.core as pyro
    24 import Pyro.core as pyro
    25 from threading import Timer, Thread, Lock, Semaphore, Event
    25 from threading import Timer, Thread, Lock, Semaphore, Event
    26 import ctypes, os, commands, types, sys
    26 import ctypes
       
    27 import os
       
    28 import commands
       
    29 import types
       
    30 import sys
       
    31 import traceback
    27 from targets.typemapping import LogLevelsDefault, LogLevelsCount, TypeTranslator, UnpackDebugBuffer
    32 from targets.typemapping import LogLevelsDefault, LogLevelsCount, TypeTranslator, UnpackDebugBuffer
    28 from time import time
    33 from time import time
    29 
    34 
    30 
    35 
    31 if os.name in ("nt", "ce"):
    36 if os.name in ("nt", "ce"):
    32     from _ctypes import LoadLibrary as dlopen
    37     from _ctypes import LoadLibrary as dlopen
    33     from _ctypes import FreeLibrary as dlclose
    38     from _ctypes import FreeLibrary as dlclose
    34 elif os.name == "posix":
    39 elif os.name == "posix":
    35     from _ctypes import dlopen, dlclose
    40     from _ctypes import dlopen, dlclose
    36 
    41 
    37 import traceback
    42 
    38 def get_last_traceback(tb):
    43 def get_last_traceback(tb):
    39     while tb.tb_next:
    44     while tb.tb_next:
    40         tb = tb.tb_next
    45         tb = tb.tb_next
    41     return tb
    46     return tb
    42 
    47 
    43 lib_ext ={
    48 
    44      "linux2":".so",
    49 lib_ext = {
    45      "win32":".dll",
    50      "linux2": ".so",
       
    51      "win32":  ".dll",
    46      }.get(sys.platform, "")
    52      }.get(sys.platform, "")
       
    53 
    47 
    54 
    48 def PLCprint(message):
    55 def PLCprint(message):
    49     sys.stdout.write("PLCobject : "+message+"\n")
    56     sys.stdout.write("PLCobject : "+message+"\n")
    50     sys.stdout.flush()
    57     sys.stdout.flush()
       
    58 
    51 
    59 
    52 class PLCObject(pyro.ObjBase):
    60 class PLCObject(pyro.ObjBase):
    53     def __init__(self, workingdir, daemon, argv, statuschange, evaluator, pyruntimevars):
    61     def __init__(self, workingdir, daemon, argv, statuschange, evaluator, pyruntimevars):
    54         pyro.ObjBase.__init__(self)
    62         pyro.ObjBase.__init__(self)
    55         self.evaluator = evaluator
    63         self.evaluator = evaluator
    56         self.argv = [workingdir] + argv # force argv[0] to be "path" to exec...
    64         self.argv = [workingdir] + argv  # force argv[0] to be "path" to exec...
    57         self.workingdir = workingdir
    65         self.workingdir = workingdir
    58         self.PLCStatus = "Empty"
    66         self.PLCStatus = "Empty"
    59         self.PLClibraryHandle = None
    67         self.PLClibraryHandle = None
    60         self.PLClibraryLock = Lock()
    68         self.PLClibraryLock = Lock()
    61         self.DummyIteratorLock = None
    69         self.DummyIteratorLock = None
    73         self.Traces = []
    81         self.Traces = []
    74 
    82 
    75     def AutoLoad(self):
    83     def AutoLoad(self):
    76         # Get the last transfered PLC if connector must be restart
    84         # Get the last transfered PLC if connector must be restart
    77         try:
    85         try:
    78             self.CurrentPLCFilename=open(
    86             self.CurrentPLCFilename = open(
    79                              self._GetMD5FileName(),
    87                              self._GetMD5FileName(),
    80                              "r").read().strip() + lib_ext
    88                              "r").read().strip() + lib_ext
    81             if self.LoadPLC():
    89             if self.LoadPLC():
    82                 self.PLCStatus = "Stopped"
    90                 self.PLCStatus = "Stopped"
    83         except Exception, e:
    91         except Exception, e:
    84             self.PLCStatus = "Empty"
    92             self.PLCStatus = "Empty"
    85             self.CurrentPLCFilename=None
    93             self.CurrentPLCFilename = None
    86 
    94 
    87     def StatusChange(self):
    95     def StatusChange(self):
    88         if self.statuschange is not None:
    96         if self.statuschange is not None:
    89             for callee in self.statuschange:
    97             for callee in self.statuschange:
    90                 callee(self.PLCStatus)
    98                 callee(self.PLCStatus)
   100     def ResetLogCount(self):
   108     def ResetLogCount(self):
   101         if self._ResetLogCount is not None:
   109         if self._ResetLogCount is not None:
   102             self._ResetLogCount()
   110             self._ResetLogCount()
   103 
   111 
   104     def GetLogCount(self, level):
   112     def GetLogCount(self, level):
   105         if self._GetLogCount is not None :
   113         if self._GetLogCount is not None:
   106             return int(self._GetLogCount(level))
   114             return int(self._GetLogCount(level))
   107         elif self._loading_error is not None and level==0:
   115         elif self._loading_error is not None and level == 0:
   108             return 1
   116             return 1
   109 
   117 
   110     def GetLogMessage(self, level, msgid):
   118     def GetLogMessage(self, level, msgid):
   111         tick = ctypes.c_uint32()
   119         tick = ctypes.c_uint32()
   112         tv_sec = ctypes.c_uint32()
   120         tv_sec = ctypes.c_uint32()
   113         tv_nsec = ctypes.c_uint32()
   121         tv_nsec = ctypes.c_uint32()
   114         if self._GetLogMessage is not None:
   122         if self._GetLogMessage is not None:
   115             maxsz = len(self._log_read_buffer)-1
   123             maxsz = len(self._log_read_buffer)-1
   116             sz = self._GetLogMessage(level, msgid,
   124             sz = self._GetLogMessage(level, msgid,
   117                 self._log_read_buffer, maxsz,
   125                                      self._log_read_buffer, maxsz,
   118                 ctypes.byref(tick),
   126                                      ctypes.byref(tick),
   119                 ctypes.byref(tv_sec),
   127                                      ctypes.byref(tv_sec),
   120                 ctypes.byref(tv_nsec))
   128                                      ctypes.byref(tv_nsec))
   121             if sz and sz <= maxsz:
   129             if sz and sz <= maxsz:
   122                 self._log_read_buffer[sz] = '\x00'
   130                 self._log_read_buffer[sz] = '\x00'
   123                 return self._log_read_buffer.value,tick.value,tv_sec.value,tv_nsec.value
   131                 return self._log_read_buffer.value, tick.value, tv_sec.value, tv_nsec.value
   124         elif self._loading_error is not None and level==0:
   132         elif self._loading_error is not None and level == 0:
   125             return self._loading_error,0,0,0
   133             return self._loading_error, 0, 0, 0
   126         return None
   134         return None
   127 
   135 
   128     def _GetMD5FileName(self):
   136     def _GetMD5FileName(self):
   129         return os.path.join(self.workingdir, "lasttransferedPLC.md5")
   137         return os.path.join(self.workingdir, "lasttransferedPLC.md5")
   130 
   138 
   131     def _GetLibFileName(self):
   139     def _GetLibFileName(self):
   132         return os.path.join(self.workingdir,self.CurrentPLCFilename)
   140         return os.path.join(self.workingdir, self.CurrentPLCFilename)
   133 
       
   134 
   141 
   135     def LoadPLC(self):
   142     def LoadPLC(self):
   136         """
   143         """
   137         Load PLC library
   144         Load PLC library
   138         Declare all functions, arguments and return values
   145         Declare all functions, arguments and return values
   141         try:
   148         try:
   142             self._PLClibraryHandle = dlopen(self._GetLibFileName())
   149             self._PLClibraryHandle = dlopen(self._GetLibFileName())
   143             self.PLClibraryHandle = ctypes.CDLL(self.CurrentPLCFilename, handle=self._PLClibraryHandle)
   150             self.PLClibraryHandle = ctypes.CDLL(self.CurrentPLCFilename, handle=self._PLClibraryHandle)
   144 
   151 
   145             self.PLC_ID = ctypes.c_char_p.in_dll(self.PLClibraryHandle, "PLC_ID")
   152             self.PLC_ID = ctypes.c_char_p.in_dll(self.PLClibraryHandle, "PLC_ID")
   146             if len(md5) == 32 : 
   153             if len(md5) == 32:
   147                 self.PLC_ID.value = md5 
   154                 self.PLC_ID.value = md5
   148 
   155 
   149             self._startPLC = self.PLClibraryHandle.startPLC
   156             self._startPLC = self.PLClibraryHandle.startPLC
   150             self._startPLC.restype = ctypes.c_int
   157             self._startPLC.restype = ctypes.c_int
   151             self._startPLC.argtypes = [ctypes.c_int, ctypes.POINTER(ctypes.c_char_p)]
   158             self._startPLC.argtypes = [ctypes.c_int, ctypes.POINTER(ctypes.c_char_p)]
   152 
   159 
   161                 self._stopPLC = self._stopPLC_real
   168                 self._stopPLC = self._stopPLC_real
   162             else:
   169             else:
   163                 # If python confnode is not enabled, we reuse _PythonIterator
   170                 # If python confnode is not enabled, we reuse _PythonIterator
   164                 # as a call that block pythonthread until StopPLC
   171                 # as a call that block pythonthread until StopPLC
   165                 self.PlcStopping = Event()
   172                 self.PlcStopping = Event()
       
   173 
   166                 def PythonIterator(res, blkid):
   174                 def PythonIterator(res, blkid):
   167                     self.PlcStopping.clear()
   175                     self.PlcStopping.clear()
   168                     self.PlcStopping.wait()
   176                     self.PlcStopping.wait()
   169                     return None
   177                     return None
   170                 self._PythonIterator = PythonIterator
   178                 self._PythonIterator = PythonIterator
   172                 def __StopPLC():
   180                 def __StopPLC():
   173                     self._stopPLC_real()
   181                     self._stopPLC_real()
   174                     self.PlcStopping.set()
   182                     self.PlcStopping.set()
   175                 self._stopPLC = __StopPLC
   183                 self._stopPLC = __StopPLC
   176 
   184 
   177 
       
   178             self._ResetDebugVariables = self.PLClibraryHandle.ResetDebugVariables
   185             self._ResetDebugVariables = self.PLClibraryHandle.ResetDebugVariables
   179             self._ResetDebugVariables.restype = None
   186             self._ResetDebugVariables.restype = None
   180 
   187 
   181             self._RegisterDebugVariable = self.PLClibraryHandle.RegisterDebugVariable
   188             self._RegisterDebugVariable = self.PLClibraryHandle.RegisterDebugVariable
   182             self._RegisterDebugVariable.restype = None
   189             self._RegisterDebugVariable.restype = None
   205 
   212 
   206             self._LogMessage = self.PLClibraryHandle.LogMessage
   213             self._LogMessage = self.PLClibraryHandle.LogMessage
   207             self._LogMessage.restype = ctypes.c_int
   214             self._LogMessage.restype = ctypes.c_int
   208             self._LogMessage.argtypes = [ctypes.c_uint8, ctypes.c_char_p, ctypes.c_uint32]
   215             self._LogMessage.argtypes = [ctypes.c_uint8, ctypes.c_char_p, ctypes.c_uint32]
   209 
   216 
   210             self._log_read_buffer = ctypes.create_string_buffer(1<<14) #16K
   217             self._log_read_buffer = ctypes.create_string_buffer(1 << 14)  # 16K
   211             self._GetLogMessage = self.PLClibraryHandle.GetLogMessage
   218             self._GetLogMessage = self.PLClibraryHandle.GetLogMessage
   212             self._GetLogMessage.restype = ctypes.c_uint32
   219             self._GetLogMessage.restype = ctypes.c_uint32
   213             self._GetLogMessage.argtypes = [ctypes.c_uint8, ctypes.c_uint32, ctypes.c_char_p, ctypes.c_uint32, ctypes.POINTER(ctypes.c_uint32), ctypes.POINTER(ctypes.c_uint32), ctypes.POINTER(ctypes.c_uint32)]
   220             self._GetLogMessage.argtypes = [ctypes.c_uint8, ctypes.c_uint32, ctypes.c_char_p, ctypes.c_uint32, ctypes.POINTER(ctypes.c_uint32), ctypes.POINTER(ctypes.c_uint32), ctypes.POINTER(ctypes.c_uint32)]
   214 
   221 
   215             self._loading_error = None
   222             self._loading_error = None
   216 
   223 
   217             self.PythonRuntimeInit()
   224             self.PythonRuntimeInit()
   218 
   225 
   219             return True
   226             return True
   220         except:
   227         except Exception:
   221             self._loading_error = traceback.format_exc()
   228             self._loading_error = traceback.format_exc()
   222             PLCprint(self._loading_error)
   229             PLCprint(self._loading_error)
   223             return False
   230             return False
   224 
   231 
   225     def UnLoadPLC(self):
   232     def UnLoadPLC(self):
   231         Unload PLC library.
   238         Unload PLC library.
   232         This is also called by __init__ to create dummy C func proxies
   239         This is also called by __init__ to create dummy C func proxies
   233         """
   240         """
   234         self.PLClibraryLock.acquire()
   241         self.PLClibraryLock.acquire()
   235         # Forget all refs to library
   242         # Forget all refs to library
   236         self._startPLC = lambda x,y:None
   243         self._startPLC = lambda x, y: None
   237         self._stopPLC = lambda:None
   244         self._stopPLC = lambda: None
   238         self._ResetDebugVariables = lambda:None
   245         self._ResetDebugVariables = lambda: None
   239         self._RegisterDebugVariable = lambda x, y:None
   246         self._RegisterDebugVariable = lambda x, y: None
   240         self._IterDebugData = lambda x,y:None
   247         self._IterDebugData = lambda x, y: None
   241         self._FreeDebugData = lambda:None
   248         self._FreeDebugData = lambda: None
   242         self._GetDebugData = lambda:-1
   249         self._GetDebugData = lambda: -1
   243         self._suspendDebug = lambda x:-1
   250         self._suspendDebug = lambda x: -1
   244         self._resumeDebug = lambda:None
   251         self._resumeDebug = lambda: None
   245         self._PythonIterator = lambda:""
   252         self._PythonIterator = lambda: ""
   246         self._GetLogCount = None
   253         self._GetLogCount = None
   247         self._LogMessage = lambda l,m,s:PLCprint("OFF LOG :"+m)
   254         self._LogMessage = lambda l, m, s: PLCprint("OFF LOG :"+m)
   248         self._GetLogMessage = None
   255         self._GetLogMessage = None
   249         self.PLClibraryHandle = None
   256         self.PLClibraryHandle = None
   250         # Unload library explicitely
   257         # Unload library explicitely
   251         if getattr(self,"_PLClibraryHandle",None) is not None:
   258         if getattr(self, "_PLClibraryHandle", None) is not None:
   252             dlclose(self._PLClibraryHandle)
   259             dlclose(self._PLClibraryHandle)
   253             self._PLClibraryHandle = None
   260             self._PLClibraryHandle = None
   254 
   261 
   255         self.PLClibraryLock.release()
   262         self.PLClibraryLock.release()
   256         return False
   263         return False
   258     def PythonRuntimeCall(self, methodname):
   265     def PythonRuntimeCall(self, methodname):
   259         """
   266         """
   260         Calls init, start, stop or cleanup method provided by
   267         Calls init, start, stop or cleanup method provided by
   261         runtime python files, loaded when new PLC uploaded
   268         runtime python files, loaded when new PLC uploaded
   262         """
   269         """
   263         for method in self.python_runtime_vars.get("_runtime_%s"%methodname, []):
   270         for method in self.python_runtime_vars.get("_runtime_%s" % methodname, []):
   264             res,exp = self.evaluator(method)
   271             res, exp = self.evaluator(method)
   265             if exp is not None:
   272             if exp is not None:
   266                 self.LogMessage(0,'\n'.join(traceback.format_exception(*exp)))
   273                 self.LogMessage(0, '\n'.join(traceback.format_exception(*exp)))
   267 
   274 
   268     def PythonRuntimeInit(self):
   275     def PythonRuntimeInit(self):
   269         MethodNames = ["init", "start", "stop", "cleanup"]
   276         MethodNames = ["init", "start", "stop", "cleanup"]
   270         self.python_runtime_vars = globals().copy()
   277         self.python_runtime_vars = globals().copy()
   271         self.python_runtime_vars.update(self.pyruntimevars)
   278         self.python_runtime_vars.update(self.pyruntimevars)
   272 
   279 
   273         class PLCSafeGlobals:
   280         class PLCSafeGlobals:
   274             def __getattr__(_self, name):
   281             def __getattr__(_self, name):
   275                 try :
   282                 try:
   276                     t = self.python_runtime_vars["_"+name+"_ctype"]
   283                     t = self.python_runtime_vars["_"+name+"_ctype"]
   277                 except KeyError:
   284                 except KeyError:
   278                     raise KeyError("Try to get unknown shared global variable : %s"%name)
   285                     raise KeyError("Try to get unknown shared global variable : %s" % name)
   279                 v = t()
   286                 v = t()
   280                 r = self.python_runtime_vars["_PySafeGetPLCGlob_"+name](ctypes.byref(v))
   287                 r = self.python_runtime_vars["_PySafeGetPLCGlob_"+name](ctypes.byref(v))
   281                 return self.python_runtime_vars["_"+name+"_unpack"](v)
   288                 return self.python_runtime_vars["_"+name+"_unpack"](v)
       
   289 
   282             def __setattr__(_self, name, value):
   290             def __setattr__(_self, name, value):
   283                 try :
   291                 try:
   284                     t = self.python_runtime_vars["_"+name+"_ctype"]
   292                     t = self.python_runtime_vars["_"+name+"_ctype"]
   285                 except KeyError:
   293                 except KeyError:
   286                     raise KeyError("Try to set unknown shared global variable : %s"%name)
   294                     raise KeyError("Try to set unknown shared global variable : %s" % name)
   287                 v = self.python_runtime_vars["_"+name+"_pack"](t,value)
   295                 v = self.python_runtime_vars["_"+name+"_pack"](t, value)
   288                 self.python_runtime_vars["_PySafeSetPLCGlob_"+name](ctypes.byref(v))
   296                 self.python_runtime_vars["_PySafeSetPLCGlob_"+name](ctypes.byref(v))
   289 
   297 
   290         self.python_runtime_vars.update({
   298         self.python_runtime_vars.update({
   291             "PLCGlobals" : PLCSafeGlobals(),
   299             "PLCGlobals":     PLCSafeGlobals(),
   292             "WorkingDir" : self.workingdir,
   300             "WorkingDir":     self.workingdir,
   293             "PLCObject"  : self,
   301             "PLCObject":      self,
   294             "PLCBinary"  : self.PLClibraryHandle,
   302             "PLCBinary":      self.PLClibraryHandle,
   295             "PLCGlobalsDesc" : []})
   303             "PLCGlobalsDesc": []})
   296 
   304 
   297         for methodname in MethodNames :
   305         for methodname in MethodNames:
   298             self.python_runtime_vars["_runtime_%s"%methodname] = []
   306             self.python_runtime_vars["_runtime_%s" % methodname] = []
   299 
   307 
   300         try:
   308         try:
   301             filenames = os.listdir(self.workingdir)
   309             filenames = os.listdir(self.workingdir)
   302             filenames.sort()
   310             filenames.sort()
   303             for filename in filenames:
   311             for filename in filenames:
   305                 if name.upper().startswith("RUNTIME") and ext.upper() == ".PY":
   313                 if name.upper().startswith("RUNTIME") and ext.upper() == ".PY":
   306                     execfile(os.path.join(self.workingdir, filename), self.python_runtime_vars)
   314                     execfile(os.path.join(self.workingdir, filename), self.python_runtime_vars)
   307                     for methodname in MethodNames:
   315                     for methodname in MethodNames:
   308                         method = self.python_runtime_vars.get("_%s_%s" % (name, methodname), None)
   316                         method = self.python_runtime_vars.get("_%s_%s" % (name, methodname), None)
   309                         if method is not None:
   317                         if method is not None:
   310                             self.python_runtime_vars["_runtime_%s"%methodname].append(method)
   318                             self.python_runtime_vars["_runtime_%s" % methodname].append(method)
   311         except:
   319         except Exception:
   312             self.LogMessage(0,traceback.format_exc())
   320             self.LogMessage(0, traceback.format_exc())
   313             raise
   321             raise
   314 
   322 
   315         self.PythonRuntimeCall("init")
   323         self.PythonRuntimeCall("init")
   316 
       
   317 
       
   318 
   324 
   319     def PythonRuntimeCleanup(self):
   325     def PythonRuntimeCleanup(self):
   320         if self.python_runtime_vars is not None:
   326         if self.python_runtime_vars is not None:
   321             self.PythonRuntimeCall("cleanup")
   327             self.PythonRuntimeCall("cleanup")
   322 
   328 
   323         self.python_runtime_vars = None
   329         self.python_runtime_vars = None
   324 
   330 
   325     def PythonThreadProc(self):
   331     def PythonThreadProc(self):
   326         self.StartSem.release()
   332         self.StartSem.release()
   327         res,cmd,blkid = "None","None",ctypes.c_void_p()
   333         res, cmd, blkid = "None", "None", ctypes.c_void_p()
   328         compile_cache={}
   334         compile_cache = {}
   329         while True:
   335         while True:
   330             # print "_PythonIterator(", res, ")",
   336             # print "_PythonIterator(", res, ")",
   331             cmd = self._PythonIterator(res,blkid)
   337             cmd = self._PythonIterator(res, blkid)
   332             FBID = blkid.value
   338             FBID = blkid.value
   333             # print " -> ", cmd, blkid
   339             # print " -> ", cmd, blkid
   334             if cmd is None:
   340             if cmd is None:
   335                 break
   341                 break
   336             try :
   342             try:
   337                 self.python_runtime_vars["FBID"]=FBID
   343                 self.python_runtime_vars["FBID"] = FBID
   338                 ccmd,AST =compile_cache.get(FBID, (None,None))
   344                 ccmd, AST = compile_cache.get(FBID, (None, None))
   339                 if ccmd is None or ccmd!=cmd:
   345                 if ccmd is None or ccmd != cmd:
   340                     AST = compile(cmd, '<plc>', 'eval')
   346                     AST = compile(cmd, '<plc>', 'eval')
   341                     compile_cache[FBID]=(cmd,AST)
   347                     compile_cache[FBID] = (cmd, AST)
   342                 result,exp = self.evaluator(eval,AST,self.python_runtime_vars)
   348                 result, exp = self.evaluator(eval, AST, self.python_runtime_vars)
   343                 if exp is not None:
   349                 if exp is not None:
   344                     res = "#EXCEPTION : "+str(exp[1])
   350                     res = "#EXCEPTION : "+str(exp[1])
   345                     self.LogMessage(1,('PyEval@0x%x(Code="%s") Exception "%s"')%(FBID,cmd,
   351                     self.LogMessage(1, ('PyEval@0x%x(Code="%s") Exception "%s"') % (
   346                         '\n'.join(traceback.format_exception(*exp))))
   352                         FBID, cmd, '\n'.join(traceback.format_exception(*exp))))
   347                 else:
   353                 else:
   348                     res=str(result)
   354                     res = str(result)
   349                 self.python_runtime_vars["FBID"]=None
   355                 self.python_runtime_vars["FBID"] = None
   350             except Exception,e:
   356             except Exception, e:
   351                 res = "#EXCEPTION : "+str(e)
   357                 res = "#EXCEPTION : "+str(e)
   352                 self.LogMessage(1,('PyEval@0x%x(Code="%s") Exception "%s"')%(FBID,cmd,str(e)))
   358                 self.LogMessage(1, ('PyEval@0x%x(Code="%s") Exception "%s"') % (FBID, cmd, str(e)))
   353 
   359 
   354     def StartPLC(self):
   360     def StartPLC(self):
   355         if self.CurrentPLCFilename is not None and self.PLCStatus == "Stopped":
   361         if self.CurrentPLCFilename is not None and self.PLCStatus == "Stopped":
   356             c_argv = ctypes.c_char_p * len(self.argv)
   362             c_argv = ctypes.c_char_p * len(self.argv)
   357             error = None
   363             error = None
   358             res = self._startPLC(len(self.argv),c_argv(*self.argv))
   364             res = self._startPLC(len(self.argv), c_argv(*self.argv))
   359             if res == 0:
   365             if res == 0:
   360                 self.PLCStatus = "Started"
   366                 self.PLCStatus = "Started"
   361                 self.StatusChange()
   367                 self.StatusChange()
   362                 self.PythonRuntimeCall("start")
   368                 self.PythonRuntimeCall("start")
   363                 self.StartSem=Semaphore(0)
   369                 self.StartSem = Semaphore(0)
   364                 self.PythonThread = Thread(target=self.PythonThreadProc)
   370                 self.PythonThread = Thread(target=self.PythonThreadProc)
   365                 self.PythonThread.start()
   371                 self.PythonThread.start()
   366                 self.StartSem.acquire()
   372                 self.StartSem.acquire()
   367                 self.LogMessage("PLC started")
   373                 self.LogMessage("PLC started")
   368             else:
   374             else:
   369                 self.LogMessage(0,_("Problem starting PLC : error %d" % res))
   375                 self.LogMessage(0, _("Problem starting PLC : error %d" % res))
   370                 self.PLCStatus = "Broken"
   376                 self.PLCStatus = "Broken"
   371                 self.StatusChange()
   377                 self.StatusChange()
   372 
   378 
   373     def StopPLC(self):
   379     def StopPLC(self):
   374         if self.PLCStatus == "Started":
   380         if self.PLCStatus == "Started":
   376             self._stopPLC()
   382             self._stopPLC()
   377             self.PythonThread.join()
   383             self.PythonThread.join()
   378             self.PLCStatus = "Stopped"
   384             self.PLCStatus = "Stopped"
   379             self.StatusChange()
   385             self.StatusChange()
   380             self.PythonRuntimeCall("stop")
   386             self.PythonRuntimeCall("stop")
   381             if self.TraceThread is not None :
   387             if self.TraceThread is not None:
   382                 self.TraceWakeup.set()
   388                 self.TraceWakeup.set()
   383                 self.TraceThread.join()
   389                 self.TraceThread.join()
   384                 self.TraceThread = None
   390                 self.TraceThread = None
   385             return True
   391             return True
   386         return False
   392         return False
   387 
   393 
   388     def _Reload(self):
   394     def _Reload(self):
   389         self.daemon.shutdown(True)
   395         self.daemon.shutdown(True)
   390         self.daemon.sock.close()
   396         self.daemon.sock.close()
   391         os.execv(sys.executable,[sys.executable]+sys.argv[:])
   397         os.execv(sys.executable, [sys.executable]+sys.argv[:])
   392         # never reached
   398         # never reached
   393         return 0
   399         return 0
   394 
   400 
   395     def ForceReload(self):
   401     def ForceReload(self):
   396         # respawn python interpreter
   402         # respawn python interpreter
   397         Timer(0.1,self._Reload).start()
   403         Timer(0.1, self._Reload).start()
   398         return True
   404         return True
   399 
   405 
   400     def GetPLCstatus(self):
   406     def GetPLCstatus(self):
   401         return self.PLCStatus, map(self.GetLogCount,xrange(LogLevelsCount))
   407         return self.PLCStatus, map(self.GetLogCount, xrange(LogLevelsCount))
   402 
   408 
   403     def NewPLC(self, md5sum, data, extrafiles):
   409     def NewPLC(self, md5sum, data, extrafiles):
   404         if self.PLCStatus in ["Stopped", "Empty", "Broken"]:
   410         if self.PLCStatus in ["Stopped", "Empty", "Broken"]:
   405             NewFileName = md5sum + lib_ext
   411             NewFileName = md5sum + lib_ext
   406             extra_files_log = os.path.join(self.workingdir,"extra_files.txt")
   412             extra_files_log = os.path.join(self.workingdir, "extra_files.txt")
   407 
   413 
   408             self.UnLoadPLC()
   414             self.UnLoadPLC()
   409 
   415 
   410             self.LogMessage("NewPLC (%s)"%md5sum)
   416             self.LogMessage("NewPLC (%s)" % md5sum)
   411             self.PLCStatus = "Empty"
   417             self.PLCStatus = "Empty"
   412 
   418 
   413             try:
   419             try:
   414                 os.remove(os.path.join(self.workingdir,
   420                 os.remove(os.path.join(self.workingdir,
   415                                        self.CurrentPLCFilename))
   421                                        self.CurrentPLCFilename))
   416                 for filename in file(extra_files_log, "r").readlines() + [extra_files_log]:
   422                 for filename in file(extra_files_log, "r").readlines() + [extra_files_log]:
   417                     try:
   423                     try:
   418                         os.remove(os.path.join(self.workingdir, filename.strip()))
   424                         os.remove(os.path.join(self.workingdir, filename.strip()))
   419                     except:
   425                     except Exception:
   420                         pass
   426                         pass
   421             except:
   427             except Exception:
   422                 pass
   428                 pass
   423 
   429 
   424             try:
   430             try:
   425                 # Create new PLC file
   431                 # Create new PLC file
   426                 open(os.path.join(self.workingdir,NewFileName),
   432                 open(os.path.join(self.workingdir, NewFileName),
   427                      'wb').write(data)
   433                      'wb').write(data)
   428 
   434 
   429                 # Store new PLC filename based on md5 key
   435                 # Store new PLC filename based on md5 key
   430                 open(self._GetMD5FileName(), "w").write(md5sum)
   436                 open(self._GetMD5FileName(), "w").write(md5sum)
   431 
   437 
   432                 # Then write the files
   438                 # Then write the files
   433                 log = file(extra_files_log, "w")
   439                 log = file(extra_files_log, "w")
   434                 for fname,fdata in extrafiles:
   440                 for fname, fdata in extrafiles:
   435                     fpath = os.path.join(self.workingdir,fname)
   441                     fpath = os.path.join(self.workingdir, fname)
   436                     open(fpath, "wb").write(fdata)
   442                     open(fpath, "wb").write(fdata)
   437                     log.write(fname+'\n')
   443                     log.write(fname+'\n')
   438 
   444 
   439                 # Store new PLC filename
   445                 # Store new PLC filename
   440                 self.CurrentPLCFilename = NewFileName
   446                 self.CurrentPLCFilename = NewFileName
   441             except:
   447             except Exception:
   442                 self.PLCStatus = "Broken"
   448                 self.PLCStatus = "Broken"
   443                 self.StatusChange()
   449                 self.StatusChange()
   444                 PLCprint(traceback.format_exc())
   450                 PLCprint(traceback.format_exc())
   445                 return False
   451                 return False
   446 
   452 
   456 
   462 
   457     def MatchMD5(self, MD5):
   463     def MatchMD5(self, MD5):
   458         try:
   464         try:
   459             last_md5 = open(self._GetMD5FileName(), "r").read()
   465             last_md5 = open(self._GetMD5FileName(), "r").read()
   460             return last_md5 == MD5
   466             return last_md5 == MD5
   461         except:
   467         except Exception:
   462             pass
   468             pass
   463         return False
   469         return False
   464 
   470 
   465     def SetTraceVariablesList(self, idxs):
   471     def SetTraceVariablesList(self, idxs):
   466         """
   472         """
   470         if idxs:
   476         if idxs:
   471             # suspend but dont disable
   477             # suspend but dont disable
   472             if self._suspendDebug(False) == 0:
   478             if self._suspendDebug(False) == 0:
   473                 # keep a copy of requested idx
   479                 # keep a copy of requested idx
   474                 self._ResetDebugVariables()
   480                 self._ResetDebugVariables()
   475                 for idx,iectype,force in idxs:
   481                 for idx, iectype, force in idxs:
   476                     if force !=None:
   482                     if force is not None:
   477                         c_type,unpack_func, pack_func = \
   483                         c_type, unpack_func, pack_func = \
   478                             TypeTranslator.get(iectype,
   484                             TypeTranslator.get(iectype,
   479                                                     (None,None,None))
   485                                                (None, None, None))
   480                         force = ctypes.byref(pack_func(c_type,force))
   486                         force = ctypes.byref(pack_func(c_type, force))
   481                     self._RegisterDebugVariable(idx, force)
   487                     self._RegisterDebugVariable(idx, force)
   482                 self._TracesSwap()
   488                 self._TracesSwap()
   483                 self._resumeDebug()
   489                 self._resumeDebug()
   484         else:
   490         else:
   485             self._suspendDebug(True)
   491             self._suspendDebug(True)
   486 
   492 
   487     def _TracesPush(self, trace):
   493     def _TracesPush(self, trace):
   488         self.TraceLock.acquire()
   494         self.TraceLock.acquire()
   489         lT = len(self.Traces)
   495         lT = len(self.Traces)
   490         if lT != 0 and lT * len(self.Traces[0]) > 1024 * 1024 :
   496         if lT != 0 and lT * len(self.Traces[0]) > 1024 * 1024:
   491             self.Traces.pop(0)
   497             self.Traces.pop(0)
   492         self.Traces.append(trace)
   498         self.Traces.append(trace)
   493         self.TraceLock.release()
   499         self.TraceLock.release()
   494 
   500 
   495     def _TracesSwap(self):
   501     def _TracesSwap(self):
   509         traces_age = time() - self.LastSwapTrace
   515         traces_age = time() - self.LastSwapTrace
   510         if traces_age > 3:
   516         if traces_age > 3:
   511             self.TraceLock.acquire()
   517             self.TraceLock.acquire()
   512             self.Traces = []
   518             self.Traces = []
   513             self.TraceLock.release()
   519             self.TraceLock.release()
   514             self._suspendDebug(True) # Disable debugger
   520             self._suspendDebug(True)  # Disable debugger
   515             self.TraceWakeup.clear()
   521             self.TraceWakeup.clear()
   516             self.TraceWakeup.wait()
   522             self.TraceWakeup.wait()
   517             self._resumeDebug() # Re-enable debugger
   523             self._resumeDebug()  # Re-enable debugger
   518 
   524 
   519     def _TracesFlush(self):
   525     def _TracesFlush(self):
   520         self.TraceLock.acquire()
   526         self.TraceLock.acquire()
   521         self.Traces = []
   527         self.Traces = []
   522         self.TraceLock.release()
   528         self.TraceLock.release()
   526 
   532 
   527     def TraceThreadProc(self):
   533     def TraceThreadProc(self):
   528         """
   534         """
   529         Return a list of traces, corresponding to the list of required idx
   535         Return a list of traces, corresponding to the list of required idx
   530         """
   536         """
   531         while self.PLCStatus == "Started" :
   537         while self.PLCStatus == "Started":
   532             tick = ctypes.c_uint32()
   538             tick = ctypes.c_uint32()
   533             size = ctypes.c_uint32()
   539             size = ctypes.c_uint32()
   534             buff = ctypes.c_void_p()
   540             buff = ctypes.c_void_p()
   535             TraceBuffer = None
   541             TraceBuffer = None
   536             if self.PLClibraryLock.acquire(False):
   542             if self.PLClibraryLock.acquire(False):
   544             if TraceBuffer is not None:
   550             if TraceBuffer is not None:
   545                 self._TracesPush((tick.value, TraceBuffer))
   551                 self._TracesPush((tick.value, TraceBuffer))
   546             self._TracesAutoSuspend()
   552             self._TracesAutoSuspend()
   547         self._TracesFlush()
   553         self._TracesFlush()
   548 
   554 
   549 
       
   550     def RemoteExec(self, script, *kwargs):
   555     def RemoteExec(self, script, *kwargs):
   551         try:
   556         try:
   552             exec script in kwargs
   557             exec script in kwargs
   553         except:
   558         except Exception:
   554             e_type, e_value, e_traceback = sys.exc_info()
   559             e_type, e_value, e_traceback = sys.exc_info()
   555             line_no = traceback.tb_lineno(get_last_traceback(e_traceback))
   560             line_no = traceback.tb_lineno(get_last_traceback(e_traceback))
   556             return (-1, "RemoteExec script failed!\n\nLine %d: %s\n\t%s" %
   561             return (-1, "RemoteExec script failed!\n\nLine %d: %s\n\t%s" %
   557                         (line_no, e_value, script.splitlines()[line_no - 1]))
   562                         (line_no, e_value, script.splitlines()[line_no - 1]))
   558         return (0, kwargs.get("returnVal", None))
   563         return (0, kwargs.get("returnVal", None))
   559 
       
   560