|
1 #!/usr/bin/env python |
|
2 # -*- coding: utf-8 -*- |
|
3 |
|
4 # Written by Edouard TISSERANT (C) 2024 |
|
5 # This file is part of Beremiz runtime |
|
6 # See COPYING.Runtime file for copyrights details. |
|
7 |
|
8 import sys |
|
9 import traceback |
|
10 from inspect import getmembers, isfunction |
|
11 |
|
12 import erpc |
|
13 |
|
14 # eRPC service code |
|
15 from erpc_interface.erpc_PLCObject.common import PSKID, PLCstatus, TraceVariables, trace_sample, PLCstatus_enum, log_message |
|
16 from erpc_interface.erpc_PLCObject.interface import IBeremizPLCObjectService |
|
17 from erpc_interface.erpc_PLCObject.server import BeremizPLCObjectServiceService |
|
18 |
|
19 from runtime import GetPLCObjectSingleton as PLC |
|
20 from runtime.loglevels import LogLevelsDict |
|
21 from runtime.ServicePublisher import ServicePublisher |
|
22 |
|
23 |
|
24 CRITICAL_LOG_LEVEL = LogLevelsDict["CRITICAL"] |
|
25 |
|
26 def ReturnAsLastOutput(method, args_wrapper, *args): |
|
27 args[-1].value = method(*args_wrapper(*args[:-1])) |
|
28 return 0 |
|
29 |
|
30 def TranslatedReturnAsLastOutput(translator): |
|
31 def wrapper(method, args_wrapper, *args): |
|
32 args[-1].value = translator(method(*args_wrapper(*args[:-1]))) |
|
33 return 0 |
|
34 return wrapper |
|
35 |
|
36 |
|
37 ReturnWrappers = { |
|
38 "AppendChunkToBlob":ReturnAsLastOutput, |
|
39 "GetLogMessage":TranslatedReturnAsLastOutput( |
|
40 lambda res:log_message(*res)), |
|
41 "GetPLCID":TranslatedReturnAsLastOutput( |
|
42 lambda res:PSKID(*res)), |
|
43 "GetPLCstatus":TranslatedReturnAsLastOutput( |
|
44 lambda res:PLCstatus(getattr(PLCstatus_enum, res[0]),res[1])), |
|
45 "GetTraceVariables":TranslatedReturnAsLastOutput( |
|
46 lambda res:TraceVariables(res[0],[trace_sample(*sample) for sample in res[1]])), |
|
47 "MatchMD5":ReturnAsLastOutput, |
|
48 "NewPLC":ReturnAsLastOutput, |
|
49 "SeedBlob":ReturnAsLastOutput, |
|
50 } |
|
51 |
|
52 ArgsWrappers = { |
|
53 "AppendChunkToBlob": |
|
54 lambda data, blobID:(data, bytes(blobID)), |
|
55 "NewPLC": |
|
56 lambda md5sum, plcObjectBlobID, extrafiles: ( |
|
57 md5sum, bytes(plcObjectBlobID), [(f.fname, bytes(f.blobID)) for f in extrafiles]), |
|
58 "SetTraceVariablesList": |
|
59 lambda orders : ([(order.idx, order.iectype, order.force) for order in orders],) |
|
60 } |
|
61 |
|
62 def rpc_wrapper(method_name): |
|
63 PLCobj = PLC() |
|
64 method=getattr(PLCobj, method_name) |
|
65 args_wrapper = ArgsWrappers.get(method_name, lambda *x:x) |
|
66 return_wrapper = ReturnWrappers.get(method_name, |
|
67 lambda method, args_wrapper, *args: method(*args_wrapper(*args))) |
|
68 |
|
69 def exception_wrapper(self, *args): |
|
70 try: |
|
71 print("Srv "+method_name) |
|
72 return_wrapper(method, args_wrapper, *args) |
|
73 return 0 |
|
74 except Exception as e: |
|
75 print(traceback.format_exc()) |
|
76 PLCobj.LogMessage(CRITICAL_LOG_LEVEL, f'eRPC call {method_name} Exception "{str(e)}"') |
|
77 raise |
|
78 |
|
79 return exception_wrapper |
|
80 |
|
81 |
|
82 class eRPCServer(object): |
|
83 def __init__(self, servicename, ip_addr, port): |
|
84 self.continueloop = True |
|
85 self.server = None |
|
86 self.transport = None |
|
87 self.servicename = servicename |
|
88 self.ip_addr = ip_addr |
|
89 self.port = port |
|
90 self.servicepublisher = None |
|
91 |
|
92 def _to_be_published(self): |
|
93 return self.servicename is not None and \ |
|
94 self.ip_addr not in ["", "localhost", "127.0.0.1"] |
|
95 |
|
96 def PrintServerInfo(self): |
|
97 print(_("eRPC port :"), self.port) |
|
98 |
|
99 if self._to_be_published(): |
|
100 print(_("Publishing service on local network")) |
|
101 |
|
102 if sys.stdout: |
|
103 sys.stdout.flush() |
|
104 |
|
105 def Loop(self, when_ready): |
|
106 if self._to_be_published(): |
|
107 self.Publish() |
|
108 |
|
109 while self.continueloop: |
|
110 |
|
111 # service handler calls PLC object though erpc_stubs's wrappers |
|
112 handler = type( |
|
113 "PLCObjectServiceHandlder", |
|
114 (IBeremizPLCObjectService,), |
|
115 {name: rpc_wrapper(name) |
|
116 for name,_func in getmembers(IBeremizPLCObjectService, isfunction)})() |
|
117 |
|
118 service = BeremizPLCObjectServiceService(handler) |
|
119 |
|
120 # TODO initialize Serial transport layer if selected |
|
121 # transport = erpc.transport.SerialTransport(device, baudrate) |
|
122 |
|
123 # initialize TCP transport layer |
|
124 self.transport = erpc.transport.TCPTransport(self.ip_addr, int(self.port), True) |
|
125 |
|
126 self.server = erpc.simple_server.SimpleServer(self.transport, erpc.basic_codec.BasicCodec) |
|
127 self.server.add_service(service) |
|
128 |
|
129 when_ready() |
|
130 |
|
131 self.server.run() |
|
132 |
|
133 self.Unpublish() |
|
134 |
|
135 def Restart(self): |
|
136 self.server.stop() |
|
137 self.transport.stop() |
|
138 |
|
139 def Quit(self): |
|
140 self.continueloop = False |
|
141 self.server.stop() |
|
142 self.transport.stop() |
|
143 |
|
144 def Publish(self): |
|
145 self.servicepublisher = ServicePublisher("ERPC") |
|
146 self.servicepublisher.RegisterService(self.servicename, |
|
147 self.ip_addr, self.port) |
|
148 |
|
149 def Unpublish(self): |
|
150 if self.servicepublisher is not None: |
|
151 self.servicepublisher.UnRegisterService() |
|
152 self.servicepublisher = None |