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