1 #!/usr/bin/env python |
|
2 # -*- coding: utf-8 -*- |
|
3 |
|
4 # This file is part of Beremiz, a Integrated Development Environment for |
|
5 # programming IEC 61131-3 automates supporting plcopen standard and CanFestival. |
|
6 # |
|
7 # Copyright (C) 2007: Edouard TISSERANT and Laurent BESSARD |
|
8 # |
|
9 # See COPYING file for copyrights details. |
|
10 # |
|
11 # This program is free software; you can redistribute it and/or |
|
12 # modify it under the terms of the GNU General Public License |
|
13 # as published by the Free Software Foundation; either version 2 |
|
14 # of the License, or (at your option) any later version. |
|
15 # |
|
16 # This program is distributed in the hope that it will be useful, |
|
17 # but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
18 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
19 # GNU General Public License for more details. |
|
20 # |
|
21 # You should have received a copy of the GNU General Public License |
|
22 # along with this program; if not, write to the Free Software |
|
23 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. |
|
24 |
|
25 |
|
26 from time import sleep |
|
27 import copy |
|
28 import socket |
|
29 import os.path |
|
30 |
|
31 import Pyro5 |
|
32 import Pyro5.client |
|
33 import Pyro5.errors |
|
34 |
|
35 # TODO: PSK |
|
36 |
|
37 import importlib |
|
38 |
|
39 |
|
40 Pyro5.config.SERIALIZER = "msgpack" |
|
41 |
|
42 |
|
43 def PYRO_connector_factory(uri, confnodesroot): |
|
44 """ |
|
45 This returns the connector to Pyro style PLCobject |
|
46 """ |
|
47 confnodesroot.logger.write(_("PYRO connecting to URI : %s\n") % uri) |
|
48 |
|
49 scheme, location = uri.split("://") |
|
50 |
|
51 # TODO: use ssl |
|
52 |
|
53 schemename = "PYRO" |
|
54 |
|
55 # Try to get the proxy object |
|
56 try: |
|
57 RemotePLCObjectProxy = Pyro5.client.Proxy(f"{schemename}:PLCObject@{location}") |
|
58 except Exception as e: |
|
59 confnodesroot.logger.write_error( |
|
60 _("Connection to {loc} failed with exception {ex}\n").format( |
|
61 loc=location, ex=str(e))) |
|
62 return None |
|
63 |
|
64 RemotePLCObjectProxy._pyroTimeout = 60 |
|
65 |
|
66 class MissingCallException(Exception): |
|
67 pass |
|
68 |
|
69 def PyroCatcher(func, default=None): |
|
70 """ |
|
71 A function that catch a Pyro exceptions, write error to logger |
|
72 and return default value when it happen |
|
73 """ |
|
74 def catcher_func(*args, **kwargs): |
|
75 try: |
|
76 return func(*args, **kwargs) |
|
77 except Pyro5.errors.ConnectionClosedError as e: |
|
78 confnodesroot._SetConnector(None) |
|
79 confnodesroot.logger.write_error(_("Connection lost!\n")) |
|
80 except Pyro5.errors.ProtocolError as e: |
|
81 confnodesroot.logger.write_error(_("Pyro exception: %s\n") % e) |
|
82 except MissingCallException as e: |
|
83 confnodesroot.logger.write_warning(_("Remote call not supported: %s\n") % e.message) |
|
84 except Exception as e: |
|
85 errmess = ''.join(Pyro5.errors.get_pyro_traceback()) |
|
86 confnodesroot.logger.write_error(errmess + "\n") |
|
87 print(errmess) |
|
88 confnodesroot._SetConnector(None) |
|
89 return default |
|
90 return catcher_func |
|
91 |
|
92 # Check connection is effective. |
|
93 # lambda is for getattr of GetPLCstatus to happen inside catcher |
|
94 IDPSK = PyroCatcher(RemotePLCObjectProxy.GetPLCID)() |
|
95 if IDPSK is None: |
|
96 confnodesroot.logger.write_warning(_("PLC did not provide identity and security infomation.\n")) |
|
97 else: |
|
98 ID, secret = IDPSK |
|
99 PSK.UpdateID(confnodesroot.ProjectPath, ID, secret, uri) |
|
100 |
|
101 class PyroProxyProxy(object): |
|
102 """ |
|
103 A proxy proxy class to handle Beremiz Pyro interface specific behavior. |
|
104 And to put Pyro exception catcher in between caller and Pyro proxy |
|
105 """ |
|
106 def __getattr__(self, attrName): |
|
107 member = self.__dict__.get(attrName, None) |
|
108 if member is None: |
|
109 def my_local_func(*args, **kwargs): |
|
110 call = RemotePLCObjectProxy.__getattr__(attrName) |
|
111 if call is None: |
|
112 raise MissingCallException(attrName) |
|
113 else: |
|
114 return call(*args, **kwargs) |
|
115 member = PyroCatcher(my_local_func, self.PLCObjDefaults.get(attrName, None)) |
|
116 self.__dict__[attrName] = member |
|
117 return member |
|
118 |
|
119 return PyroProxyProxy |
|