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(), |
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: |