runtime/PLCObject.py
changeset 1434 6e0cd0ceabb7
parent 1433 4a45f6642523
child 1435 291a17b755d1
equal deleted inserted replaced
1433:4a45f6642523 1434:6e0cd0ceabb7
    21 #You should have received a copy of the GNU General Public
    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
    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
    23 #Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
    24 
    24 
    25 import Pyro.core as pyro
    25 import Pyro.core as pyro
    26 from threading import Timer, Thread, Lock, Semaphore
    26 from threading import Timer, Thread, Lock, Semaphore, Event
    27 import ctypes, os, commands, types, sys
    27 import ctypes, os, commands, types, sys
    28 from targets.typemapping import LogLevelsDefault, LogLevelsCount, TypeTranslator, UnpackDebugBuffer
    28 from targets.typemapping import LogLevelsDefault, LogLevelsCount, TypeTranslator, UnpackDebugBuffer
       
    29 from time import time
    29 
    30 
    30 
    31 
    31 if os.name in ("nt", "ce"):
    32 if os.name in ("nt", "ce"):
    32     from _ctypes import LoadLibrary as dlopen
    33     from _ctypes import LoadLibrary as dlopen
    33     from _ctypes import FreeLibrary as dlclose
    34     from _ctypes import FreeLibrary as dlclose
    48 def PLCprint(message):
    49 def PLCprint(message):
    49     sys.stdout.write("PLCobject : "+message+"\n")
    50     sys.stdout.write("PLCobject : "+message+"\n")
    50     sys.stdout.flush()
    51     sys.stdout.flush()
    51 
    52 
    52 class PLCObject(pyro.ObjBase):
    53 class PLCObject(pyro.ObjBase):
    53     _Idxs = []
       
    54     def __init__(self, workingdir, daemon, argv, statuschange, evaluator, website):
    54     def __init__(self, workingdir, daemon, argv, statuschange, evaluator, website):
    55         pyro.ObjBase.__init__(self)
    55         pyro.ObjBase.__init__(self)
    56         self.evaluator = evaluator
    56         self.evaluator = evaluator
    57         self.argv = [workingdir] + argv # force argv[0] to be "path" to exec...
    57         self.argv = [workingdir] + argv # force argv[0] to be "path" to exec...
    58         self.workingdir = workingdir
    58         self.workingdir = workingdir
    66         self.statuschange = statuschange
    66         self.statuschange = statuschange
    67         self.hmi_frame = None
    67         self.hmi_frame = None
    68         self.website = website
    68         self.website = website
    69         self._loading_error = None
    69         self._loading_error = None
    70         self.python_runtime_vars = None
    70         self.python_runtime_vars = None
       
    71         self.TraceThread = None
       
    72         self.TraceLock = Lock()
       
    73         self.TraceWakeup = Event()
       
    74         self.Traces = []
    71 
    75 
    72         # Get the last transfered PLC if connector must be restart
    76         # Get the last transfered PLC if connector must be restart
    73         try:
    77         try:
    74             self.CurrentPLCFilename=open(
    78             self.CurrentPLCFilename=open(
    75                              self._GetMD5FileName(),
    79                              self._GetMD5FileName(),
   363     def StopPLC(self):
   367     def StopPLC(self):
   364         if self.PLCStatus == "Started":
   368         if self.PLCStatus == "Started":
   365             self.LogMessage("PLC stopped")
   369             self.LogMessage("PLC stopped")
   366             self._stopPLC()
   370             self._stopPLC()
   367             self.PythonThread.join()
   371             self.PythonThread.join()
       
   372             if self.TraceThread is not None :
       
   373                 self.TraceWakeup.set()
       
   374                 self.TraceThread.join()
       
   375                 self.TraceThread = None
   368             return True
   376             return True
   369         return False
   377         return False
   370 
   378 
   371     def _Reload(self):
   379     def _Reload(self):
   372         self.daemon.shutdown(True)
   380         self.daemon.shutdown(True)
   451         """
   459         """
   452         if idxs:
   460         if idxs:
   453             # suspend but dont disable
   461             # suspend but dont disable
   454             if self._suspendDebug(False) == 0:
   462             if self._suspendDebug(False) == 0:
   455                 # keep a copy of requested idx
   463                 # keep a copy of requested idx
   456                 self._Idxs = idxs[:]
       
   457                 self._ResetDebugVariables()
   464                 self._ResetDebugVariables()
   458                 for idx,iectype,force in idxs:
   465                 for idx,iectype,force in idxs:
   459                     if force !=None:
   466                     if force !=None:
   460                         c_type,unpack_func, pack_func = \
   467                         c_type,unpack_func, pack_func = \
   461                             TypeTranslator.get(iectype,
   468                             TypeTranslator.get(iectype,
   462                                                     (None,None,None))
   469                                                     (None,None,None))
   463                         force = ctypes.byref(pack_func(c_type,force))
   470                         force = ctypes.byref(pack_func(c_type,force))
   464                     self._RegisterDebugVariable(idx, force)
   471                     self._RegisterDebugVariable(idx, force)
       
   472                 self._TracesSwap()
   465                 self._resumeDebug()
   473                 self._resumeDebug()
   466         else:
   474         else:
   467             self._suspendDebug(True)
   475             self._suspendDebug(True)
   468             self._Idxs =  []
   476 
       
   477     def _TracesPush(self, trace):
       
   478         self.TraceLock.acquire()
       
   479         lT = len(self.Traces)
       
   480         if lT != 0 and lT * len(self.Traces[0]) > 1024 * 1024 :
       
   481             self.Traces.pop(0)
       
   482         self.Traces.append(trace)
       
   483         self.TraceLock.release()
       
   484 
       
   485     def _TracesSwap(self):
       
   486         self.LastSwapTrace = time()
       
   487         if self.TraceThread is None and self.PLCStatus == "Started":
       
   488             self.TraceThread = Thread(target=self.TraceThreadProc)
       
   489             self.TraceThread.start()
       
   490         self.TraceLock.acquire()
       
   491         Traces = self.Traces
       
   492         self.Traces = []
       
   493         self.TraceLock.release()
       
   494         self.TraceWakeup.set()
       
   495         return Traces
       
   496 
       
   497     def _TracesAutoSuspend(self):
       
   498         # TraceProc stops here if Traces not polled for 3 seconds
       
   499         traces_age = time() - self.LastSwapTrace
       
   500         if traces_age > 3:
       
   501             self.TraceLock.acquire()
       
   502             self.Traces = []
       
   503             self.TraceLock.release()
       
   504             self._suspendDebug(True) # Disable debugger
       
   505             self.TraceWakeup.wait()
       
   506             self._resumeDebug() # Re-enable debugger
       
   507 
       
   508     def _TracesFlush(self):
       
   509         self.TraceLock.acquire()
       
   510         self.Traces = []
       
   511         self.TraceLock.release()
   469 
   512 
   470     def GetTraceVariables(self):
   513     def GetTraceVariables(self):
   471         """
   514         return self.PLCStatus, self._TracesSwap()
   472         Return a list of variables, corresponding to the list of required idx
   515 
   473         """
   516     def TraceThreadProc(self):
   474         if self.PLCStatus == "Started":
   517         """
       
   518         Return a list of traces, corresponding to the list of required idx
       
   519         """
       
   520         while self.PLCStatus == "Started" :
   475             tick = ctypes.c_uint32()
   521             tick = ctypes.c_uint32()
   476             size = ctypes.c_uint32()
   522             size = ctypes.c_uint32()
   477             buff = ctypes.c_void_p()
   523             buff = ctypes.c_void_p()
   478             TraceBuffer = None
   524             TraceBuffer = None
   479             if self.PLClibraryLock.acquire(False):
   525             if self.PLClibraryLock.acquire(False):
   483                     if size.value:
   529                     if size.value:
   484                         TraceBuffer = ctypes.string_at(buff.value, size.value)
   530                         TraceBuffer = ctypes.string_at(buff.value, size.value)
   485                     self._FreeDebugData()
   531                     self._FreeDebugData()
   486                 self.PLClibraryLock.release()
   532                 self.PLClibraryLock.release()
   487             if TraceBuffer is not None:
   533             if TraceBuffer is not None:
   488                 return self.PLCStatus, tick.value, TraceBuffer
   534                 self._TracesPush((tick.value, TraceBuffer))
   489         return self.PLCStatus, None, None
   535             self._TracesAutoSuspend()
       
   536         self._TracesFlush()
       
   537 
   490 
   538 
   491     def RemoteExec(self, script, **kwargs):
   539     def RemoteExec(self, script, **kwargs):
   492         try:
   540         try:
   493             exec script in kwargs
   541             exec script in kwargs
   494         except:
   542         except: