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
edouard@477: import Pyro.util
etisserant@203: import traceback
etisserant@203: from time import sleep
etisserant@231: import copy
etisserant@203: 
laurent@399: # this module attribute contains a list of DNS-SD (Zeroconf) service types
laurent@399: # supported by this connector plugin.
laurent@399: #
laurent@399: # for connectors that do not support DNS-SD, this attribute can be omitted
laurent@399: # or set to an empty list.
laurent@399: supported_dnssd_services = ["_PYRO._tcp.local."]
laurent@399: 
etisserant@203: def PYRO_connector_factory(uri, pluginsroot):
etisserant@203:     """
etisserant@203:     This returns the connector to Pyro style PLCobject
etisserant@203:     """
laurent@361:     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:
laurent@361:         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)
laurent@493:             except Pyro.errors.ProtocolError, e:
laurent@493:                 pass
laurent@486:             except Pyro.errors.ConnectionClosedError, e:
laurent@486:                 pluginsroot.logger.write_error("Connection lost!\n")
laurent@486:                 pluginsroot._connector = None
edouard@477:             except Exception,e:
etisserant@203:                 #pluginsroot.logger.write_error(traceback.format_exc())
edouard@477:                 errmess = ''.join(Pyro.util.getPyroTraceback(e))
edouard@477:                 pluginsroot.logger.write_error(errmess+"\n")
edouard@477:                 print errmess
greg@354:                 pluginsroot._connector = None
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:
laurent@361:         pluginsroot.logger.write_error(_("Cannot get PLC status - connection failed.\n"))
etisserant@203:         return None
etisserant@231: 
etisserant@284: 
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@284:         def __init__(self):
etisserant@284:             # for safe use in from debug thread, must create a copy
etisserant@284:             self.RemotePLCObjectProxyCopy = None
etisserant@284: 
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:             """
edouard@465:             current_status = pluginsroot._connector.GetPyroProxy().GetPLCstatus()
edouard@465:             if current_status == "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:                 """
laurent@361:                 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@284:             self.RemotePLCObjectProxyCopy = copy.copy(pluginsroot._connector.GetPyroProxy())
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:             """
edouard@465:             if self.RemotePLCObjectProxyCopy is None:
edouard@465:                 self.RemotePLCObjectProxyCopy = copy.copy(pluginsroot._connector.GetPyroProxy())
ed@446:             return self.RemotePLCObjectProxyCopy.GetTraceVariables()
ed@446:         GetTraceVariables = PyroCatcher(_PyroGetTraceVariables,("Broken",None,None))
etisserant@231: 
ed@446:         def _PyroGetPLCstatus(self):
ed@446:             return RemotePLCObjectProxy.GetPLCstatus()
ed@446:         GetPLCstatus = PyroCatcher(_PyroGetPLCstatus, "Broken")
ed@446: 
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: