diff -r 31e63e25b4cc -r 64beb9e9c749 connectors/PYRO/__init__.py --- a/connectors/PYRO/__init__.py Mon Aug 21 20:17:19 2017 +0000 +++ b/connectors/PYRO/__init__.py Mon Aug 21 23:22:58 2017 +0300 @@ -1,199 +1,202 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- - -# This file is part of Beremiz, a Integrated Development Environment for -# programming IEC 61131-3 automates supporting plcopen standard and CanFestival. -# -# Copyright (C) 2007: Edouard TISSERANT and Laurent BESSARD -# -# See COPYING file for copyrights details. -# -# This program is free software; you can redistribute it and/or -# modify it under the terms of the GNU General Public License -# as published by the Free Software Foundation; either version 2 -# of the License, or (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - -import Pyro -import Pyro.core -import Pyro.util -from Pyro.errors import PyroError -import traceback -from time import sleep -import copy -import socket -service_type = '_PYRO._tcp.local.' -import os.path -# this module attribute contains a list of DNS-SD (Zeroconf) service types -# supported by this connector confnode. -# -# for connectors that do not support DNS-SD, this attribute can be omitted -# or set to an empty list. - -def PYRO_connector_factory(uri, confnodesroot): - """ - This returns the connector to Pyro style PLCobject - """ - confnodesroot.logger.write(_("PYRO connecting to URI : %s\n") % uri) - - servicetype, location = uri.split("://") - if servicetype == "PYROS": - schemename = "PYROLOCSSL" - # Protect against name->IP substitution in Pyro3 - Pyro.config.PYRO_DNS_URI = True - # Beware Pyro lib need str path, not unicode - # don't rely on PYRO_STORAGE ! see documentation - Pyro.config.PYROSSL_CERTDIR = os.path.abspath(str(confnodesroot.ProjectPath) + '/certs') - if not os.path.exists(Pyro.config.PYROSSL_CERTDIR): - confnodesroot.logger.write_error( - 'Error : the directory %s is missing for SSL certificates (certs_dir).' - 'Please fix it in your project.\n' % Pyro.config.PYROSSL_CERTDIR) - return None - else: - confnodesroot.logger.write(_("PYRO using certificates in '%s' \n") - % (Pyro.config.PYROSSL_CERTDIR)) - Pyro.config.PYROSSL_CERT = "client.crt" - Pyro.config.PYROSSL_KEY = "client.key" - # Ugly Monkey Patching - def _gettimeout(self): - return self.timeout - - def _settimeout(self, timeout): - self.timeout = timeout - from M2Crypto.SSL import Connection - Connection.timeout = None - Connection.gettimeout = _gettimeout - Connection.settimeout = _settimeout - # M2Crypto.SSL.Checker.WrongHost: Peer certificate commonName does not - # match host, expected 127.0.0.1, got server - Connection.clientPostConnectionCheck = None - else: - schemename = "PYROLOC" - if location.find(service_type) != -1: - try: - from util.Zeroconf import Zeroconf - r = Zeroconf() - i = r.getServiceInfo(service_type, location) - if i is None: - raise Exception("'%s' not found" % location) - ip = str(socket.inet_ntoa(i.getAddress())) - port = str(i.getPort()) - newlocation = ip + ':' + port - confnodesroot.logger.write(_("'{a1}' is located at {a2}\n").format(a1 = location, a2 = newlocation)) - location = newlocation - r.close() - except Exception, msg: - confnodesroot.logger.write_error(_("MDNS resolution failure for '%s'\n") % location) - confnodesroot.logger.write_error(traceback.format_exc()) - return None - - # Try to get the proxy object - try: - RemotePLCObjectProxy = Pyro.core.getAttrProxyForURI(schemename + "://" + location + "/PLCObject") - except Exception, msg: - confnodesroot.logger.write_error(_("Connection to '%s' failed.\n") % location) - confnodesroot.logger.write_error(traceback.format_exc()) - return None - - def PyroCatcher(func, default=None): - """ - A function that catch a Pyro exceptions, write error to logger - and return default value when it happen - """ - def catcher_func(*args, **kwargs): - try: - return func(*args, **kwargs) - except Pyro.errors.ConnectionClosedError, e: - confnodesroot.logger.write_error(_("Connection lost!\n")) - confnodesroot._SetConnector(None) - except Pyro.errors.ProtocolError, e: - confnodesroot.logger.write_error(_("Pyro exception: %s\n") % e) - except Exception, e: - # confnodesroot.logger.write_error(traceback.format_exc()) - errmess = ''.join(Pyro.util.getPyroTraceback(e)) - confnodesroot.logger.write_error(errmess + "\n") - print errmess - confnodesroot._SetConnector(None) - return default - return catcher_func - - # Check connection is effective. - # lambda is for getattr of GetPLCstatus to happen inside catcher - if PyroCatcher(lambda: RemotePLCObjectProxy.GetPLCstatus())() is None: - confnodesroot.logger.write_error(_("Cannot get PLC status - connection failed.\n")) - return None - - class PyroProxyProxy(object): - """ - A proxy proxy class to handle Beremiz Pyro interface specific behavior. - And to put Pyro exception catcher in between caller and Pyro proxy - """ - def __init__(self): - # for safe use in from debug thread, must create a copy - self.RemotePLCObjectProxyCopy = None - - def GetPyroProxy(self): - """ - This func returns the real Pyro Proxy. - Use this if you musn't keep reference to it. - """ - return RemotePLCObjectProxy - - def _PyroStartPLC(self, *args, **kwargs): - """ - confnodesroot._connector.GetPyroProxy() is used - rather than RemotePLCObjectProxy because - object is recreated meanwhile, - so we must not keep ref to it here - """ - current_status, log_count = confnodesroot._connector.GetPyroProxy().GetPLCstatus() - if current_status == "Dirty": - """ - Some bad libs with static symbols may polute PLC - ask runtime to suicide and come back again - """ - confnodesroot.logger.write(_("Force runtime reload\n")) - confnodesroot._connector.GetPyroProxy().ForceReload() - confnodesroot._Disconnect() - # let remote PLC time to resurect.(freeze app) - sleep(0.5) - confnodesroot._Connect() - self.RemotePLCObjectProxyCopy = copy.copy(confnodesroot._connector.GetPyroProxy()) - return confnodesroot._connector.GetPyroProxy().StartPLC(*args, **kwargs) - StartPLC = PyroCatcher(_PyroStartPLC, False) - - def _PyroGetTraceVariables(self): - """ - for safe use in from debug thread, must use the copy - """ - if self.RemotePLCObjectProxyCopy is None: - self.RemotePLCObjectProxyCopy = copy.copy(confnodesroot._connector.GetPyroProxy()) - return self.RemotePLCObjectProxyCopy.GetTraceVariables() - GetTraceVariables = PyroCatcher(_PyroGetTraceVariables, ("Broken", None)) - - def _PyroGetPLCstatus(self): - return RemotePLCObjectProxy.GetPLCstatus() - GetPLCstatus = PyroCatcher(_PyroGetPLCstatus, ("Broken", None)) - - def _PyroRemoteExec(self, script, **kwargs): - return RemotePLCObjectProxy.RemoteExec(script, **kwargs) - RemoteExec = PyroCatcher(_PyroRemoteExec, (-1, "RemoteExec script failed!")) - - def __getattr__(self, attrName): - member = self.__dict__.get(attrName, None) - if member is None: - def my_local_func(*args, **kwargs): - return RemotePLCObjectProxy.__getattr__(attrName)(*args, **kwargs) - member = PyroCatcher(my_local_func, None) - self.__dict__[attrName] = member - return member - - return PyroProxyProxy() +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +# This file is part of Beremiz, a Integrated Development Environment for +# programming IEC 61131-3 automates supporting plcopen standard and CanFestival. +# +# Copyright (C) 2007: Edouard TISSERANT and Laurent BESSARD +# +# See COPYING file for copyrights details. +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +import Pyro +import Pyro.core +import Pyro.util +from Pyro.errors import PyroError +import traceback +from time import sleep +import copy +import socket +import os.path + +service_type = '_PYRO._tcp.local.' +# this module attribute contains a list of DNS-SD (Zeroconf) service types +# supported by this connector confnode. +# +# for connectors that do not support DNS-SD, this attribute can be omitted +# or set to an empty list. + + +def PYRO_connector_factory(uri, confnodesroot): + """ + This returns the connector to Pyro style PLCobject + """ + confnodesroot.logger.write(_("PYRO connecting to URI : %s\n") % uri) + + servicetype, location = uri.split("://") + if servicetype == "PYROS": + schemename = "PYROLOCSSL" + # Protect against name->IP substitution in Pyro3 + Pyro.config.PYRO_DNS_URI = True + # Beware Pyro lib need str path, not unicode + # don't rely on PYRO_STORAGE ! see documentation + Pyro.config.PYROSSL_CERTDIR = os.path.abspath(str(confnodesroot.ProjectPath) + '/certs') + if not os.path.exists(Pyro.config.PYROSSL_CERTDIR): + confnodesroot.logger.write_error( + 'Error : the directory %s is missing for SSL certificates (certs_dir).' + 'Please fix it in your project.\n' % Pyro.config.PYROSSL_CERTDIR) + return None + else: + confnodesroot.logger.write(_("PYRO using certificates in '%s' \n") + % (Pyro.config.PYROSSL_CERTDIR)) + Pyro.config.PYROSSL_CERT = "client.crt" + Pyro.config.PYROSSL_KEY = "client.key" + + # Ugly Monkey Patching + def _gettimeout(self): + return self.timeout + + def _settimeout(self, timeout): + self.timeout = timeout + from M2Crypto.SSL import Connection + Connection.timeout = None + Connection.gettimeout = _gettimeout + Connection.settimeout = _settimeout + # M2Crypto.SSL.Checker.WrongHost: Peer certificate commonName does not + # match host, expected 127.0.0.1, got server + Connection.clientPostConnectionCheck = None + else: + schemename = "PYROLOC" + if location.find(service_type) != -1: + try: + from util.Zeroconf import Zeroconf + r = Zeroconf() + i = r.getServiceInfo(service_type, location) + if i is None: + raise Exception("'%s' not found" % location) + ip = str(socket.inet_ntoa(i.getAddress())) + port = str(i.getPort()) + newlocation = ip + ':' + port + confnodesroot.logger.write(_("'{a1}' is located at {a2}\n").format(a1=location, a2=newlocation)) + location = newlocation + r.close() + except Exception, msg: + confnodesroot.logger.write_error(_("MDNS resolution failure for '%s'\n") % location) + confnodesroot.logger.write_error(traceback.format_exc()) + return None + + # Try to get the proxy object + try: + RemotePLCObjectProxy = Pyro.core.getAttrProxyForURI(schemename + "://" + location + "/PLCObject") + except Exception, msg: + confnodesroot.logger.write_error(_("Connection to '%s' failed.\n") % location) + confnodesroot.logger.write_error(traceback.format_exc()) + return None + + def PyroCatcher(func, default=None): + """ + A function that catch a Pyro exceptions, write error to logger + and return default value when it happen + """ + def catcher_func(*args, **kwargs): + try: + return func(*args, **kwargs) + except Pyro.errors.ConnectionClosedError, e: + confnodesroot.logger.write_error(_("Connection lost!\n")) + confnodesroot._SetConnector(None) + except Pyro.errors.ProtocolError, e: + confnodesroot.logger.write_error(_("Pyro exception: %s\n") % e) + except Exception, e: + # confnodesroot.logger.write_error(traceback.format_exc()) + errmess = ''.join(Pyro.util.getPyroTraceback(e)) + confnodesroot.logger.write_error(errmess + "\n") + print errmess + confnodesroot._SetConnector(None) + return default + return catcher_func + + # Check connection is effective. + # lambda is for getattr of GetPLCstatus to happen inside catcher + if PyroCatcher(lambda: RemotePLCObjectProxy.GetPLCstatus())() is None: + confnodesroot.logger.write_error(_("Cannot get PLC status - connection failed.\n")) + return None + + class PyroProxyProxy(object): + """ + A proxy proxy class to handle Beremiz Pyro interface specific behavior. + And to put Pyro exception catcher in between caller and Pyro proxy + """ + def __init__(self): + # for safe use in from debug thread, must create a copy + self.RemotePLCObjectProxyCopy = None + + def GetPyroProxy(self): + """ + This func returns the real Pyro Proxy. + Use this if you musn't keep reference to it. + """ + return RemotePLCObjectProxy + + def _PyroStartPLC(self, *args, **kwargs): + """ + confnodesroot._connector.GetPyroProxy() is used + rather than RemotePLCObjectProxy because + object is recreated meanwhile, + so we must not keep ref to it here + """ + current_status, log_count = confnodesroot._connector.GetPyroProxy().GetPLCstatus() + if current_status == "Dirty": + """ + Some bad libs with static symbols may polute PLC + ask runtime to suicide and come back again + """ + confnodesroot.logger.write(_("Force runtime reload\n")) + confnodesroot._connector.GetPyroProxy().ForceReload() + confnodesroot._Disconnect() + # let remote PLC time to resurect.(freeze app) + sleep(0.5) + confnodesroot._Connect() + self.RemotePLCObjectProxyCopy = copy.copy(confnodesroot._connector.GetPyroProxy()) + return confnodesroot._connector.GetPyroProxy().StartPLC(*args, **kwargs) + StartPLC = PyroCatcher(_PyroStartPLC, False) + + def _PyroGetTraceVariables(self): + """ + for safe use in from debug thread, must use the copy + """ + if self.RemotePLCObjectProxyCopy is None: + self.RemotePLCObjectProxyCopy = copy.copy(confnodesroot._connector.GetPyroProxy()) + return self.RemotePLCObjectProxyCopy.GetTraceVariables() + GetTraceVariables = PyroCatcher(_PyroGetTraceVariables, ("Broken", None)) + + def _PyroGetPLCstatus(self): + return RemotePLCObjectProxy.GetPLCstatus() + GetPLCstatus = PyroCatcher(_PyroGetPLCstatus, ("Broken", None)) + + def _PyroRemoteExec(self, script, **kwargs): + return RemotePLCObjectProxy.RemoteExec(script, **kwargs) + RemoteExec = PyroCatcher(_PyroRemoteExec, (-1, "RemoteExec script failed!")) + + def __getattr__(self, attrName): + member = self.__dict__.get(attrName, None) + if member is None: + def my_local_func(*args, **kwargs): + return RemotePLCObjectProxy.__getattr__(attrName)(*args, **kwargs) + member = PyroCatcher(my_local_func, None) + self.__dict__[attrName] = member + return member + + return PyroProxyProxy()