etisserant@203: #!/usr/bin/env python
etisserant@203: # -*- coding: utf-8 -*-
etisserant@203: #
etisserant@203: #Copyright (C) 2007: Edouard TISSERANT and Laurent BESSARD
etisserant@203: #
etisserant@203: #See COPYING file for copyrights details.
etisserant@203: #
etisserant@203: #This library is free software; you can redistribute it and/or
etisserant@203: #modify it under the terms of the GNU General Public
etisserant@203: #License as published by the Free Software Foundation; either
etisserant@203: #version 2.1 of the License, or (at your option) any later version.
etisserant@203: #
etisserant@203: #This library is distributed in the hope that it will be useful,
etisserant@203: #but WITHOUT ANY WARRANTY; without even the implied warranty of
etisserant@203: #MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
etisserant@203: #General Public License for more details.
etisserant@203: #
etisserant@203: #You should have received a copy of the GNU General Public
etisserant@203: #License along with this library; if not, write to the Free Software
etisserant@203: #Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
etisserant@203: import Pyro.core as pyro
etisserant@203: from Pyro.errors import PyroError
etisserant@203: import traceback
etisserant@203: from time import sleep
etisserant@231: import copy
etisserant@203: 
etisserant@203: def PYRO_connector_factory(uri, pluginsroot):
etisserant@203:     """
etisserant@203:     This returns the connector to Pyro style PLCobject
etisserant@203:     """
etisserant@203:     pluginsroot.logger.write("Connecting to URI : %s\n"%uri)
etisserant@203: 
etisserant@203:     servicetype, location = uri.split("://")
etisserant@203:     
etisserant@203:     # Try to get the proxy object
etisserant@203:     try :
etisserant@203:         RemotePLCObjectProxy = pyro.getAttrProxyForURI("PYROLOC://"+location+"/PLCObject")
etisserant@203:     except Exception, msg:
etisserant@203:         pluginsroot.logger.write_error("Wrong URI, please check it !\n")
etisserant@203:         pluginsroot.logger.write_error(traceback.format_exc())
etisserant@203:         return None
etisserant@203: 
etisserant@203:     def PyroCatcher(func, default=None):
etisserant@203:         """
etisserant@203:         A function that catch a pyro exceptions, write error to logger
etisserant@203:         and return defaul value when it happen
etisserant@203:         """
greg@218:         def catcher_func(*args,**kwargs):
etisserant@203:             try:
etisserant@203:                 return func(*args,**kwargs)
etisserant@203:             except PyroError,e:
etisserant@203:                 #pluginsroot.logger.write_error(traceback.format_exc())
etisserant@203:                 pluginsroot.logger.write_error(str(e))
etisserant@203:                 pluginsroot._Disconnect()
etisserant@203:                 return default
greg@218:         return catcher_func
etisserant@203: 
etisserant@203:     # Check connection is effective. 
etisserant@203:     # lambda is for getattr of GetPLCstatus to happen inside catcher
etisserant@203:     if PyroCatcher(lambda:RemotePLCObjectProxy.GetPLCstatus())() == None:
etisserant@203:         pluginsroot.logger.write_error("Cannot get PLC status - connection failed.\n")
etisserant@203:         return None
etisserant@231: 
etisserant@231:     # for safe use in from debug thread, must create a copy
etisserant@231:     RemotePLCObjectProxyCopy = copy.copy(RemotePLCObjectProxy)
etisserant@203:         
etisserant@203:     class PyroProxyProxy:
etisserant@203:         """
etisserant@203:         A proxy proxy class to handle Beremiz Pyro interface specific behavior.
etisserant@203:         And to put pyro exception catcher in between caller and pyro proxy
etisserant@203:         """
etisserant@203:         def GetPyroProxy(self):
etisserant@203:             """
etisserant@203:             This func returns the real Pyro Proxy.
etisserant@203:             Use this if you musn't keep reference to it.
etisserant@203:             """
etisserant@203:             return RemotePLCObjectProxy
etisserant@231: 
etisserant@235:         def _PyroStartPLC(self, *args, **kwargs):
etisserant@231:             """
etisserant@231:             pluginsroot._connector.GetPyroProxy() is used 
etisserant@231:             rather than RemotePLCObjectProxy because
etisserant@231:             object is recreated meanwhile, 
etisserant@231:             so we must not keep ref to it here
etisserant@231:             """
etisserant@231:             if pluginsroot._connector.GetPyroProxy().GetPLCstatus() == "Dirty":
etisserant@231:                 """
etisserant@231:                 Some bad libs with static symbols may polute PLC
etisserant@231:                 ask runtime to suicide and come back again
etisserant@231:                 """
etisserant@231:                 pluginsroot.logger.write("Force runtime reload\n")
etisserant@231:                 pluginsroot._connector.GetPyroProxy().ForceReload()
etisserant@231:                 pluginsroot._Disconnect()
etisserant@231:                 # let remote PLC time to resurect.(freeze app)
etisserant@231:                 sleep(0.5)
etisserant@231:                 pluginsroot._Connect()
etisserant@235:             return pluginsroot._connector.GetPyroProxy().StartPLC(*args, **kwargs)
etisserant@231:         StartPLC = PyroCatcher(_PyroStartPLC, False)
etisserant@231: 
etisserant@231: 
etisserant@231:         def _PyroGetTraceVariables(self):
etisserant@231:             """
etisserant@231:             for safe use in from debug thread, must use the copy
etisserant@231:             """
etisserant@235:             if RemotePLCObjectProxyCopy.GetPLCstatus() == "Started":
etisserant@235:                 return RemotePLCObjectProxyCopy.GetTraceVariables()
etisserant@235:             else:
etisserant@235:                 return None,None
etisserant@231:         GetTraceVariables = PyroCatcher(_PyroGetTraceVariables)
etisserant@231: 
etisserant@203:         
etisserant@203:         def __getattr__(self, attrName):
etisserant@231:             member = self.__dict__.get(attrName, None)
etisserant@231:             if member is None:
etisserant@231:                 def my_local_func(*args,**kwargs):
etisserant@231:                     return RemotePLCObjectProxy.__getattr__(attrName)(*args,**kwargs)
etisserant@231:                 member = PyroCatcher(my_local_func, None)
etisserant@203:                 self.__dict__[attrName] = member
etisserant@231:             return member
etisserant@231: 
etisserant@203:     return PyroProxyProxy()
etisserant@203:     
etisserant@203: