12 |
12 |
13 |
13 |
14 import sys |
14 import sys |
15 import os |
15 import os |
16 |
16 |
17 import Pyro |
17 import Pyro5 |
18 import Pyro.core as pyro |
18 import Pyro5.server |
|
19 |
19 import runtime |
20 import runtime |
20 from runtime.ServicePublisher import ServicePublisher |
21 from runtime.ServicePublisher import ServicePublisher |
21 |
22 |
|
23 def make_pyro_exposed_stub(method_name): |
|
24 stub = lambda self, *args, **kwargs: \ |
|
25 getattr(self.plc_object_instance, method_name)(self, *args, **kwargs) |
|
26 stub.__name__ = method_name |
|
27 Pyro5.server.expose(stub) |
|
28 return stub |
|
29 |
|
30 |
|
31 class PLCObjectPyroAdapter(type("PLCObjectPyroStubs", (), { |
|
32 name: make_pyro_exposed_stub(name) for name in [ |
|
33 "AppendChunkToBlob", |
|
34 "GetLogMessage", |
|
35 "GetPLCID", |
|
36 "GetPLCstatus", |
|
37 "GetTraceVariables", |
|
38 "MatchMD5", |
|
39 "NewPLC", |
|
40 "PurgeBlobs", |
|
41 "RemoteExec", |
|
42 "RepairPLC", |
|
43 "ResetLogCount", |
|
44 "SeedBlob", |
|
45 "SetTraceVariablesList", |
|
46 "StartPLC", |
|
47 "StopPLC" |
|
48 ] |
|
49 })): |
|
50 def __init__(self, plc_object_instance): |
|
51 self.plc_object_instance = plc_object_instance |
|
52 |
22 |
53 |
23 class PyroServer(object): |
54 class PyroServer(object): |
24 def __init__(self, servicename, ip_addr, port): |
55 def __init__(self, servicename, ip_addr, port): |
25 self.continueloop = True |
56 self.continueloop = True |
26 self.daemon = None |
57 self.daemon = None |
45 def PyroLoop(self, when_ready): |
76 def PyroLoop(self, when_ready): |
46 if self._to_be_published(): |
77 if self._to_be_published(): |
47 self.Publish() |
78 self.Publish() |
48 |
79 |
49 while self.continueloop: |
80 while self.continueloop: |
50 Pyro.config.PYRO_MULTITHREADED = 0 |
81 self.daemon = Pyro5.server.Daemon(host=self.ip_addr, port=self.port) |
51 pyro.initServer() |
|
52 self.daemon = pyro.Daemon(host=self.ip_addr, port=self.port) |
|
53 |
82 |
54 # pyro never frees memory after connection close if no timeout set |
83 self.daemon.register(PLCObjectPyroAdapter(runtime.GetPLCObjectSingleton()), "PLCObject") |
55 # taking too small timeout value may cause |
|
56 # unwanted diconnection when IDE is kept busy for long periods |
|
57 self.daemon.setTimeout(60) |
|
58 |
|
59 pyro_obj = Pyro.core.ObjBase() |
|
60 pyro_obj.delegateTo(runtime.GetPLCObjectSingleton()) |
|
61 |
|
62 self.daemon.connect(pyro_obj, "PLCObject") |
|
63 |
84 |
64 when_ready() |
85 when_ready() |
65 |
86 |
66 # "pipe to self" trick to to accelerate runtime shutdown |
87 self.daemon.requestLoop() |
67 # instead of waiting for arbitrary pyro timeout. |
|
68 others = [] |
|
69 if not sys.platform.startswith('win'): |
|
70 self.piper, self.pipew = os.pipe() |
|
71 others.append(self.piper) |
|
72 |
88 |
73 self.daemon.requestLoop(others=others, callback=lambda x: None) |
|
74 self.piper, self.pipew = None, None |
|
75 if hasattr(self, 'sock'): |
|
76 self.daemon.sock.close() |
|
77 self.Unpublish() |
89 self.Unpublish() |
78 |
90 |
79 def Restart(self): |
91 def Restart(self): |
80 self.daemon.shutdown(True) |
92 self.daemon.shutdown(True) |
81 |
93 |
82 def Quit(self): |
94 def Quit(self): |
83 self.continueloop = False |
95 self.continueloop = False |
84 self.daemon.shutdown(True) |
96 self.daemon.shutdown() |
85 self.daemon.closedown() |
|
86 if not sys.platform.startswith('win'): |
97 if not sys.platform.startswith('win'): |
87 if self.pipew is not None: |
98 if self.pipew is not None: |
88 os.write(self.pipew, "goodbye") |
99 os.write(self.pipew, "goodbye") |
89 |
100 |
90 def Publish(self): |
101 def Publish(self): |