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: |
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: |