runtime/PLCObject.py
changeset 1740 b789b695b5c6
parent 1739 ec153828ded2
child 1741 dd94b9a68c61
equal deleted inserted replaced
1739:ec153828ded2 1740:b789b695b5c6
    45     while tb.tb_next:
    45     while tb.tb_next:
    46         tb = tb.tb_next
    46         tb = tb.tb_next
    47     return tb
    47     return tb
    48 
    48 
    49 lib_ext ={
    49 lib_ext ={
    50      "linux2":".so",
    50      "linux2": ".so",
    51      "win32":".dll",
    51      "win32":  ".dll",
    52      }.get(sys.platform, "")
    52      }.get(sys.platform, "")
    53 
    53 
    54 
    54 
    55 def PLCprint(message):
    55 def PLCprint(message):
    56     sys.stdout.write("PLCobject : "+message+"\n")
    56     sys.stdout.write("PLCobject : "+message+"\n")
   126                 ctypes.byref(tick),
   126                 ctypes.byref(tick),
   127                 ctypes.byref(tv_sec),
   127                 ctypes.byref(tv_sec),
   128                 ctypes.byref(tv_nsec))
   128                 ctypes.byref(tv_nsec))
   129             if sz and sz <= maxsz:
   129             if sz and sz <= maxsz:
   130                 self._log_read_buffer[sz] = '\x00'
   130                 self._log_read_buffer[sz] = '\x00'
   131                 return self._log_read_buffer.value,tick.value,tv_sec.value,tv_nsec.value
   131                 return self._log_read_buffer.value, tick.value, tv_sec.value, tv_nsec.value
   132         elif self._loading_error is not None and level==0:
   132         elif self._loading_error is not None and level==0:
   133             return self._loading_error,0,0,0
   133             return self._loading_error, 0, 0, 0
   134         return None
   134         return None
   135 
   135 
   136     def _GetMD5FileName(self):
   136     def _GetMD5FileName(self):
   137         return os.path.join(self.workingdir, "lasttransferedPLC.md5")
   137         return os.path.join(self.workingdir, "lasttransferedPLC.md5")
   138 
   138 
   139     def _GetLibFileName(self):
   139     def _GetLibFileName(self):
   140         return os.path.join(self.workingdir,self.CurrentPLCFilename)
   140         return os.path.join(self.workingdir, self.CurrentPLCFilename)
   141 
   141 
   142 
   142 
   143     def LoadPLC(self):
   143     def LoadPLC(self):
   144         """
   144         """
   145         Load PLC library
   145         Load PLC library
   239         Unload PLC library.
   239         Unload PLC library.
   240         This is also called by __init__ to create dummy C func proxies
   240         This is also called by __init__ to create dummy C func proxies
   241         """
   241         """
   242         self.PLClibraryLock.acquire()
   242         self.PLClibraryLock.acquire()
   243         # Forget all refs to library
   243         # Forget all refs to library
   244         self._startPLC = lambda x,y:None
   244         self._startPLC = lambda x, y: None
   245         self._stopPLC = lambda:None
   245         self._stopPLC = lambda: None
   246         self._ResetDebugVariables = lambda:None
   246         self._ResetDebugVariables = lambda: None
   247         self._RegisterDebugVariable = lambda x, y:None
   247         self._RegisterDebugVariable = lambda x, y: None
   248         self._IterDebugData = lambda x,y:None
   248         self._IterDebugData = lambda x, y: None
   249         self._FreeDebugData = lambda:None
   249         self._FreeDebugData = lambda: None
   250         self._GetDebugData = lambda:-1
   250         self._GetDebugData = lambda: -1
   251         self._suspendDebug = lambda x:-1
   251         self._suspendDebug = lambda x: -1
   252         self._resumeDebug = lambda:None
   252         self._resumeDebug = lambda: None
   253         self._PythonIterator = lambda:""
   253         self._PythonIterator = lambda: ""
   254         self._GetLogCount = None
   254         self._GetLogCount = None
   255         self._LogMessage = lambda l,m,s:PLCprint("OFF LOG :"+m)
   255         self._LogMessage = lambda l, m, s: PLCprint("OFF LOG :"+m)
   256         self._GetLogMessage = None
   256         self._GetLogMessage = None
   257         self.PLClibraryHandle = None
   257         self.PLClibraryHandle = None
   258         # Unload library explicitely
   258         # Unload library explicitely
   259         if getattr(self,"_PLClibraryHandle",None) is not None:
   259         if getattr(self, "_PLClibraryHandle", None) is not None:
   260             dlclose(self._PLClibraryHandle)
   260             dlclose(self._PLClibraryHandle)
   261             self._PLClibraryHandle = None
   261             self._PLClibraryHandle = None
   262 
   262 
   263         self.PLClibraryLock.release()
   263         self.PLClibraryLock.release()
   264         return False
   264         return False
   267         """
   267         """
   268         Calls init, start, stop or cleanup method provided by
   268         Calls init, start, stop or cleanup method provided by
   269         runtime python files, loaded when new PLC uploaded
   269         runtime python files, loaded when new PLC uploaded
   270         """
   270         """
   271         for method in self.python_runtime_vars.get("_runtime_%s" % methodname, []):
   271         for method in self.python_runtime_vars.get("_runtime_%s" % methodname, []):
   272             res,exp = self.evaluator(method)
   272             res, exp = self.evaluator(method)
   273             if exp is not None:
   273             if exp is not None:
   274                 self.LogMessage(0,'\n'.join(traceback.format_exception(*exp)))
   274                 self.LogMessage(0, '\n'.join(traceback.format_exception(*exp)))
   275 
   275 
   276     def PythonRuntimeInit(self):
   276     def PythonRuntimeInit(self):
   277         MethodNames = ["init", "start", "stop", "cleanup"]
   277         MethodNames = ["init", "start", "stop", "cleanup"]
   278         self.python_runtime_vars = globals().copy()
   278         self.python_runtime_vars = globals().copy()
   279         self.python_runtime_vars.update(self.pyruntimevars)
   279         self.python_runtime_vars.update(self.pyruntimevars)
   290             def __setattr__(_self, name, value):
   290             def __setattr__(_self, name, value):
   291                 try:
   291                 try:
   292                     t = self.python_runtime_vars["_"+name+"_ctype"]
   292                     t = self.python_runtime_vars["_"+name+"_ctype"]
   293                 except KeyError:
   293                 except KeyError:
   294                     raise KeyError("Try to set unknown shared global variable : %s" % name)
   294                     raise KeyError("Try to set unknown shared global variable : %s" % name)
   295                 v = self.python_runtime_vars["_"+name+"_pack"](t,value)
   295                 v = self.python_runtime_vars["_"+name+"_pack"](t, value)
   296                 self.python_runtime_vars["_PySafeSetPLCGlob_"+name](ctypes.byref(v))
   296                 self.python_runtime_vars["_PySafeSetPLCGlob_"+name](ctypes.byref(v))
   297 
   297 
   298         self.python_runtime_vars.update({
   298         self.python_runtime_vars.update({
   299             "PLCGlobals":     PLCSafeGlobals(),
   299             "PLCGlobals":     PLCSafeGlobals(),
   300             "WorkingDir":     self.workingdir,
   300             "WorkingDir":     self.workingdir,
   315                     for methodname in MethodNames:
   315                     for methodname in MethodNames:
   316                         method = self.python_runtime_vars.get("_%s_%s" % (name, methodname), None)
   316                         method = self.python_runtime_vars.get("_%s_%s" % (name, methodname), None)
   317                         if method is not None:
   317                         if method is not None:
   318                             self.python_runtime_vars["_runtime_%s" % methodname].append(method)
   318                             self.python_runtime_vars["_runtime_%s" % methodname].append(method)
   319         except:
   319         except:
   320             self.LogMessage(0,traceback.format_exc())
   320             self.LogMessage(0, traceback.format_exc())
   321             raise
   321             raise
   322 
   322 
   323         self.PythonRuntimeCall("init")
   323         self.PythonRuntimeCall("init")
   324 
   324 
   325 
   325 
   330 
   330 
   331         self.python_runtime_vars = None
   331         self.python_runtime_vars = None
   332 
   332 
   333     def PythonThreadProc(self):
   333     def PythonThreadProc(self):
   334         self.StartSem.release()
   334         self.StartSem.release()
   335         res,cmd,blkid = "None","None",ctypes.c_void_p()
   335         res, cmd, blkid = "None", "None", ctypes.c_void_p()
   336         compile_cache={}
   336         compile_cache={}
   337         while True:
   337         while True:
   338             # print "_PythonIterator(", res, ")",
   338             # print "_PythonIterator(", res, ")",
   339             cmd = self._PythonIterator(res,blkid)
   339             cmd = self._PythonIterator(res, blkid)
   340             FBID = blkid.value
   340             FBID = blkid.value
   341             # print " -> ", cmd, blkid
   341             # print " -> ", cmd, blkid
   342             if cmd is None:
   342             if cmd is None:
   343                 break
   343                 break
   344             try:
   344             try:
   345                 self.python_runtime_vars["FBID"]=FBID
   345                 self.python_runtime_vars["FBID"]=FBID
   346                 ccmd,AST =compile_cache.get(FBID, (None,None))
   346                 ccmd, AST =compile_cache.get(FBID, (None, None))
   347                 if ccmd is None or ccmd!=cmd:
   347                 if ccmd is None or ccmd!=cmd:
   348                     AST = compile(cmd, '<plc>', 'eval')
   348                     AST = compile(cmd, '<plc>', 'eval')
   349                     compile_cache[FBID]=(cmd,AST)
   349                     compile_cache[FBID]=(cmd, AST)
   350                 result,exp = self.evaluator(eval,AST,self.python_runtime_vars)
   350                 result, exp = self.evaluator(eval, AST, self.python_runtime_vars)
   351                 if exp is not None:
   351                 if exp is not None:
   352                     res = "#EXCEPTION : "+str(exp[1])
   352                     res = "#EXCEPTION : "+str(exp[1])
   353                     self.LogMessage(1,('PyEval@0x%x(Code="%s") Exception "%s"') % (FBID,cmd,
   353                     self.LogMessage(1, ('PyEval@0x%x(Code="%s") Exception "%s"') % (FBID, cmd,
   354                         '\n'.join(traceback.format_exception(*exp))))
   354                         '\n'.join(traceback.format_exception(*exp))))
   355                 else:
   355                 else:
   356                     res=str(result)
   356                     res=str(result)
   357                 self.python_runtime_vars["FBID"]=None
   357                 self.python_runtime_vars["FBID"]=None
   358             except Exception,e:
   358             except Exception, e:
   359                 res = "#EXCEPTION : "+str(e)
   359                 res = "#EXCEPTION : "+str(e)
   360                 self.LogMessage(1,('PyEval@0x%x(Code="%s") Exception "%s"') % (FBID,cmd,str(e)))
   360                 self.LogMessage(1, ('PyEval@0x%x(Code="%s") Exception "%s"') % (FBID, cmd, str(e)))
   361 
   361 
   362     def StartPLC(self):
   362     def StartPLC(self):
   363         if self.CurrentPLCFilename is not None and self.PLCStatus == "Stopped":
   363         if self.CurrentPLCFilename is not None and self.PLCStatus == "Stopped":
   364             c_argv = ctypes.c_char_p * len(self.argv)
   364             c_argv = ctypes.c_char_p * len(self.argv)
   365             error = None
   365             error = None
   366             res = self._startPLC(len(self.argv),c_argv(*self.argv))
   366             res = self._startPLC(len(self.argv), c_argv(*self.argv))
   367             if res == 0:
   367             if res == 0:
   368                 self.PLCStatus = "Started"
   368                 self.PLCStatus = "Started"
   369                 self.StatusChange()
   369                 self.StatusChange()
   370                 self.PythonRuntimeCall("start")
   370                 self.PythonRuntimeCall("start")
   371                 self.StartSem=Semaphore(0)
   371                 self.StartSem=Semaphore(0)
   372                 self.PythonThread = Thread(target=self.PythonThreadProc)
   372                 self.PythonThread = Thread(target=self.PythonThreadProc)
   373                 self.PythonThread.start()
   373                 self.PythonThread.start()
   374                 self.StartSem.acquire()
   374                 self.StartSem.acquire()
   375                 self.LogMessage("PLC started")
   375                 self.LogMessage("PLC started")
   376             else:
   376             else:
   377                 self.LogMessage(0,_("Problem starting PLC : error %d" % res))
   377                 self.LogMessage(0, _("Problem starting PLC : error %d" % res))
   378                 self.PLCStatus = "Broken"
   378                 self.PLCStatus = "Broken"
   379                 self.StatusChange()
   379                 self.StatusChange()
   380 
   380 
   381     def StopPLC(self):
   381     def StopPLC(self):
   382         if self.PLCStatus == "Started":
   382         if self.PLCStatus == "Started":
   394         return False
   394         return False
   395 
   395 
   396     def _Reload(self):
   396     def _Reload(self):
   397         self.daemon.shutdown(True)
   397         self.daemon.shutdown(True)
   398         self.daemon.sock.close()
   398         self.daemon.sock.close()
   399         os.execv(sys.executable,[sys.executable]+sys.argv[:])
   399         os.execv(sys.executable, [sys.executable]+sys.argv[:])
   400         # never reached
   400         # never reached
   401         return 0
   401         return 0
   402 
   402 
   403     def ForceReload(self):
   403     def ForceReload(self):
   404         # respawn python interpreter
   404         # respawn python interpreter
   405         Timer(0.1,self._Reload).start()
   405         Timer(0.1, self._Reload).start()
   406         return True
   406         return True
   407 
   407 
   408     def GetPLCstatus(self):
   408     def GetPLCstatus(self):
   409         return self.PLCStatus, map(self.GetLogCount,xrange(LogLevelsCount))
   409         return self.PLCStatus, map(self.GetLogCount, xrange(LogLevelsCount))
   410 
   410 
   411     def NewPLC(self, md5sum, data, extrafiles):
   411     def NewPLC(self, md5sum, data, extrafiles):
   412         if self.PLCStatus in ["Stopped", "Empty", "Broken"]:
   412         if self.PLCStatus in ["Stopped", "Empty", "Broken"]:
   413             NewFileName = md5sum + lib_ext
   413             NewFileName = md5sum + lib_ext
   414             extra_files_log = os.path.join(self.workingdir,"extra_files.txt")
   414             extra_files_log = os.path.join(self.workingdir, "extra_files.txt")
   415 
   415 
   416             self.UnLoadPLC()
   416             self.UnLoadPLC()
   417 
   417 
   418             self.LogMessage("NewPLC (%s)" % md5sum)
   418             self.LogMessage("NewPLC (%s)" % md5sum)
   419             self.PLCStatus = "Empty"
   419             self.PLCStatus = "Empty"
   429             except:
   429             except:
   430                 pass
   430                 pass
   431 
   431 
   432             try:
   432             try:
   433                 # Create new PLC file
   433                 # Create new PLC file
   434                 open(os.path.join(self.workingdir,NewFileName),
   434                 open(os.path.join(self.workingdir, NewFileName),
   435                      'wb').write(data)
   435                      'wb').write(data)
   436 
   436 
   437                 # Store new PLC filename based on md5 key
   437                 # Store new PLC filename based on md5 key
   438                 open(self._GetMD5FileName(), "w").write(md5sum)
   438                 open(self._GetMD5FileName(), "w").write(md5sum)
   439 
   439 
   440                 # Then write the files
   440                 # Then write the files
   441                 log = file(extra_files_log, "w")
   441                 log = file(extra_files_log, "w")
   442                 for fname,fdata in extrafiles:
   442                 for fname, fdata in extrafiles:
   443                     fpath = os.path.join(self.workingdir,fname)
   443                     fpath = os.path.join(self.workingdir, fname)
   444                     open(fpath, "wb").write(fdata)
   444                     open(fpath, "wb").write(fdata)
   445                     log.write(fname+'\n')
   445                     log.write(fname+'\n')
   446 
   446 
   447                 # Store new PLC filename
   447                 # Store new PLC filename
   448                 self.CurrentPLCFilename = NewFileName
   448                 self.CurrentPLCFilename = NewFileName
   478         if idxs:
   478         if idxs:
   479             # suspend but dont disable
   479             # suspend but dont disable
   480             if self._suspendDebug(False) == 0:
   480             if self._suspendDebug(False) == 0:
   481                 # keep a copy of requested idx
   481                 # keep a copy of requested idx
   482                 self._ResetDebugVariables()
   482                 self._ResetDebugVariables()
   483                 for idx,iectype,force in idxs:
   483                 for idx, iectype, force in idxs:
   484                     if force !=None:
   484                     if force !=None:
   485                         c_type,unpack_func, pack_func = \
   485                         c_type, unpack_func, pack_func = \
   486                             TypeTranslator.get(iectype,
   486                             TypeTranslator.get(iectype,
   487                                                     (None,None,None))
   487                                                     (None, None, None))
   488                         force = ctypes.byref(pack_func(c_type,force))
   488                         force = ctypes.byref(pack_func(c_type, force))
   489                     self._RegisterDebugVariable(idx, force)
   489                     self._RegisterDebugVariable(idx, force)
   490                 self._TracesSwap()
   490                 self._TracesSwap()
   491                 self._resumeDebug()
   491                 self._resumeDebug()
   492         else:
   492         else:
   493             self._suspendDebug(True)
   493             self._suspendDebug(True)