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 from threading import Thread, Lock, Semaphore, Event |
26 from threading import Thread, Lock, Event, Condition |
27 import ctypes |
27 import ctypes |
28 import os |
28 import os |
29 import sys |
29 import sys |
30 import traceback |
30 import traceback |
31 import shutil |
31 import shutil |
40 from runtime.typemapping import TypeTranslator |
40 from runtime.typemapping import TypeTranslator |
41 from runtime.loglevels import LogLevelsDefault, LogLevelsCount |
41 from runtime.loglevels import LogLevelsDefault, LogLevelsCount |
42 from runtime.Stunnel import getPSKID |
42 from runtime.Stunnel import getPSKID |
43 from runtime import PlcStatus |
43 from runtime import PlcStatus |
44 from runtime import MainWorker |
44 from runtime import MainWorker |
|
45 from runtime import default_evaluator |
45 |
46 |
46 if os.name in ("nt", "ce"): |
47 if os.name in ("nt", "ce"): |
47 dlopen = _ctypes.LoadLibrary |
48 dlopen = _ctypes.LoadLibrary |
48 dlclose = _ctypes.FreeLibrary |
49 dlclose = _ctypes.FreeLibrary |
49 elif os.name == "posix": |
50 elif os.name == "posix": |
317 finally: |
318 finally: |
318 self.PLClibraryLock.release() |
319 self.PLClibraryLock.release() |
319 |
320 |
320 return False |
321 return False |
321 |
322 |
322 def PythonRuntimeCall(self, methodname): |
323 def PythonRuntimeCall(self, methodname, use_evaluator=True): |
323 """ |
324 """ |
324 Calls init, start, stop or cleanup method provided by |
325 Calls init, start, stop or cleanup method provided by |
325 runtime python files, loaded when new PLC uploaded |
326 runtime python files, loaded when new PLC uploaded |
326 """ |
327 """ |
327 for method in self.python_runtime_vars.get("_runtime_%s" % methodname, []): |
328 for method in self.python_runtime_vars.get("_runtime_%s" % methodname, []): |
328 _res, exp = self.evaluator(method) |
329 if use_evaluator: |
|
330 _res, exp = self.evaluator(method) |
|
331 else: |
|
332 _res, exp = default_evaluator(method) |
329 if exp is not None: |
333 if exp is not None: |
330 self.LogMessage(0, '\n'.join(traceback.format_exception(*exp))) |
334 self.LogMessage(0, '\n'.join(traceback.format_exception(*exp))) |
331 |
335 |
332 # used internaly |
336 # used internaly |
333 def PythonRuntimeInit(self): |
337 def PythonRuntimeInit(self): |
377 self.python_runtime_vars["_runtime_%s" % methodname].append(method) |
381 self.python_runtime_vars["_runtime_%s" % methodname].append(method) |
378 except Exception: |
382 except Exception: |
379 self.LogMessage(0, traceback.format_exc()) |
383 self.LogMessage(0, traceback.format_exc()) |
380 raise |
384 raise |
381 |
385 |
382 self.PythonRuntimeCall("init") |
386 self.PythonRuntimeCall("init", use_evaluator=False) |
|
387 |
|
388 self.PythonThreadCondLock = Lock() |
|
389 self.PythonThreadCond = Condition(self.PythonThreadCondLock) |
|
390 self.PythonThreadCmd = "Wait" |
|
391 self.PythonThread = Thread(target=self.PythonThreadProc) |
|
392 self.PythonThread.start() |
|
393 |
383 |
394 |
384 # used internaly |
395 # used internaly |
385 def PythonRuntimeCleanup(self): |
396 def PythonRuntimeCleanup(self): |
386 if self.python_runtime_vars is not None: |
397 if self.python_runtime_vars is not None: |
387 self.PythonRuntimeCall("cleanup") |
398 self.PythonThreadCommand("Finish") |
|
399 self.PythonThread.join() |
|
400 self.PythonRuntimeCall("cleanup", use_evaluator=False) |
388 |
401 |
389 self.python_runtime_vars = None |
402 self.python_runtime_vars = None |
390 |
403 |
391 def PythonThreadProc(self): |
404 def PythonThreadLoop(self): |
392 self.StartSem.release() |
|
393 res, cmd, blkid = "None", "None", ctypes.c_void_p() |
405 res, cmd, blkid = "None", "None", ctypes.c_void_p() |
394 compile_cache = {} |
406 compile_cache = {} |
395 while True: |
407 while True: |
396 cmd = self._PythonIterator(res, blkid) |
408 cmd = self._PythonIterator(res, blkid) |
397 FBID = blkid.value |
409 FBID = blkid.value |
413 self.python_runtime_vars["FBID"] = None |
425 self.python_runtime_vars["FBID"] = None |
414 except Exception as e: |
426 except Exception as e: |
415 res = "#EXCEPTION : "+str(e) |
427 res = "#EXCEPTION : "+str(e) |
416 self.LogMessage(1, ('PyEval@0x%x(Code="%s") Exception "%s"') % (FBID, cmd, str(e))) |
428 self.LogMessage(1, ('PyEval@0x%x(Code="%s") Exception "%s"') % (FBID, cmd, str(e))) |
417 |
429 |
|
430 def PythonThreadProc(self): |
|
431 while True: |
|
432 self.PythonThreadCondLock.acquire() |
|
433 cmd = self.PythonThreadCmd |
|
434 while cmd == "Wait": |
|
435 self.PythonThreadCond.wait() |
|
436 cmd = self.PythonThreadCmd |
|
437 self.PythonThreadCmd = "Wait" |
|
438 self.PythonThreadCondLock.release() |
|
439 |
|
440 if cmd == "Activate" : |
|
441 self.PythonRuntimeCall("start") |
|
442 |
|
443 self.PythonThreadLoop() |
|
444 |
|
445 self.PythonRuntimeCall("stop") |
|
446 else: # "Finish" |
|
447 break |
|
448 |
|
449 def PythonThreadCommand(self, cmd): |
|
450 self.PythonThreadCondLock.acquire() |
|
451 self.PythonThreadCmd = cmd |
|
452 self.PythonThreadCond.notify() |
|
453 self.PythonThreadCondLock.release() |
|
454 |
418 @RunInMain |
455 @RunInMain |
419 def StartPLC(self): |
456 def StartPLC(self): |
420 if self.CurrentPLCFilename is not None and self.PLCStatus == PlcStatus.Stopped: |
457 if self.CurrentPLCFilename is not None and self.PLCStatus == PlcStatus.Stopped: |
421 c_argv = ctypes.c_char_p * len(self.argv) |
458 c_argv = ctypes.c_char_p * len(self.argv) |
422 res = self._startPLC(len(self.argv), c_argv(*self.argv)) |
459 res = self._startPLC(len(self.argv), c_argv(*self.argv)) |
423 if res == 0: |
460 if res == 0: |
424 self.PLCStatus = PlcStatus.Started |
461 self.PLCStatus = PlcStatus.Started |
425 self.StatusChange() |
462 self.StatusChange() |
426 self.PythonRuntimeCall("start") |
463 self.PythonThreadCommand("Activate") |
427 self.StartSem = Semaphore(0) |
|
428 self.PythonThread = Thread(target=self.PythonThreadProc) |
|
429 self.PythonThread.start() |
|
430 self.StartSem.acquire() |
|
431 self.LogMessage("PLC started") |
464 self.LogMessage("PLC started") |
432 else: |
465 else: |
433 self.LogMessage(0, _("Problem starting PLC : error %d" % res)) |
466 self.LogMessage(0, _("Problem starting PLC : error %d" % res)) |
434 self.PLCStatus = PlcStatus.Broken |
467 self.PLCStatus = PlcStatus.Broken |
435 self.StatusChange() |
468 self.StatusChange() |
437 @RunInMain |
470 @RunInMain |
438 def StopPLC(self): |
471 def StopPLC(self): |
439 if self.PLCStatus == PlcStatus.Started: |
472 if self.PLCStatus == PlcStatus.Started: |
440 self.LogMessage("PLC stopped") |
473 self.LogMessage("PLC stopped") |
441 self._stopPLC() |
474 self._stopPLC() |
442 self.PythonThread.join() |
|
443 self.PLCStatus = PlcStatus.Stopped |
475 self.PLCStatus = PlcStatus.Stopped |
444 self.StatusChange() |
476 self.StatusChange() |
445 self.PythonRuntimeCall("stop") |
|
446 if self.TraceThread is not None: |
477 if self.TraceThread is not None: |
447 self.TraceThread.join() |
478 self.TraceThread.join() |
448 self.TraceThread = None |
479 self.TraceThread = None |
449 return True |
480 return True |
450 return False |
481 return False |