33 import _ctypes # pylint: disable=wrong-import-order |
33 import _ctypes # pylint: disable=wrong-import-order |
34 import Pyro.core as pyro |
34 import Pyro.core as pyro |
35 |
35 |
36 from runtime.typemapping import TypeTranslator |
36 from runtime.typemapping import TypeTranslator |
37 from runtime.loglevels import LogLevelsDefault, LogLevelsCount |
37 from runtime.loglevels import LogLevelsDefault, LogLevelsCount |
|
38 from runtime import PlcStatus |
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 |
42 elif os.name == "posix": |
43 elif os.name == "posix": |
173 def __init__(self, server): |
174 def __init__(self, server): |
174 pyro.ObjBase.__init__(self) |
175 pyro.ObjBase.__init__(self) |
175 self.evaluator = server.evaluator |
176 self.evaluator = server.evaluator |
176 self.argv = [server.workdir] + server.argv # force argv[0] to be "path" to exec... |
177 self.argv = [server.workdir] + server.argv # force argv[0] to be "path" to exec... |
177 self.workingdir = server.workdir |
178 self.workingdir = server.workdir |
178 self.PLCStatus = "Empty" |
179 self.PLCStatus = PlcStatus.Empty |
179 self.PLClibraryHandle = None |
180 self.PLClibraryHandle = None |
180 self.PLClibraryLock = Lock() |
181 self.PLClibraryLock = Lock() |
181 self.DummyIteratorLock = None |
182 self.DummyIteratorLock = None |
182 # Creates fake C funcs proxies |
183 # Creates fake C funcs proxies |
183 self._InitPLCStubCalls() |
184 self._InitPLCStubCalls() |
197 try: |
198 try: |
198 self.CurrentPLCFilename = open( |
199 self.CurrentPLCFilename = open( |
199 self._GetMD5FileName(), |
200 self._GetMD5FileName(), |
200 "r").read().strip() + lib_ext |
201 "r").read().strip() + lib_ext |
201 if self.LoadPLC(): |
202 if self.LoadPLC(): |
202 self.PLCStatus = "Stopped" |
203 self.PLCStatus = PlcStatus.Stopped |
203 except Exception: |
204 except Exception: |
204 self.PLCStatus = "Empty" |
205 self.PLCStatus = PlcStatus.Empty |
205 self.CurrentPLCFilename = None |
206 self.CurrentPLCFilename = None |
206 |
207 |
207 def StatusChange(self): |
208 def StatusChange(self): |
208 if self.statuschange is not None: |
209 if self.statuschange is not None: |
209 for callee in self.statuschange: |
210 for callee in self.statuschange: |
500 res = "#EXCEPTION : "+str(e) |
501 res = "#EXCEPTION : "+str(e) |
501 self.LogMessage(1, ('PyEval@0x%x(Code="%s") Exception "%s"') % (FBID, cmd, str(e))) |
502 self.LogMessage(1, ('PyEval@0x%x(Code="%s") Exception "%s"') % (FBID, cmd, str(e))) |
502 |
503 |
503 @RunInMain |
504 @RunInMain |
504 def StartPLC(self): |
505 def StartPLC(self): |
505 if self.CurrentPLCFilename is not None and self.PLCStatus == "Stopped": |
506 if self.CurrentPLCFilename is not None and self.PLCStatus == PlcStatus.Stopped: |
506 c_argv = ctypes.c_char_p * len(self.argv) |
507 c_argv = ctypes.c_char_p * len(self.argv) |
507 res = self._startPLC(len(self.argv), c_argv(*self.argv)) |
508 res = self._startPLC(len(self.argv), c_argv(*self.argv)) |
508 if res == 0: |
509 if res == 0: |
509 self.PLCStatus = "Started" |
510 self.PLCStatus = PlcStatus.Started |
510 self.StatusChange() |
511 self.StatusChange() |
511 self.PythonRuntimeCall("start") |
512 self.PythonRuntimeCall("start") |
512 self.StartSem = Semaphore(0) |
513 self.StartSem = Semaphore(0) |
513 self.PythonThread = Thread(target=self.PythonThreadProc) |
514 self.PythonThread = Thread(target=self.PythonThreadProc) |
514 self.PythonThread.start() |
515 self.PythonThread.start() |
515 self.StartSem.acquire() |
516 self.StartSem.acquire() |
516 self.LogMessage("PLC started") |
517 self.LogMessage("PLC started") |
517 else: |
518 else: |
518 self.LogMessage(0, _("Problem starting PLC : error %d" % res)) |
519 self.LogMessage(0, _("Problem starting PLC : error %d" % res)) |
519 self.PLCStatus = "Broken" |
520 self.PLCStatus = PlcStatus.Broken |
520 self.StatusChange() |
521 self.StatusChange() |
521 |
522 |
522 @RunInMain |
523 @RunInMain |
523 def StopPLC(self): |
524 def StopPLC(self): |
524 if self.PLCStatus == "Started": |
525 if self.PLCStatus == PlcStatus.Started: |
525 self.LogMessage("PLC stopped") |
526 self.LogMessage("PLC stopped") |
526 self._stopPLC() |
527 self._stopPLC() |
527 self.PythonThread.join() |
528 self.PythonThread.join() |
528 self.PLCStatus = "Stopped" |
529 self.PLCStatus = PlcStatus.Stopped |
529 self.StatusChange() |
530 self.StatusChange() |
530 self.PythonRuntimeCall("stop") |
531 self.PythonRuntimeCall("stop") |
531 if self.TraceThread is not None: |
532 if self.TraceThread is not None: |
532 self.TraceThread.join() |
533 self.TraceThread.join() |
533 self.TraceThread = None |
534 self.TraceThread = None |
538 def GetPLCstatus(self): |
539 def GetPLCstatus(self): |
539 return self.PLCStatus, map(self.GetLogCount, xrange(LogLevelsCount)) |
540 return self.PLCStatus, map(self.GetLogCount, xrange(LogLevelsCount)) |
540 |
541 |
541 @RunInMain |
542 @RunInMain |
542 def NewPLC(self, md5sum, data, extrafiles): |
543 def NewPLC(self, md5sum, data, extrafiles): |
543 if self.PLCStatus in ["Stopped", "Empty", "Broken"]: |
544 if self.PLCStatus in [PlcStatus.Stopped, PlcStatus.Empty, PlcStatus.Broken]: |
544 NewFileName = md5sum + lib_ext |
545 NewFileName = md5sum + lib_ext |
545 extra_files_log = os.path.join(self.workingdir, "extra_files.txt") |
546 extra_files_log = os.path.join(self.workingdir, "extra_files.txt") |
546 |
547 |
547 old_PLC_filename = os.path.join(self.workingdir, self.CurrentPLCFilename) \ |
548 old_PLC_filename = os.path.join(self.workingdir, self.CurrentPLCFilename) \ |
548 if self.CurrentPLCFilename is not None \ |
549 if self.CurrentPLCFilename is not None \ |
554 |
555 |
555 if replace_PLC_shared_object: |
556 if replace_PLC_shared_object: |
556 self.UnLoadPLC() |
557 self.UnLoadPLC() |
557 |
558 |
558 self.LogMessage("NewPLC (%s)" % md5sum) |
559 self.LogMessage("NewPLC (%s)" % md5sum) |
559 self.PLCStatus = "Empty" |
560 self.PLCStatus = PlcStatus.Empty |
560 |
561 |
561 try: |
562 try: |
562 if replace_PLC_shared_object: |
563 if replace_PLC_shared_object: |
563 os.remove(old_PLC_filename) |
564 os.remove(old_PLC_filename) |
564 for filename in file(extra_files_log, "r").readlines() + [extra_files_log]: |
565 for filename in file(extra_files_log, "r").readlines() + [extra_files_log]: |
585 log.write(fname+'\n') |
586 log.write(fname+'\n') |
586 |
587 |
587 # Store new PLC filename |
588 # Store new PLC filename |
588 self.CurrentPLCFilename = NewFileName |
589 self.CurrentPLCFilename = NewFileName |
589 except Exception: |
590 except Exception: |
590 self.PLCStatus = "Broken" |
591 self.PLCStatus = PlcStatus.Broken |
591 self.StatusChange() |
592 self.StatusChange() |
592 PLCprint(traceback.format_exc()) |
593 PLCprint(traceback.format_exc()) |
593 return False |
594 return False |
594 |
595 |
595 if not replace_PLC_shared_object: |
596 if not replace_PLC_shared_object: |
596 self.PLCStatus = "Stopped" |
597 self.PLCStatus = PlcStatus.Stopped |
597 elif self.LoadPLC(): |
598 elif self.LoadPLC(): |
598 self.PLCStatus = "Stopped" |
599 self.PLCStatus = PlcStatus.Stopped |
599 else: |
600 else: |
600 self.PLCStatus = "Broken" |
601 self.PLCStatus = PlcStatus.Broken |
601 self.StatusChange() |
602 self.StatusChange() |
602 |
603 |
603 return self.PLCStatus == "Stopped" |
604 return self.PLCStatus == PlcStatus.Stopped |
604 return False |
605 return False |
605 |
606 |
606 def MatchMD5(self, MD5): |
607 def MatchMD5(self, MD5): |
607 try: |
608 try: |
608 last_md5 = open(self._GetMD5FileName(), "r").read() |
609 last_md5 = open(self._GetMD5FileName(), "r").read() |
633 else: |
634 else: |
634 self._suspendDebug(True) |
635 self._suspendDebug(True) |
635 |
636 |
636 def _TracesSwap(self): |
637 def _TracesSwap(self): |
637 self.LastSwapTrace = time() |
638 self.LastSwapTrace = time() |
638 if self.TraceThread is None and self.PLCStatus == "Started": |
639 if self.TraceThread is None and self.PLCStatus == PlcStatus.Started: |
639 self.TraceThread = Thread(target=self.TraceThreadProc) |
640 self.TraceThread = Thread(target=self.TraceThreadProc) |
640 self.TraceThread.start() |
641 self.TraceThread.start() |
641 self.TraceLock.acquire() |
642 self.TraceLock.acquire() |
642 Traces = self.Traces |
643 Traces = self.Traces |
643 self.Traces = [] |
644 self.Traces = [] |
651 def TraceThreadProc(self): |
652 def TraceThreadProc(self): |
652 """ |
653 """ |
653 Return a list of traces, corresponding to the list of required idx |
654 Return a list of traces, corresponding to the list of required idx |
654 """ |
655 """ |
655 self._resumeDebug() # Re-enable debugger |
656 self._resumeDebug() # Re-enable debugger |
656 while self.PLCStatus == "Started": |
657 while self.PLCStatus == PlcStatus.Started: |
657 tick = ctypes.c_uint32() |
658 tick = ctypes.c_uint32() |
658 size = ctypes.c_uint32() |
659 size = ctypes.c_uint32() |
659 buff = ctypes.c_void_p() |
660 buff = ctypes.c_void_p() |
660 TraceBuffer = None |
661 TraceBuffer = None |
661 |
662 |