32 import _ctypes # pylint: disable=wrong-import-order |
32 import _ctypes # pylint: disable=wrong-import-order |
33 |
33 |
34 from runtime.typemapping import TypeTranslator |
34 from runtime.typemapping import TypeTranslator |
35 from runtime.loglevels import LogLevelsDefault, LogLevelsCount |
35 from runtime.loglevels import LogLevelsDefault, LogLevelsCount |
36 from runtime.Stunnel import getPSKID |
36 from runtime.Stunnel import getPSKID |
|
37 from runtime import PlcStatus |
37 from runtime import MainWorker |
38 from runtime import MainWorker |
38 |
39 |
39 if os.name in ("nt", "ce"): |
40 if os.name in ("nt", "ce"): |
40 dlopen = _ctypes.LoadLibrary |
41 dlopen = _ctypes.LoadLibrary |
41 dlclose = _ctypes.FreeLibrary |
42 dlclose = _ctypes.FreeLibrary |
73 # FIXME : is argv of any use nowadays ? |
74 # FIXME : is argv of any use nowadays ? |
74 self.argv = [WorkingDir] + argv # force argv[0] to be "path" to exec... |
75 self.argv = [WorkingDir] + argv # force argv[0] to be "path" to exec... |
75 self.statuschange = statuschange |
76 self.statuschange = statuschange |
76 self.evaluator = evaluator |
77 self.evaluator = evaluator |
77 self.pyruntimevars = pyruntimevars |
78 self.pyruntimevars = pyruntimevars |
78 self.PLCStatus = "Empty" |
79 self.PLCStatus = PlcStatus.Empty |
79 self.PLClibraryHandle = None |
80 self.PLClibraryHandle = None |
80 self.PLClibraryLock = Lock() |
81 self.PLClibraryLock = Lock() |
81 # Creates fake C funcs proxies |
82 # Creates fake C funcs proxies |
82 self._InitPLCStubCalls() |
83 self._InitPLCStubCalls() |
83 self._loading_error = None |
84 self._loading_error = None |
394 self.LogMessage(1, ('PyEval@0x%x(Code="%s") Exception "%s"') % ( |
395 self.LogMessage(1, ('PyEval@0x%x(Code="%s") Exception "%s"') % ( |
395 FBID, cmd, '\n'.join(traceback.format_exception(*exp)))) |
396 FBID, cmd, '\n'.join(traceback.format_exception(*exp)))) |
396 else: |
397 else: |
397 res = str(result) |
398 res = str(result) |
398 self.python_runtime_vars["FBID"] = None |
399 self.python_runtime_vars["FBID"] = None |
399 except Exception, e: |
400 except Exception as e: |
400 res = "#EXCEPTION : "+str(e) |
401 res = "#EXCEPTION : "+str(e) |
401 self.LogMessage(1, ('PyEval@0x%x(Code="%s") Exception "%s"') % (FBID, cmd, str(e))) |
402 self.LogMessage(1, ('PyEval@0x%x(Code="%s") Exception "%s"') % (FBID, cmd, str(e))) |
402 |
403 |
403 @RunInMain |
404 @RunInMain |
404 def StartPLC(self): |
405 def StartPLC(self): |
405 if self.CurrentPLCFilename is not None and self.PLCStatus == "Stopped": |
406 if self.CurrentPLCFilename is not None and self.PLCStatus == PlcStatus.Stopped: |
406 c_argv = ctypes.c_char_p * len(self.argv) |
407 c_argv = ctypes.c_char_p * len(self.argv) |
407 res = self._startPLC(len(self.argv), c_argv(*self.argv)) |
408 res = self._startPLC(len(self.argv), c_argv(*self.argv)) |
408 if res == 0: |
409 if res == 0: |
409 self.PLCStatus = "Started" |
410 self.PLCStatus = PlcStatus.Started |
410 self.StatusChange() |
411 self.StatusChange() |
411 self.PythonRuntimeCall("start") |
412 self.PythonRuntimeCall("start") |
412 self.StartSem = Semaphore(0) |
413 self.StartSem = Semaphore(0) |
413 self.PythonThread = Thread(target=self.PythonThreadProc) |
414 self.PythonThread = Thread(target=self.PythonThreadProc) |
414 self.PythonThread.start() |
415 self.PythonThread.start() |
415 self.StartSem.acquire() |
416 self.StartSem.acquire() |
416 self.LogMessage("PLC started") |
417 self.LogMessage("PLC started") |
417 else: |
418 else: |
418 self.LogMessage(0, _("Problem starting PLC : error %d" % res)) |
419 self.LogMessage(0, _("Problem starting PLC : error %d" % res)) |
419 self.PLCStatus = "Broken" |
420 self.PLCStatus = PlcStatus.Broken |
420 self.StatusChange() |
421 self.StatusChange() |
421 |
422 |
422 @RunInMain |
423 @RunInMain |
423 def StopPLC(self): |
424 def StopPLC(self): |
424 if self.PLCStatus == "Started": |
425 if self.PLCStatus == PlcStatus.Started: |
425 self.LogMessage("PLC stopped") |
426 self.LogMessage("PLC stopped") |
426 self._stopPLC() |
427 self._stopPLC() |
427 self.PythonThread.join() |
428 self.PythonThread.join() |
428 self.PLCStatus = "Stopped" |
429 self.PLCStatus = PlcStatus.Stopped |
429 self.StatusChange() |
430 self.StatusChange() |
430 self.PythonRuntimeCall("stop") |
431 self.PythonRuntimeCall("stop") |
431 if self.TraceThread is not None: |
432 if self.TraceThread is not None: |
432 self.TraceThread.join() |
433 self.TraceThread.join() |
433 self.TraceThread = None |
434 self.TraceThread = None |
442 def GetPLCID(self): |
443 def GetPLCID(self): |
443 return getPSKID() |
444 return getPSKID() |
444 |
445 |
445 @RunInMain |
446 @RunInMain |
446 def NewPLC(self, md5sum, data, extrafiles): |
447 def NewPLC(self, md5sum, data, extrafiles): |
447 if self.PLCStatus in ["Stopped", "Empty", "Broken"]: |
448 if self.PLCStatus in [PlcStatus.Stopped, PlcStatus.Empty, PlcStatus.Broken]: |
448 NewFileName = md5sum + lib_ext |
449 NewFileName = md5sum + lib_ext |
449 extra_files_log = os.path.join(self.workingdir, "extra_files.txt") |
450 extra_files_log = os.path.join(self.workingdir, "extra_files.txt") |
450 |
451 |
451 old_PLC_filename = os.path.join(self.workingdir, self.CurrentPLCFilename) \ |
452 old_PLC_filename = os.path.join(self.workingdir, self.CurrentPLCFilename) \ |
452 if self.CurrentPLCFilename is not None \ |
453 if self.CurrentPLCFilename is not None \ |
458 |
459 |
459 if replace_PLC_shared_object: |
460 if replace_PLC_shared_object: |
460 self.UnLoadPLC() |
461 self.UnLoadPLC() |
461 |
462 |
462 self.LogMessage("NewPLC (%s)" % md5sum) |
463 self.LogMessage("NewPLC (%s)" % md5sum) |
463 self.PLCStatus = "Empty" |
464 self.PLCStatus = PlcStatus.Empty |
464 |
465 |
465 try: |
466 try: |
466 if replace_PLC_shared_object: |
467 if replace_PLC_shared_object: |
467 os.remove(old_PLC_filename) |
468 os.remove(old_PLC_filename) |
468 for filename in file(extra_files_log, "r").readlines() + [extra_files_log]: |
469 for filename in file(extra_files_log, "r").readlines() + [extra_files_log]: |
489 log.write(fname+'\n') |
490 log.write(fname+'\n') |
490 |
491 |
491 # Store new PLC filename |
492 # Store new PLC filename |
492 self.CurrentPLCFilename = NewFileName |
493 self.CurrentPLCFilename = NewFileName |
493 except Exception: |
494 except Exception: |
494 self.PLCStatus = "Broken" |
495 self.PLCStatus = PlcStatus.Broken |
495 self.StatusChange() |
496 self.StatusChange() |
496 PLCprint(traceback.format_exc()) |
497 PLCprint(traceback.format_exc()) |
497 return False |
498 return False |
498 |
499 |
499 if not replace_PLC_shared_object: |
500 if not replace_PLC_shared_object: |
500 self.PLCStatus = "Stopped" |
501 self.PLCStatus = PlcStatus.Stopped |
501 elif self.LoadPLC(): |
502 elif self.LoadPLC(): |
502 self.PLCStatus = "Stopped" |
503 self.PLCStatus = PlcStatus.Stopped |
503 else: |
504 else: |
504 self.PLCStatus = "Broken" |
505 self.PLCStatus = PlcStatus.Broken |
505 self.StatusChange() |
506 self.StatusChange() |
506 |
507 |
507 return self.PLCStatus == "Stopped" |
508 return self.PLCStatus == PlcStatus.Stopped |
508 return False |
509 return False |
509 |
510 |
510 def MatchMD5(self, MD5): |
511 def MatchMD5(self, MD5): |
511 try: |
512 try: |
512 last_md5 = open(self._GetMD5FileName(), "r").read() |
513 last_md5 = open(self._GetMD5FileName(), "r").read() |
537 else: |
538 else: |
538 self._suspendDebug(True) |
539 self._suspendDebug(True) |
539 |
540 |
540 def _TracesSwap(self): |
541 def _TracesSwap(self): |
541 self.LastSwapTrace = time() |
542 self.LastSwapTrace = time() |
542 if self.TraceThread is None and self.PLCStatus == "Started": |
543 if self.TraceThread is None and self.PLCStatus == PlcStatus.Started: |
543 self.TraceThread = Thread(target=self.TraceThreadProc) |
544 self.TraceThread = Thread(target=self.TraceThreadProc) |
544 self.TraceThread.start() |
545 self.TraceThread.start() |
545 self.TraceLock.acquire() |
546 self.TraceLock.acquire() |
546 Traces = self.Traces |
547 Traces = self.Traces |
547 self.Traces = [] |
548 self.Traces = [] |
555 def TraceThreadProc(self): |
556 def TraceThreadProc(self): |
556 """ |
557 """ |
557 Return a list of traces, corresponding to the list of required idx |
558 Return a list of traces, corresponding to the list of required idx |
558 """ |
559 """ |
559 self._resumeDebug() # Re-enable debugger |
560 self._resumeDebug() # Re-enable debugger |
560 while self.PLCStatus == "Started": |
561 while self.PLCStatus == PlcStatus.Started: |
561 tick = ctypes.c_uint32() |
562 tick = ctypes.c_uint32() |
562 size = ctypes.c_uint32() |
563 size = ctypes.c_uint32() |
563 buff = ctypes.c_void_p() |
564 buff = ctypes.c_void_p() |
564 TraceBuffer = None |
565 TraceBuffer = None |
565 |
566 |
598 |
599 |
599 self.TraceThread = None |
600 self.TraceThread = None |
600 |
601 |
601 def RemoteExec(self, script, *kwargs): |
602 def RemoteExec(self, script, *kwargs): |
602 try: |
603 try: |
603 exec script in kwargs |
604 exec(script, kwargs) |
604 except Exception: |
605 except Exception: |
605 _e_type, e_value, e_traceback = sys.exc_info() |
606 _e_type, e_value, e_traceback = sys.exc_info() |
606 line_no = traceback.tb_lineno(get_last_traceback(e_traceback)) |
607 line_no = traceback.tb_lineno(get_last_traceback(e_traceback)) |
607 return (-1, "RemoteExec script failed!\n\nLine %d: %s\n\t%s" % |
608 return (-1, "RemoteExec script failed!\n\nLine %d: %s\n\t%s" % |
608 (line_no, e_value, script.splitlines()[line_no - 1])) |
609 (line_no, e_value, script.splitlines()[line_no - 1])) |