runtime/PLCObject.py
changeset 1984 081265cda5b1
parent 1983 edd7a3a06d86
child 1987 8d1aca3c9e83
equal deleted inserted replaced
1983:edd7a3a06d86 1984:081265cda5b1
    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 
    24 
    25 from __future__ import absolute_import
    25 from __future__ import absolute_import
    26 from threading import Timer, Thread, Lock, Semaphore, Event
    26 import thread
       
    27 from threading import Timer, Thread, Lock, Semaphore, Event, Condition
    27 import ctypes
    28 import ctypes
    28 import os
    29 import os
    29 import sys
    30 import sys
    30 import traceback
    31 import traceback
    31 from time import time
    32 from time import time
    58 def PLCprint(message):
    59 def PLCprint(message):
    59     sys.stdout.write("PLCobject : "+message+"\n")
    60     sys.stdout.write("PLCobject : "+message+"\n")
    60     sys.stdout.flush()
    61     sys.stdout.flush()
    61 
    62 
    62 
    63 
       
    64 class job(object):
       
    65     """
       
    66     job to be executed by a worker
       
    67     """
       
    68     def __init__(self,call,*args,**kwargs):
       
    69         self.job = (call,args,kwargs)
       
    70         self.result = None
       
    71         self.success = False
       
    72         self.exc_info = None
       
    73 
       
    74     def do(self):
       
    75         """
       
    76         do the job by executing the call, and deal with exceptions 
       
    77         """
       
    78         try :
       
    79             call, args, kwargs = self.job
       
    80             self.result = call(*args,**kwargs)
       
    81             self.success = True
       
    82         except Exception:
       
    83             self.success = False
       
    84             self.exc_info = sys.exc_info()
       
    85 
       
    86 
       
    87 class worker(object):
       
    88     """
       
    89     serialize main thread load/unload of PLC shared objects
       
    90     """
       
    91     def __init__(self):
       
    92         # Only one job at a time
       
    93         self._finish = False
       
    94         self._threadID = None
       
    95         self.mutex = Lock()
       
    96         self.todo = Condition(self.mutex)
       
    97         self.done = Condition(self.mutex)
       
    98         self.job = None
       
    99 
       
   100     def runloop(self):
       
   101         """
       
   102         meant to be called by worker thread (blocking)
       
   103         """
       
   104         self._threadID = thread.get_ident()
       
   105         self.mutex.acquire()
       
   106         while not self._finish:
       
   107             self.todo.wait()
       
   108             if self.job is not None:
       
   109                 self.job.do()
       
   110             self.done.notify_all()
       
   111         self.mutex.release()
       
   112     
       
   113     def call(self, *args, **kwargs):
       
   114         print("call", args, kwargs)
       
   115         """
       
   116         creates a job, execute it in worker thread, and deliver result.
       
   117         if job execution raise exception, re-raise same exception 
       
   118         meant to be called by non-worker threads, but this is accepted.
       
   119         blocking until job done
       
   120         """
       
   121 
       
   122         _job = job(*args,**kwargs)
       
   123 
       
   124         if self._threadID == thread.get_ident():
       
   125             # if caller is worker thread execute immediately
       
   126             _job.do()
       
   127         else:
       
   128             # otherwise notify and wait for completion
       
   129             self.mutex.acquire()
       
   130 
       
   131             while self.job is not None:
       
   132                 self.done.wait()
       
   133 
       
   134             self.job = _job
       
   135             self.todo.notify()
       
   136             self.done.wait()
       
   137             _job = self.job
       
   138             self.job = None
       
   139             self.mutex.release()
       
   140 
       
   141         if _job.success:
       
   142             return _job.result
       
   143         else:
       
   144             raise _job.exc_info[0], _job.exc_info[1], _job.exc_info[2]
       
   145         
       
   146     def quit(self):
       
   147         """
       
   148         unblocks main thread, and terminate execution of runloop()
       
   149         """
       
   150         # mark queue
       
   151         self._finish = True
       
   152         self.mutex.acquire()
       
   153         self.job = None
       
   154         self.todo.notify()
       
   155         self.mutex.release()
       
   156 
       
   157 
       
   158 MainWorker = worker()
       
   159 
       
   160 
       
   161 def RunInMain(func):
       
   162     def func_wrapper(*args,**kwargs):
       
   163         return MainWorker.call(func, *args, **kwargs)
       
   164     return func_wrapper
       
   165     
       
   166 
    63 class PLCObject(pyro.ObjBase):
   167 class PLCObject(pyro.ObjBase):
    64     def __init__(self, workingdir, daemon, argv, statuschange, evaluator, pyruntimevars):
   168     def __init__(self, server):
       
   169 
    65         pyro.ObjBase.__init__(self)
   170         pyro.ObjBase.__init__(self)
    66         self.evaluator = evaluator
   171         self.evaluator = server.evaluator
    67         self.argv = [workingdir] + argv  # force argv[0] to be "path" to exec...
   172         self.argv = [server.workdir] + server.argv  # force argv[0] to be "path" to exec...
    68         self.workingdir = workingdir
   173         self.workingdir = server.workdir
    69         self.PLCStatus = "Empty"
   174         self.PLCStatus = "Empty"
    70         self.PLClibraryHandle = None
   175         self.PLClibraryHandle = None
    71         self.PLClibraryLock = Lock()
   176         self.PLClibraryLock = Lock()
    72         self.DummyIteratorLock = None
   177         self.DummyIteratorLock = None
    73         # Creates fake C funcs proxies
   178         # Creates fake C funcs proxies
    74         self._FreePLC()
   179         self._InitPLCStubCalls()
    75         self.daemon = daemon
   180         self.daemon = server.daemon
    76         self.statuschange = statuschange
   181         self.statuschange = server.statuschange
    77         self.hmi_frame = None
   182         self.hmi_frame = None
    78         self.pyruntimevars = pyruntimevars
   183         self.pyruntimevars = server.pyruntimevars
    79         self._loading_error = None
   184         self._loading_error = None
    80         self.python_runtime_vars = None
   185         self.python_runtime_vars = None
    81         self.TraceThread = None
   186         self.TraceThread = None
    82         self.TraceLock = Lock()
   187         self.TraceLock = Lock()
    83         self.TraceWakeup = Event()
   188         self.TraceWakeup = Event()
    84         self.Traces = []
   189         self.Traces = []
       
   190         server.RegisterPLCObject(self)
    85 
   191 
    86     def AutoLoad(self):
   192     def AutoLoad(self):
    87         # Get the last transfered PLC if connector must be restart
   193         # Get the last transfered PLC if connector must be restart
    88         try:
   194         try:
    89             self.CurrentPLCFilename = open(
   195             self.CurrentPLCFilename = open(
   143         return os.path.join(self.workingdir, "lasttransferedPLC.md5")
   249         return os.path.join(self.workingdir, "lasttransferedPLC.md5")
   144 
   250 
   145     def _GetLibFileName(self):
   251     def _GetLibFileName(self):
   146         return os.path.join(self.workingdir, self.CurrentPLCFilename)
   252         return os.path.join(self.workingdir, self.CurrentPLCFilename)
   147 
   253 
       
   254     @RunInMain
   148     def LoadPLC(self):
   255     def LoadPLC(self):
   149         """
   256         """
   150         Load PLC library
   257         Load PLC library
   151         Declare all functions, arguments and return values
   258         Declare all functions, arguments and return values
   152         """
   259         """
   231 
   338 
   232             return True
   339             return True
   233         except Exception:
   340         except Exception:
   234             self._loading_error = traceback.format_exc()
   341             self._loading_error = traceback.format_exc()
   235             PLCprint(self._loading_error)
   342             PLCprint(self._loading_error)
       
   343             self._FreePLC()
   236             return False
   344             return False
   237 
   345 
       
   346     @RunInMain
   238     def UnLoadPLC(self):
   347     def UnLoadPLC(self):
   239         self.PythonRuntimeCleanup()
   348         self.PythonRuntimeCleanup()
   240         self._FreePLC()
   349         self._FreePLC()
   241 
   350 
   242     def _FreePLC(self):
   351     def _InitPLCStubCalls(self):
   243         """
   352         """
   244         Unload PLC library.
   353         create dummy C func proxies
   245         This is also called by __init__ to create dummy C func proxies
   354         """
   246         """
       
   247         self.PLClibraryLock.acquire()
       
   248         # Forget all refs to library
       
   249         self._startPLC = lambda x, y: None
   355         self._startPLC = lambda x, y: None
   250         self._stopPLC = lambda: None
   356         self._stopPLC = lambda: None
   251         self._ResetDebugVariables = lambda: None
   357         self._ResetDebugVariables = lambda: None
   252         self._RegisterDebugVariable = lambda x, y: None
   358         self._RegisterDebugVariable = lambda x, y: None
   253         self._IterDebugData = lambda x, y: None
   359         self._IterDebugData = lambda x, y: None
   257         self._resumeDebug = lambda: None
   363         self._resumeDebug = lambda: None
   258         self._PythonIterator = lambda: ""
   364         self._PythonIterator = lambda: ""
   259         self._GetLogCount = None
   365         self._GetLogCount = None
   260         self._LogMessage = None
   366         self._LogMessage = None
   261         self._GetLogMessage = None
   367         self._GetLogMessage = None
       
   368         self._PLClibraryHandle = None
   262         self.PLClibraryHandle = None
   369         self.PLClibraryHandle = None
       
   370 
       
   371     def _FreePLC(self):
       
   372         """
       
   373         Unload PLC library.
       
   374         This is also called by __init__ to create dummy C func proxies
       
   375         """
       
   376         self.PLClibraryLock.acquire()
       
   377 
   263         # Unload library explicitely
   378         # Unload library explicitely
   264         if getattr(self, "_PLClibraryHandle", None) is not None:
   379         if getattr(self, "_PLClibraryHandle", None) is not None:
   265             dlclose(self._PLClibraryHandle)
   380             dlclose(self._PLClibraryHandle)
   266             self._PLClibraryHandle = None
   381 
       
   382         # Forget all refs to library
       
   383         self._InitPLCStubCalls()
   267 
   384 
   268         self.PLClibraryLock.release()
   385         self.PLClibraryLock.release()
   269         return False
   386         return False
   270 
   387 
   271     def PythonRuntimeCall(self, methodname):
   388     def PythonRuntimeCall(self, methodname):
   446 
   563 
   447             if self.LoadPLC():
   564             if self.LoadPLC():
   448                 self.PLCStatus = "Stopped"
   565                 self.PLCStatus = "Stopped"
   449             else:
   566             else:
   450                 self.PLCStatus = "Broken"
   567                 self.PLCStatus = "Broken"
   451                 self._FreePLC()
       
   452             self.StatusChange()
   568             self.StatusChange()
   453 
   569 
   454             return self.PLCStatus == "Stopped"
   570             return self.PLCStatus == "Stopped"
   455         return False
   571         return False
   456 
   572