runtime/eRPCServer.py
changeset 3884 34da877021d5
child 3885 22a009561502
equal deleted inserted replaced
3883:a6e7dd8bac36 3884:34da877021d5
       
     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