runtime/PLCObject.py
changeset 2416 1ca207782dde
parent 1997 d9e8fb47340f
child 2418 5587c490a070
equal deleted inserted replaced
2415:f7d8891fe708 2416:1ca207782dde
    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