runtime/PLCObject.py
branch1.1 Korean release
changeset 968 eee7625de1f7
parent 956 c838c50f8946
child 969 1950fe687dde
equal deleted inserted replaced
808:6e205c1f05a0 968:eee7625de1f7
    23 #Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
    23 #Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
    24 
    24 
    25 import Pyro.core as pyro
    25 import Pyro.core as pyro
    26 from threading import Timer, Thread, Lock, Semaphore
    26 from threading import Timer, Thread, Lock, Semaphore
    27 import ctypes, os, commands, types, sys
    27 import ctypes, os, commands, types, sys
    28 from targets.typemapping import SameEndianessTypeTranslator as TypeTranslator
    28 from targets.typemapping import LogLevelsDefault, LogLevelsCount, SameEndianessTypeTranslator as TypeTranslator
       
    29 
    29 
    30 
    30 if os.name in ("nt", "ce"):
    31 if os.name in ("nt", "ce"):
    31     from _ctypes import LoadLibrary as dlopen
    32     from _ctypes import LoadLibrary as dlopen
    32     from _ctypes import FreeLibrary as dlclose
    33     from _ctypes import FreeLibrary as dlclose
    33 elif os.name == "posix":
    34 elif os.name == "posix":
    63         self._FreePLC()
    64         self._FreePLC()
    64         self.daemon = daemon
    65         self.daemon = daemon
    65         self.statuschange = statuschange
    66         self.statuschange = statuschange
    66         self.hmi_frame = None
    67         self.hmi_frame = None
    67         self.website = website
    68         self.website = website
       
    69         self._loading_error = None
       
    70         self.python_threads_vars = None
    68         
    71         
    69         # Get the last transfered PLC if connector must be restart
    72         # Get the last transfered PLC if connector must be restart
    70         try:
    73         try:
    71             self.CurrentPLCFilename=open(
    74             self.CurrentPLCFilename=open(
    72                              self._GetMD5FileName(),
    75                              self._GetMD5FileName(),
    77 
    80 
    78     def StatusChange(self):
    81     def StatusChange(self):
    79         if self.statuschange is not None:
    82         if self.statuschange is not None:
    80             self.statuschange(self.PLCStatus)
    83             self.statuschange(self.PLCStatus)
    81 
    84 
       
    85     def LogMessage(self, *args):
       
    86         if len(args) == 2:
       
    87             level, msg = args
       
    88         else:
       
    89             level = LogLevelsDefault
       
    90             msg, = args
       
    91         return self._LogMessage(level, msg, len(msg))
       
    92 
       
    93 
       
    94     def GetLogCount(self, level):
       
    95         if self._GetLogCount is not None :
       
    96             return int(self._GetLogCount(level))
       
    97         elif self._loading_error is not None and level==0:
       
    98             return 1;
       
    99 
       
   100     def GetLogMessage(self, level, msgid):
       
   101         tick = ctypes.c_uint32()
       
   102         tv_sec = ctypes.c_uint32()
       
   103         tv_nsec = ctypes.c_uint32()
       
   104         if self._GetLogMessage is not None:
       
   105             maxsz = len(self._log_read_buffer)-1
       
   106             sz = self._GetLogMessage(level, msgid, 
       
   107                 self._log_read_buffer, maxsz,
       
   108                 ctypes.byref(tick),
       
   109                 ctypes.byref(tv_sec),
       
   110                 ctypes.byref(tv_nsec))
       
   111             if sz and sz <= maxsz:
       
   112                 self._log_read_buffer[sz] = '\x00'
       
   113                 return self._log_read_buffer.value,tick.value,tv_sec.value,tv_nsec.value
       
   114         elif self._loading_error is not None and level==0:
       
   115             return self._loading_error,0,0,0
       
   116         return None
       
   117 
    82     def _GetMD5FileName(self):
   118     def _GetMD5FileName(self):
    83         return os.path.join(self.workingdir, "lasttransferedPLC.md5")
   119         return os.path.join(self.workingdir, "lasttransferedPLC.md5")
    84 
   120 
    85     def _GetLibFileName(self):
   121     def _GetLibFileName(self):
    86         return os.path.join(self.workingdir,self.CurrentPLCFilename)
   122         return os.path.join(self.workingdir,self.CurrentPLCFilename)
   103             self._stopPLC_real.restype = None
   139             self._stopPLC_real.restype = None
   104             
   140             
   105             self._PythonIterator = getattr(self.PLClibraryHandle, "PythonIterator", None)
   141             self._PythonIterator = getattr(self.PLClibraryHandle, "PythonIterator", None)
   106             if self._PythonIterator is not None:
   142             if self._PythonIterator is not None:
   107                 self._PythonIterator.restype = ctypes.c_char_p
   143                 self._PythonIterator.restype = ctypes.c_char_p
   108                 self._PythonIterator.argtypes = [ctypes.c_char_p]
   144                 self._PythonIterator.argtypes = [ctypes.c_char_p, ctypes.POINTER(ctypes.c_void_p)]
   109                 
   145                 
   110                 self._stopPLC = self._stopPLC_real
   146                 self._stopPLC = self._stopPLC_real
   111             else:
   147             else:
   112                 # If python confnode is not enabled, we reuse _PythonIterator
   148                 # If python confnode is not enabled, we reuse _PythonIterator
   113                 # as a call that block pythonthread until StopPLC 
   149                 # as a call that block pythonthread until StopPLC 
   114                 self.PythonIteratorLock = Lock()
   150                 self.PythonIteratorLock = Lock()
   115                 self.PythonIteratorLock.acquire()
   151                 self.PythonIteratorLock.acquire()
   116                 def PythonIterator(res):
   152                 def PythonIterator(res, blkid):
   117                     self.PythonIteratorLock.acquire()
   153                     self.PythonIteratorLock.acquire()
   118                     self.PythonIteratorLock.release()
   154                     self.PythonIteratorLock.release()
   119                     return None
   155                     return None
   120                 self._PythonIterator = PythonIterator
   156                 self._PythonIterator = PythonIterator
   121                 
   157                 
   143             self._suspendDebug.restype = ctypes.c_int
   179             self._suspendDebug.restype = ctypes.c_int
   144             self._suspendDebug.argtypes = [ctypes.c_int]
   180             self._suspendDebug.argtypes = [ctypes.c_int]
   145 
   181 
   146             self._resumeDebug = self.PLClibraryHandle.resumeDebug
   182             self._resumeDebug = self.PLClibraryHandle.resumeDebug
   147             self._resumeDebug.restype = None
   183             self._resumeDebug.restype = None
   148             
   184 
       
   185             self._GetLogCount = self.PLClibraryHandle.GetLogCount
       
   186             self._GetLogCount.restype = ctypes.c_uint32
       
   187             self._GetLogCount.argtypes = [ctypes.c_uint8]
       
   188 
       
   189             self._LogMessage = self.PLClibraryHandle.LogMessage
       
   190             self._LogMessage.restype = ctypes.c_int
       
   191             self._LogMessage.argtypes = [ctypes.c_uint8, ctypes.c_char_p, ctypes.c_uint32]
       
   192             
       
   193             self._log_read_buffer = ctypes.create_string_buffer(1<<14) #16K
       
   194             self._GetLogMessage = self.PLClibraryHandle.GetLogMessage
       
   195             self._GetLogMessage.restype = ctypes.c_uint32
       
   196             self._GetLogMessage.argtypes = [ctypes.c_uint8, ctypes.c_uint32, ctypes.c_char_p, ctypes.c_uint32, ctypes.POINTER(ctypes.c_uint32), ctypes.POINTER(ctypes.c_uint32), ctypes.POINTER(ctypes.c_uint32)]
       
   197 
       
   198             self._loading_error = None
   149             return True
   199             return True
   150         except:
   200         except:
   151             PLCprint(traceback.format_exc())
   201             self._loading_error = traceback.format_exc()
       
   202             PLCprint(self._loading_error)
   152             return False
   203             return False
   153 
   204 
   154     def _FreePLC(self):
   205     def _FreePLC(self):
   155         """
   206         """
   156         Unload PLC library.
   207         Unload PLC library.
   166         self._FreeDebugData = lambda:None
   217         self._FreeDebugData = lambda:None
   167         self._GetDebugData = lambda:-1
   218         self._GetDebugData = lambda:-1
   168         self._suspendDebug = lambda x:-1
   219         self._suspendDebug = lambda x:-1
   169         self._resumeDebug = lambda:None
   220         self._resumeDebug = lambda:None
   170         self._PythonIterator = lambda:""
   221         self._PythonIterator = lambda:""
       
   222         self._GetLogCount = None 
       
   223         self._LogMessage = lambda l,m,s:PLCprint("OFF LOG :"+m)
       
   224         self._GetLogMessage = None
   171         self.PLClibraryHandle = None
   225         self.PLClibraryHandle = None
   172         # Unload library explicitely
   226         # Unload library explicitely
   173         if getattr(self,"_PLClibraryHandle",None) is not None:
   227         if getattr(self,"_PLClibraryHandle",None) is not None:
   174             dlclose(self._PLClibraryHandle)
   228             dlclose(self._PLClibraryHandle)
   175             self._PLClibraryHandle = None
   229             self._PLClibraryHandle = None
   219     def PythonThreadProc(self):
   273     def PythonThreadProc(self):
   220         self.PLCStatus = "Started"
   274         self.PLCStatus = "Started"
   221         self.StatusChange()
   275         self.StatusChange()
   222         self.StartSem.release()
   276         self.StartSem.release()
   223         self.evaluator(self.PrepareRuntimePy)
   277         self.evaluator(self.PrepareRuntimePy)
   224         res,cmd = "None","None"
   278         res,cmd,blkid = "None","None",ctypes.c_void_p()
       
   279         compile_cache={}
   225         while True:
   280         while True:
   226             #print "_PythonIterator(", res, ")",
   281             # print "_PythonIterator(", res, ")",
   227             cmd = self._PythonIterator(res)
   282             cmd = self._PythonIterator(res,blkid)
   228             #print " -> ", cmd
   283             FBID = blkid.value 
       
   284             # print " -> ", cmd, blkid
   229             if cmd is None:
   285             if cmd is None:
   230                 break
   286                 break
   231             try :
   287             try :
   232                 res = str(self.evaluator(eval,cmd,self.python_threads_vars))
   288                 self.python_threads_vars["FBID"]=FBID
       
   289                 ccmd,AST =compile_cache.get(FBID, (None,None))
       
   290                 if ccmd is None or ccmd!=cmd:
       
   291                     AST = compile(cmd, '<plc>', 'eval')
       
   292                     compile_cache[FBID]=(cmd,AST)
       
   293                 result,exp = self.evaluator(eval,cmd,self.python_threads_vars)
       
   294                 if exp is not None: 
       
   295                     raise(exp)
       
   296                 else:
       
   297                     res=str(result)
       
   298                 self.python_threads_vars["FBID"]=None
   233             except Exception,e:
   299             except Exception,e:
   234                 res = "#EXCEPTION : "+str(e)
   300                 res = "#EXCEPTION : "+str(e)
   235                 PLCprint(res)
   301                 PLCprint(('*** Python eval EXCEPTION ***\n'+
       
   302                           '| Function Block ID: %d\n'+
       
   303                           '| Command : "%s"\n'+
       
   304                           '| Exception : "%s"')%(FBID,cmd,str(e)))
   236         self.PLCStatus = "Stopped"
   305         self.PLCStatus = "Stopped"
   237         self.StatusChange()
   306         self.StatusChange()
   238         self.evaluator(self.FinishRuntimePy)
   307         self.evaluator(self.FinishRuntimePy)
   239     
   308     
   240     def StartPLC(self):
   309     def StartPLC(self):
   241         PLCprint("StartPLC")
       
   242         if self.CurrentPLCFilename is not None and self.PLCStatus == "Stopped":
   310         if self.CurrentPLCFilename is not None and self.PLCStatus == "Stopped":
   243             c_argv = ctypes.c_char_p * len(self.argv)
   311             c_argv = ctypes.c_char_p * len(self.argv)
   244             error = None
   312             error = None
   245             if self._LoadNewPLC():
   313             res = self._startPLC(len(self.argv),c_argv(*self.argv))
   246                 if self._startPLC(len(self.argv),c_argv(*self.argv)) == 0:
   314             if res == 0:
   247                     self.StartSem=Semaphore(0)
   315                 self.StartSem=Semaphore(0)
   248                     self.PythonThread = Thread(target=self.PythonThreadProc)
   316                 self.PythonThread = Thread(target=self.PythonThreadProc)
   249                     self.PythonThread.start()
   317                 self.PythonThread.start()
   250                     self.StartSem.acquire()
   318                 self.StartSem.acquire()
   251                 else:
   319                 self.LogMessage("PLC started")
   252                     error = "starting"
       
   253             else:
   320             else:
   254                 error = "loading"
   321                 self.LogMessage(_("Problem starting PLC : error %d" % res))
   255             if error is not None:
       
   256                 PLCprint("Problem %s PLC"%error)
       
   257                 self.PLCStatus = "Broken"
   322                 self.PLCStatus = "Broken"
   258                 self.StatusChange()
   323                 self.StatusChange()
   259                 self._FreePLC()
       
   260             
   324             
   261     def StopPLC(self):
   325     def StopPLC(self):
   262         PLCprint("StopPLC")
       
   263         if self.PLCStatus == "Started":
   326         if self.PLCStatus == "Started":
       
   327             self.LogMessage("PLC stopped")
   264             self._stopPLC()
   328             self._stopPLC()
   265             self.PythonThread.join()
   329             self.PythonThread.join()
   266             self._FreePLC()
       
   267             return True
   330             return True
   268         return False
   331         return False
   269 
   332 
   270     def _Reload(self):
   333     def _Reload(self):
   271         self.daemon.shutdown(True)
   334         self.daemon.shutdown(True)
   278         # respawn python interpreter
   341         # respawn python interpreter
   279         Timer(0.1,self._Reload).start()
   342         Timer(0.1,self._Reload).start()
   280         return True
   343         return True
   281 
   344 
   282     def GetPLCstatus(self):
   345     def GetPLCstatus(self):
   283         return self.PLCStatus
   346         return self.PLCStatus, map(self.GetLogCount,xrange(LogLevelsCount))
   284     
   347     
   285     def NewPLC(self, md5sum, data, extrafiles):
   348     def NewPLC(self, md5sum, data, extrafiles):
   286         PLCprint("NewPLC (%s)"%md5sum)
   349         self.LogMessage("NewPLC (%s)"%md5sum)
   287         if self.PLCStatus in ["Stopped", "Empty", "Broken"]:
   350         if self.PLCStatus in ["Stopped", "Empty", "Broken"]:
   288             NewFileName = md5sum + lib_ext
   351             NewFileName = md5sum + lib_ext
   289             extra_files_log = os.path.join(self.workingdir,"extra_files.txt")
   352             extra_files_log = os.path.join(self.workingdir,"extra_files.txt")
       
   353 
       
   354             self._FreePLC()
       
   355             self.PLCStatus = "Empty"
       
   356 
   290             try:
   357             try:
   291                 os.remove(os.path.join(self.workingdir,
   358                 os.remove(os.path.join(self.workingdir,
   292                                        self.CurrentPLCFilename))
   359                                        self.CurrentPLCFilename))
   293                 for filename in file(extra_files_log, "r").readlines() + [extra_files_log]:
   360                 for filename in file(extra_files_log, "r").readlines() + [extra_files_log]:
   294                     try:
   361                     try:
   314                     log.write(fname+'\n')
   381                     log.write(fname+'\n')
   315 
   382 
   316                 # Store new PLC filename
   383                 # Store new PLC filename
   317                 self.CurrentPLCFilename = NewFileName
   384                 self.CurrentPLCFilename = NewFileName
   318             except:
   385             except:
       
   386                 self.PLCStatus = "Broken"
       
   387                 self.StatusChange()
   319                 PLCprint(traceback.format_exc())
   388                 PLCprint(traceback.format_exc())
   320                 return False
   389                 return False
   321             if self.PLCStatus == "Empty":
   390 
       
   391             if self._LoadNewPLC():
   322                 self.PLCStatus = "Stopped"
   392                 self.PLCStatus = "Stopped"
   323             return True
   393             else:
       
   394                 self._FreePLC()
       
   395             self.StatusChange()
       
   396 
       
   397             return self.PLCStatus == "Stopped"
   324         return False
   398         return False
   325 
   399 
   326     def MatchMD5(self, MD5):
   400     def MatchMD5(self, MD5):
   327         try:
   401         try:
   328             last_md5 = open(self._GetMD5FileName(), "r").read()
   402             last_md5 = open(self._GetMD5FileName(), "r").read()