runtime/PLCObject.py
changeset 2270 d9175daf6522
parent 1997 d9e8fb47340f
child 2309 d8fb90a2e11f
equal deleted inserted replaced
2269:2e38b5ec4753 2270:d9175daf6522
    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 import thread
       
    27 from threading import Thread, Lock, Semaphore, Event, Condition
    26 from threading import Thread, Lock, Semaphore, Event, Condition
    28 import ctypes
    27 import ctypes
    29 import os
    28 import os
    30 import sys
    29 import sys
    31 import traceback
    30 import traceback
    32 from time import time
    31 from time import time
    33 import _ctypes  # pylint: disable=wrong-import-order
    32 import _ctypes  # pylint: disable=wrong-import-order
    34 import Pyro.core as pyro
       
    35 
    33 
    36 from runtime.typemapping import TypeTranslator
    34 from runtime.typemapping import TypeTranslator
    37 from runtime.loglevels import LogLevelsDefault, LogLevelsCount
    35 from runtime.loglevels import LogLevelsDefault, LogLevelsCount
       
    36 from runtime import MainWorker
    38 
    37 
    39 if os.name in ("nt", "ce"):
    38 if os.name in ("nt", "ce"):
    40     dlopen = _ctypes.LoadLibrary
    39     dlopen = _ctypes.LoadLibrary
    41     dlclose = _ctypes.FreeLibrary
    40     dlclose = _ctypes.FreeLibrary
    42 elif os.name == "posix":
    41 elif os.name == "posix":
    59 def PLCprint(message):
    58 def PLCprint(message):
    60     sys.stdout.write("PLCobject : "+message+"\n")
    59     sys.stdout.write("PLCobject : "+message+"\n")
    61     sys.stdout.flush()
    60     sys.stdout.flush()
    62 
    61 
    63 
    62 
    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.free = Condition(self.mutex)
       
    99         self.job = None
       
   100 
       
   101     def runloop(self, *args, **kwargs):
       
   102         """
       
   103         meant to be called by worker thread (blocking)
       
   104         """
       
   105         self._threadID = thread.get_ident()
       
   106         if args or kwargs:
       
   107             job(*args, **kwargs).do()
       
   108             # result is ignored
       
   109         self.mutex.acquire()
       
   110         while not self._finish:
       
   111             self.todo.wait()
       
   112             if self.job is not None:
       
   113                 self.job.do()
       
   114                 self.done.notify()
       
   115             else:
       
   116                 self.free.notify()
       
   117         self.mutex.release()
       
   118 
       
   119     def call(self, *args, **kwargs):
       
   120         """
       
   121         creates a job, execute it in worker thread, and deliver result.
       
   122         if job execution raise exception, re-raise same exception
       
   123         meant to be called by non-worker threads, but this is accepted.
       
   124         blocking until job done
       
   125         """
       
   126 
       
   127         _job = job(*args, **kwargs)
       
   128 
       
   129         if self._threadID == thread.get_ident() or self._threadID is None:
       
   130             # if caller is worker thread execute immediately
       
   131             _job.do()
       
   132         else:
       
   133             # otherwise notify and wait for completion
       
   134             self.mutex.acquire()
       
   135 
       
   136             while self.job is not None:
       
   137                 self.free.wait()
       
   138 
       
   139             self.job = _job
       
   140             self.todo.notify()
       
   141             self.done.wait()
       
   142             _job = self.job
       
   143             self.job = None
       
   144             self.mutex.release()
       
   145 
       
   146         if _job.success:
       
   147             return _job.result
       
   148         else:
       
   149             raise _job.exc_info[0], _job.exc_info[1], _job.exc_info[2]
       
   150 
       
   151     def quit(self):
       
   152         """
       
   153         unblocks main thread, and terminate execution of runloop()
       
   154         """
       
   155         # mark queue
       
   156         self._finish = True
       
   157         self.mutex.acquire()
       
   158         self.job = None
       
   159         self.todo.notify()
       
   160         self.mutex.release()
       
   161 
       
   162 
       
   163 MainWorker = worker()
       
   164 
       
   165 
    63 
   166 def RunInMain(func):
    64 def RunInMain(func):
   167     def func_wrapper(*args, **kwargs):
    65     def func_wrapper(*args, **kwargs):
   168         return MainWorker.call(func, *args, **kwargs)
    66         return MainWorker.call(func, *args, **kwargs)
   169     return func_wrapper
    67     return func_wrapper
   170 
    68 
   171 
    69 
   172 class PLCObject(pyro.ObjBase):
    70 class PLCObject(object):
   173     def __init__(self, server):
    71     def __init__(self, WorkingDir, argv, statuschange, evaluator, pyruntimevars):
   174         pyro.ObjBase.__init__(self)
    72         self.workingdir = WorkingDir
   175         self.evaluator = server.evaluator
    73         # FIXME : is argv of any use nowadays ?
   176         self.argv = [server.workdir] + server.argv  # force argv[0] to be "path" to exec...
    74         self.argv = [WorkingDir] + argv  # force argv[0] to be "path" to exec...
   177         self.workingdir = server.workdir
    75         self.statuschange = statuschange
       
    76         self.evaluator = evaluator
       
    77         self.pyruntimevars = pyruntimevars
   178         self.PLCStatus = "Empty"
    78         self.PLCStatus = "Empty"
   179         self.PLClibraryHandle = None
    79         self.PLClibraryHandle = None
   180         self.PLClibraryLock = Lock()
    80         self.PLClibraryLock = Lock()
   181         self.DummyIteratorLock = None
       
   182         # Creates fake C funcs proxies
    81         # Creates fake C funcs proxies
   183         self._InitPLCStubCalls()
    82         self._InitPLCStubCalls()
   184         self.daemon = server.daemon
       
   185         self.statuschange = server.statuschange
       
   186         self.hmi_frame = None
       
   187         self.pyruntimevars = server.pyruntimevars
       
   188         self._loading_error = None
    83         self._loading_error = None
   189         self.python_runtime_vars = None
    84         self.python_runtime_vars = None
   190         self.TraceThread = None
    85         self.TraceThread = None
   191         self.TraceLock = Lock()
    86         self.TraceLock = Lock()
   192         self.Traces = []
    87         self.Traces = []
   193 
    88 
   194     # First task of worker -> no @RunInMain
    89     # First task of worker -> no @RunInMain
   195     def AutoLoad(self):
    90     def AutoLoad(self, autostart):
   196         # Get the last transfered PLC
    91         # Get the last transfered PLC
   197         try:
    92         try:
   198             self.CurrentPLCFilename = open(
    93             self.CurrentPLCFilename = open(
   199                 self._GetMD5FileName(),
    94                 self._GetMD5FileName(),
   200                 "r").read().strip() + lib_ext
    95                 "r").read().strip() + lib_ext
   201             if self.LoadPLC():
    96             if self.LoadPLC():
   202                 self.PLCStatus = "Stopped"
    97                 self.PLCStatus = "Stopped"
       
    98                 if autostart:
       
    99                     self.StartPLC()
       
   100                     return
   203         except Exception:
   101         except Exception:
   204             self.PLCStatus = "Empty"
   102             self.PLCStatus = "Empty"
   205             self.CurrentPLCFilename = None
   103             self.CurrentPLCFilename = None
       
   104 
       
   105         self.StatusChange()
   206 
   106 
   207     def StatusChange(self):
   107     def StatusChange(self):
   208         if self.statuschange is not None:
   108         if self.statuschange is not None:
   209             for callee in self.statuschange:
   109             for callee in self.statuschange:
   210                 callee(self.PLCStatus)
   110                 callee(self.PLCStatus)