runtime/PLCObject.py
changeset 2584 adfdeaba5a6a
parent 2583 e172ab28d04e
child 2586 b89484560a97
equal deleted inserted replaced
2579:8fb5c6eddc72 2584:adfdeaba5a6a
    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