runtime/PLCObject.py
changeset 1997 d9e8fb47340f
parent 1994 1fdc32be71b8
child 2270 d9175daf6522
child 2416 1ca207782dde
equal deleted inserted replaced
1996:4ae9c4447947 1997:d9e8fb47340f
    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 
    24 
    25 from __future__ import absolute_import
    25 from __future__ import absolute_import
    26 import thread
    26 import thread
    27 from threading import Timer, Thread, Lock, Semaphore, Event, Condition
    27 from threading import Thread, Lock, Semaphore, Event, Condition
    28 import ctypes
    28 import ctypes
    29 import os
    29 import os
    30 import sys
    30 import sys
    31 import traceback
    31 import traceback
    32 from time import time
    32 from time import time
    63 
    63 
    64 class job(object):
    64 class job(object):
    65     """
    65     """
    66     job to be executed by a worker
    66     job to be executed by a worker
    67     """
    67     """
    68     def __init__(self,call,*args,**kwargs):
    68     def __init__(self, call, *args, **kwargs):
    69         self.job = (call,args,kwargs)
    69         self.job = (call, args, kwargs)
    70         self.result = None
    70         self.result = None
    71         self.success = False
    71         self.success = False
    72         self.exc_info = None
    72         self.exc_info = None
    73 
    73 
    74     def do(self):
    74     def do(self):
    75         """
    75         """
    76         do the job by executing the call, and deal with exceptions 
    76         do the job by executing the call, and deal with exceptions
    77         """
    77         """
    78         try :
    78         try:
    79             call, args, kwargs = self.job
    79             call, args, kwargs = self.job
    80             self.result = call(*args,**kwargs)
    80             self.result = call(*args, **kwargs)
    81             self.success = True
    81             self.success = True
    82         except Exception:
    82         except Exception:
    83             self.success = False
    83             self.success = False
    84             self.exc_info = sys.exc_info()
    84             self.exc_info = sys.exc_info()
    85 
    85 
    96         self.todo = Condition(self.mutex)
    96         self.todo = Condition(self.mutex)
    97         self.done = Condition(self.mutex)
    97         self.done = Condition(self.mutex)
    98         self.free = Condition(self.mutex)
    98         self.free = Condition(self.mutex)
    99         self.job = None
    99         self.job = None
   100 
   100 
   101     def runloop(self,*args,**kwargs):
   101     def runloop(self, *args, **kwargs):
   102         """
   102         """
   103         meant to be called by worker thread (blocking)
   103         meant to be called by worker thread (blocking)
   104         """
   104         """
   105         self._threadID = thread.get_ident()
   105         self._threadID = thread.get_ident()
   106         if args or kwargs:
   106         if args or kwargs:
   107             job(*args,**kwargs).do()
   107             job(*args, **kwargs).do()
   108             # result is ignored
   108             # result is ignored
   109         self.mutex.acquire()
   109         self.mutex.acquire()
   110         while not self._finish:
   110         while not self._finish:
   111             self.todo.wait()
   111             self.todo.wait()
   112             if self.job is not None:
   112             if self.job is not None:
   113                 self.job.do()
   113                 self.job.do()
   114                 self.done.notify()
   114                 self.done.notify()
   115             else:
   115             else:
   116                 self.free.notify()
   116                 self.free.notify()
   117         self.mutex.release()
   117         self.mutex.release()
   118     
   118 
   119     def call(self, *args, **kwargs):
   119     def call(self, *args, **kwargs):
   120         """
   120         """
   121         creates a job, execute it in worker thread, and deliver result.
   121         creates a job, execute it in worker thread, and deliver result.
   122         if job execution raise exception, re-raise same exception 
   122         if job execution raise exception, re-raise same exception
   123         meant to be called by non-worker threads, but this is accepted.
   123         meant to be called by non-worker threads, but this is accepted.
   124         blocking until job done
   124         blocking until job done
   125         """
   125         """
   126 
   126 
   127         _job = job(*args,**kwargs)
   127         _job = job(*args, **kwargs)
   128 
   128 
   129         if self._threadID == thread.get_ident() or self._threadID is None:
   129         if self._threadID == thread.get_ident() or self._threadID is None:
   130             # if caller is worker thread execute immediately
   130             # if caller is worker thread execute immediately
   131             _job.do()
   131             _job.do()
   132         else:
   132         else:
   145 
   145 
   146         if _job.success:
   146         if _job.success:
   147             return _job.result
   147             return _job.result
   148         else:
   148         else:
   149             raise _job.exc_info[0], _job.exc_info[1], _job.exc_info[2]
   149             raise _job.exc_info[0], _job.exc_info[1], _job.exc_info[2]
   150         
   150 
   151     def quit(self):
   151     def quit(self):
   152         """
   152         """
   153         unblocks main thread, and terminate execution of runloop()
   153         unblocks main thread, and terminate execution of runloop()
   154         """
   154         """
   155         # mark queue
   155         # mark queue
   162 
   162 
   163 MainWorker = worker()
   163 MainWorker = worker()
   164 
   164 
   165 
   165 
   166 def RunInMain(func):
   166 def RunInMain(func):
   167     def func_wrapper(*args,**kwargs):
   167     def func_wrapper(*args, **kwargs):
   168         return MainWorker.call(func, *args, **kwargs)
   168         return MainWorker.call(func, *args, **kwargs)
   169     return func_wrapper
   169     return func_wrapper
   170     
   170 
   171 
   171 
   172 class PLCObject(pyro.ObjBase):
   172 class PLCObject(pyro.ObjBase):
   173     def __init__(self, server):
   173     def __init__(self, server):
   174         pyro.ObjBase.__init__(self)
   174         pyro.ObjBase.__init__(self)
   175         self.evaluator = server.evaluator
   175         self.evaluator = server.evaluator
   191         self.TraceLock = Lock()
   191         self.TraceLock = Lock()
   192         self.Traces = []
   192         self.Traces = []
   193 
   193 
   194     # First task of worker -> no @RunInMain
   194     # First task of worker -> no @RunInMain
   195     def AutoLoad(self):
   195     def AutoLoad(self):
   196         # Get the last transfered PLC 
   196         # Get the last transfered PLC
   197         try:
   197         try:
   198             self.CurrentPLCFilename = open(
   198             self.CurrentPLCFilename = open(
   199                 self._GetMD5FileName(),
   199                 self._GetMD5FileName(),
   200                 "r").read().strip() + lib_ext
   200                 "r").read().strip() + lib_ext
   201             if self.LoadPLC():
   201             if self.LoadPLC():
   224     @RunInMain
   224     @RunInMain
   225     def ResetLogCount(self):
   225     def ResetLogCount(self):
   226         if self._ResetLogCount is not None:
   226         if self._ResetLogCount is not None:
   227             self._ResetLogCount()
   227             self._ResetLogCount()
   228 
   228 
   229     # used internaly 
   229     # used internaly
   230     def GetLogCount(self, level):
   230     def GetLogCount(self, level):
   231         if self._GetLogCount is not None:
   231         if self._GetLogCount is not None:
   232             return int(self._GetLogCount(level))
   232             return int(self._GetLogCount(level))
   233         elif self._loading_error is not None and level == 0:
   233         elif self._loading_error is not None and level == 0:
   234             return 1
   234             return 1
   412         for method in self.python_runtime_vars.get("_runtime_%s" % methodname, []):
   412         for method in self.python_runtime_vars.get("_runtime_%s" % methodname, []):
   413             _res, exp = self.evaluator(method)
   413             _res, exp = self.evaluator(method)
   414             if exp is not None:
   414             if exp is not None:
   415                 self.LogMessage(0, '\n'.join(traceback.format_exception(*exp)))
   415                 self.LogMessage(0, '\n'.join(traceback.format_exception(*exp)))
   416 
   416 
   417     # used internaly 
   417     # used internaly
   418     def PythonRuntimeInit(self):
   418     def PythonRuntimeInit(self):
   419         MethodNames = ["init", "start", "stop", "cleanup"]
   419         MethodNames = ["init", "start", "stop", "cleanup"]
   420         self.python_runtime_vars = globals().copy()
   420         self.python_runtime_vars = globals().copy()
   421         self.python_runtime_vars.update(self.pyruntimevars)
   421         self.python_runtime_vars.update(self.pyruntimevars)
   422         parent = self
   422         parent = self
   464             self.LogMessage(0, traceback.format_exc())
   464             self.LogMessage(0, traceback.format_exc())
   465             raise
   465             raise
   466 
   466 
   467         self.PythonRuntimeCall("init")
   467         self.PythonRuntimeCall("init")
   468 
   468 
   469     # used internaly 
   469     # used internaly
   470     def PythonRuntimeCleanup(self):
   470     def PythonRuntimeCleanup(self):
   471         if self.python_runtime_vars is not None:
   471         if self.python_runtime_vars is not None:
   472             self.PythonRuntimeCall("cleanup")
   472             self.PythonRuntimeCall("cleanup")
   473 
   473 
   474         self.python_runtime_vars = None
   474         self.python_runtime_vars = None
   542     def NewPLC(self, md5sum, data, extrafiles):
   542     def NewPLC(self, md5sum, data, extrafiles):
   543         if self.PLCStatus in ["Stopped", "Empty", "Broken"]:
   543         if self.PLCStatus in ["Stopped", "Empty", "Broken"]:
   544             NewFileName = md5sum + lib_ext
   544             NewFileName = md5sum + lib_ext
   545             extra_files_log = os.path.join(self.workingdir, "extra_files.txt")
   545             extra_files_log = os.path.join(self.workingdir, "extra_files.txt")
   546 
   546 
   547             old_PLC_filename = os.path.join(self.workingdir, \
   547             old_PLC_filename = os.path.join(self.workingdir, self.CurrentPLCFilename) \
   548                                             self.CurrentPLCFilename) \
   548                 if self.CurrentPLCFilename is not None \
   549                                if self.CurrentPLCFilename is not None \
   549                 else None
   550                                else None
       
   551             new_PLC_filename = os.path.join(self.workingdir, NewFileName)
   550             new_PLC_filename = os.path.join(self.workingdir, NewFileName)
   552 
   551 
   553             # Some platform (i.e. Xenomai) don't like reloading same .so file
   552             # Some platform (i.e. Xenomai) don't like reloading same .so file
   554             replace_PLC_shared_object = new_PLC_filename != old_PLC_filename
   553             replace_PLC_shared_object = new_PLC_filename != old_PLC_filename
   555 
   554 
   556             if replace_PLC_shared_object:
   555             if replace_PLC_shared_object:
   557                 self.UnLoadPLC()
   556                 self.UnLoadPLC()
   558 
   557 
   559             self.LogMessage("NewPLC (%s)" % md5sum)
   558             self.LogMessage("NewPLC (%s)" % md5sum)
   560             self.PLCStatus = "Empty"
   559             self.PLCStatus = "Empty"
   561 
       
   562 
   560 
   563             try:
   561             try:
   564                 if replace_PLC_shared_object:
   562                 if replace_PLC_shared_object:
   565                     os.remove(old_PLC_filename)
   563                     os.remove(old_PLC_filename)
   566                 for filename in file(extra_files_log, "r").readlines() + [extra_files_log]:
   564                 for filename in file(extra_files_log, "r").readlines() + [extra_files_log]:
   633                 self._TracesSwap()
   631                 self._TracesSwap()
   634                 self._resumeDebug()
   632                 self._resumeDebug()
   635         else:
   633         else:
   636             self._suspendDebug(True)
   634             self._suspendDebug(True)
   637 
   635 
   638 
       
   639     def _TracesSwap(self):
   636     def _TracesSwap(self):
   640         self.LastSwapTrace = time()
   637         self.LastSwapTrace = time()
   641         if self.TraceThread is None and self.PLCStatus == "Started":
   638         if self.TraceThread is None and self.PLCStatus == "Started":
   642             self.TraceThread = Thread(target=self.TraceThreadProc)
   639             self.TraceThread = Thread(target=self.TraceThreadProc)
   643             self.TraceThread.start()
   640             self.TraceThread.start()
   664 
   661 
   665             self.PLClibraryLock.acquire()
   662             self.PLClibraryLock.acquire()
   666 
   663 
   667             res = self._GetDebugData(ctypes.byref(tick),
   664             res = self._GetDebugData(ctypes.byref(tick),
   668                                      ctypes.byref(size),
   665                                      ctypes.byref(size),
   669                                      ctypes.byref(buff)) 
   666                                      ctypes.byref(buff))
   670             if res == 0:
   667             if res == 0:
   671                 if size.value:
   668                 if size.value:
   672                     TraceBuffer = ctypes.string_at(buff.value, size.value)
   669                     TraceBuffer = ctypes.string_at(buff.value, size.value)
   673                 self._FreeDebugData()
   670                 self._FreeDebugData()
   674 
   671 
   675             self.PLClibraryLock.release()
   672             self.PLClibraryLock.release()
   676                 
   673 
   677             # leave thread if GetDebugData isn't happy.
   674             # leave thread if GetDebugData isn't happy.
   678             if res != 0:
   675             if res != 0:
   679                 break
   676                 break
   680 
   677 
   681             if TraceBuffer is not None:
   678             if TraceBuffer is not None: