andrej@1731: #!/usr/bin/env python
andrej@1731: # -*- coding: utf-8 -*-
andrej@1731: 
andrej@1731: # This file is part of Beremiz, a Integrated Development Environment for
andrej@1731: # programming IEC 61131-3 automates supporting plcopen standard and CanFestival.
andrej@1731: #
andrej@1731: # Copyright (C) 2007: Edouard TISSERANT and Laurent BESSARD
andrej@1731: # Copyright (C) 2017: Andrey Skvortsov
andrej@1731: #
andrej@1731: # See COPYING file for copyrights details.
andrej@1731: #
andrej@1731: # This program is free software; you can redistribute it and/or
andrej@1731: # modify it under the terms of the GNU General Public License
andrej@1731: # as published by the Free Software Foundation; either version 2
andrej@1731: # of the License, or (at your option) any later version.
andrej@1731: #
andrej@1731: # This program is distributed in the hope that it will be useful,
andrej@1731: # but WITHOUT ANY WARRANTY; without even the implied warranty of
andrej@1731: # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
andrej@1731: # GNU General Public License for more details.
andrej@1731: #
andrej@1731: # You should have received a copy of the GNU General Public License
andrej@1731: # along with this program; if not, write to the Free Software
andrej@1731: # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
andrej@1731: 
andrej@1731: # Package initialisation
andrej@1731: 
andrej@1881: 
andrej@1881: from __future__ import absolute_import
andrej@1731: from os import listdir, path
Edouard@2463: from connectors.ConnectorBase import ConnectorBase
andrej@1731: 
edouard@2492: connectors_packages = ["PYRO", "WAMP"]
Edouard@2329: 
andrej@1731: 
andrej@1731: def _GetLocalConnectorClassFactory(name):
andrej@1731:     return lambda: getattr(__import__(name, globals(), locals()), name + "_connector_factory")
andrej@1731: 
andrej@2182: 
Edouard@2329: connectors = {name: _GetLocalConnectorClassFactory(name)
Edouard@2329:               for name in connectors_packages}
denis@2007: 
Edouard@2329: _dialogs_imported = False
Edouard@2329: per_URI_connectors = None
edouard@2492: schemes = None
edouard@2492: 
andrej@2182: 
Edouard@2329: # lazy import of connectors dialogs, only if used
Edouard@2329: def _Import_Dialogs():
Edouard@2329:     global per_URI_connectors, schemes, _dialogs_imported
edouard@2492:     if not _dialogs_imported:
Edouard@2329:         _dialogs_imported = True
Edouard@2329:         per_URI_connectors = {}
Edouard@2329:         schemes = []
Edouard@2329:         for con_name in connectors_packages:
edouard@2492:             module = __import__(con_name + '_dialog', globals(), locals())
denis@2007: 
Edouard@2329:             for scheme in module.Schemes:
Edouard@2329:                 per_URI_connectors[scheme] = getattr(module, con_name + '_dialog')
Edouard@2329:                 schemes += [scheme]
denis@2007: 
andrej@1731: 
andrej@1731: def ConnectorFactory(uri, confnodesroot):
andrej@1731:     """
andrej@1731:     Return a connector corresponding to the URI
andrej@1731:     or None if cannot connect to URI
andrej@1731:     """
Edouard@2463:     _scheme = uri.split("://")[0].upper()
Edouard@2478: 
Edouard@2478:     # commented code to enable for MDNS:// support
Edouard@2478:     # _scheme, location = uri.split("://")
Edouard@2478:     # _scheme = _scheme.upper()
Edouard@2478: 
Edouard@2463:     if _scheme == "LOCAL":
andrej@1731:         # Local is special case
andrej@1731:         # pyro connection to local runtime
andrej@1731:         # started on demand, listening on random port
Edouard@2338:         scheme = "PYRO"
andrej@1731:         runtime_port = confnodesroot.AppFrame.StartLocalRuntime(
andrej@1731:             taskbaricon=True)
andrej@1731:         uri = "PYROLOC://127.0.0.1:" + str(runtime_port)
Edouard@2478: 
Edouard@2478:     # commented code to enable for MDNS:// support
Edouard@2478:     # elif _scheme == "MDNS":
Edouard@2478:     #     try:
Edouard@2478:     #         from zeroconf import Zeroconf
Edouard@2478:     #         r = Zeroconf()
Edouard@2478:     #         i = r.get_service_info(zeroconf_service_type, location)
Edouard@2478:     #         if i is None:
Edouard@2478:     #             raise Exception("'%s' not found" % location)
Edouard@2478:     #         ip = str(socket.inet_ntoa(i.address))
Edouard@2478:     #         port = str(i.port)
Edouard@2478:     #         newlocation = ip + ':' + port
Edouard@2478:     #         confnodesroot.logger.write(_("'{a1}' is located at {a2}\n").format(a1=location, a2=newlocation))
Edouard@2478:     #         location = newlocation
Edouard@2478:     #         # not a bug, but a workaround against obvious downgrade attack
Edouard@2478:     #         scheme = "PYROS"
Edouard@2478:     #         r.close()
Edouard@2478:     #     except Exception:
Edouard@2478:     #         confnodesroot.logger.write_error(_("MDNS resolution failure for '%s'\n") % location)
Edouard@2478:     #         confnodesroot.logger.write_error(traceback.format_exc())
Edouard@2478:     #         return None
Edouard@2478: 
Edouard@2463:     elif _scheme in connectors:
Edouard@2463:         scheme = _scheme
Edouard@2463:     elif _scheme[-1] == 'S' and _scheme[:-1] in connectors:
Edouard@2463:         scheme = _scheme[:-1]
andrej@1731:     else:
andrej@1731:         return None
andrej@1731: 
Edouard@2463:     # import module according to uri type and get connector specific baseclass
edouard@2492:     # first call to import the module,
Edouard@2463:     # then call with parameters to create the class
Edouard@2463:     connector_specific_class = connectors[scheme]()(uri, confnodesroot)
edouard@2492: 
Edouard@2469:     if connector_specific_class is None:
Edouard@2469:         return None
denis@2007: 
Edouard@2463:     # new class inheriting from generic and specific connector base classes
andrej@2537:     return type(_scheme + "_connector",
andrej@2537:                 (ConnectorBase, connector_specific_class), {})()
andrej@2182: 
edouard@2492: 
Edouard@2329: def EditorClassFromScheme(scheme):
Edouard@2329:     _Import_Dialogs()
edouard@2492:     return per_URI_connectors.get(scheme, None)
edouard@2492: 
denis@2007: 
Edouard@2329: def ConnectorSchemes():
Edouard@2329:     _Import_Dialogs()
Edouard@2329:     return schemes