edouard@3884: #!/usr/bin/env python edouard@3884: # -*- coding: utf-8 -*- edouard@3884: edouard@3884: # Written by Edouard TISSERANT (C) 2024 edouard@3884: # This file is part of Beremiz IDE edouard@3884: # See COPYING file for copyrights details. edouard@3884: edouard@3884: edouard@3884: import os.path edouard@3884: import re edouard@3884: import traceback edouard@3884: from inspect import getmembers, isfunction edouard@3884: edouard@3884: edouard@3884: import erpc edouard@3884: edouard@3884: # eRPC service code edouard@3884: from erpc_interface.erpc_PLCObject.interface import IBeremizPLCObjectService edouard@3884: from erpc_interface.erpc_PLCObject.client import BeremizPLCObjectServiceClient edouard@3887: from erpc_interface.erpc_PLCObject.common import trace_order, extra_file, PLCstatus_enum edouard@3884: edouard@3884: import PSKManagement as PSK edouard@3884: from connectors.ERPC.PSK_Adapter import SSLPSKClientTransport edouard@3884: from connectors.ConnectorBase import ConnectorBase edouard@3884: edouard@3884: enum_to_PLCstatus = dict(map(lambda t:(t[1],t[0]),getmembers(PLCstatus_enum, lambda x:type(x)==int))) edouard@3884: edouard@3884: class MissingCallException(Exception): edouard@3884: pass edouard@3884: edouard@3884: def ExceptionFromERPCReturn(ret): edouard@3884: return {1:Exception, edouard@3884: 2:MissingCallException}.get(ret,ValueError) edouard@3884: edouard@3884: def ReturnAsLastOutput(client_method, obj, args_wrapper, *args): edouard@3884: retval = erpc.Reference() edouard@3884: ret = client_method(obj, *args_wrapper(*args), retval) edouard@3884: if ret != 0: edouard@3884: raise ExceptionFromERPCReturn(ret)(client_method.__name__) edouard@3884: return retval.value edouard@3884: edouard@3884: def TranslatedReturnAsLastOutput(translator): edouard@3884: def wrapper(client_method, obj, args_wrapper, *args): edouard@3884: res = ReturnAsLastOutput(client_method, obj, args_wrapper, *args) edouard@3884: return translator(res) edouard@3884: return wrapper edouard@3884: edouard@3884: ReturnWrappers = { edouard@3884: "AppendChunkToBlob":ReturnAsLastOutput, edouard@3884: "GetLogMessage":TranslatedReturnAsLastOutput( edouard@3884: lambda res:(res.msg, res.tick, res.sec, res.nsec)), edouard@3884: "GetPLCID":TranslatedReturnAsLastOutput( edouard@3884: lambda res:(res.ID, res.PSK)), edouard@3884: "GetPLCstatus":TranslatedReturnAsLastOutput( edouard@3884: lambda res:(enum_to_PLCstatus[res.PLCstatus], res.logcounts)), edouard@3884: "GetTraceVariables":TranslatedReturnAsLastOutput( edouard@3884: lambda res:(enum_to_PLCstatus[res.PLCstatus], edouard@3885: [(sample.tick, bytes(sample.TraceBuffer)) for sample in res.traces])), edouard@3884: "MatchMD5":ReturnAsLastOutput, edouard@3884: "NewPLC":ReturnAsLastOutput, edouard@3884: "SeedBlob":ReturnAsLastOutput, edouard@3885: "SetTraceVariablesList": ReturnAsLastOutput, edouard@3885: "StopPLC":ReturnAsLastOutput, edouard@3884: } edouard@3884: edouard@3884: ArgsWrappers = { edouard@3884: "NewPLC": edouard@3884: lambda md5sum, plcObjectBlobID, extrafiles: ( edouard@3884: md5sum, plcObjectBlobID, [extra_file(*f) for f in extrafiles]), edouard@3887: "SetTraceVariablesList": edouard@3885: lambda orders : ([ edouard@3887: trace_order(idx, b"" if force is None else force) edouard@3887: for idx, force in orders],) edouard@3884: } edouard@3884: edouard@3884: def ERPC_connector_factory(uri, confnodesroot): edouard@3884: """ edouard@3884: returns the ERPC connector edouard@3884: """ edouard@3884: confnodesroot.logger.write(_("ERPC connecting to URI : %s\n") % uri) edouard@3884: edouard@3884: # TODO add parsing for serial URI edouard@3884: # ERPC:///dev/ttyXX:baudrate or ERPC://:COM4:baudrate edouard@3884: edouard@3884: try: edouard@3884: _scheme, location = uri.split("://",1) edouard@3884: locator, *IDhash = location.split('#',1) edouard@3884: x = re.match(r'(?P[^\s:]+):?(?P\d+)?', locator) edouard@3884: host = x.group('host') edouard@3884: port = x.group('port') edouard@3884: if port: edouard@3884: port = int(port) edouard@3884: else: edouard@3884: port = 3000 edouard@3884: except Exception as e: edouard@3884: confnodesroot.logger.write_error( edouard@3884: 'Malformed URI "%s": %s\n' % (uri, str(e))) edouard@3884: return None edouard@3884: edouard@3884: def rpc_wrapper(method_name): edouard@3884: client_method = getattr(BeremizPLCObjectServiceClient, method_name) edouard@3884: return_wrapper = ReturnWrappers.get( edouard@3884: method_name, edouard@3884: lambda client_method, obj, args_wrapper, *args: client_method(obj, *args_wrapper(*args))) edouard@3884: args_wrapper = ArgsWrappers.get(method_name, lambda *x:x) edouard@3884: edouard@3884: def exception_wrapper(self, *args): edouard@3884: try: edouard@3884: print("Clt "+method_name) edouard@3884: return return_wrapper(client_method, self, args_wrapper, *args) edouard@3884: except erpc.transport.ConnectionClosed as e: edouard@3884: confnodesroot._SetConnector(None) edouard@3884: confnodesroot.logger.write_error(_("Connection lost!\n")) edouard@3884: except erpc.codec.CodecError as e: edouard@3884: confnodesroot.logger.write_warning(_("ERPC codec error: %s\n") % e) edouard@3884: except erpc.client.RequestError as e: edouard@3884: confnodesroot.logger.write_error(_("ERPC request error: %s\n") % e) edouard@3884: except MissingCallException as e: edouard@3884: confnodesroot.logger.write_warning(_("Remote call not supported: %s\n") % e.message) edouard@3884: except Exception as e: edouard@3884: errmess = _("Exception calling remote PLC object fucntio %s:\n") % method_name \ edouard@3884: + traceback.format_exc() edouard@3884: confnodesroot.logger.write_error(errmess + "\n") edouard@3884: print(errmess) edouard@3884: confnodesroot._SetConnector(None) edouard@3884: edouard@3884: return self.PLCObjDefaults.get(method_name) edouard@3884: return exception_wrapper edouard@3884: edouard@3884: edouard@3884: PLCObjectERPCProxy = type( edouard@3884: "PLCObjectERPCProxy", edouard@3884: (ConnectorBase, BeremizPLCObjectServiceClient), edouard@3884: {name: rpc_wrapper(name) edouard@3884: for name,_func in getmembers(IBeremizPLCObjectService, isfunction)}) edouard@3884: edouard@3884: try: edouard@3884: if IDhash: edouard@3884: ID = IDhash[0] edouard@3884: # load PSK from project edouard@3884: secpath = os.path.join(str(confnodesroot.ProjectPath), 'psk', ID + '.secret') edouard@3884: if not os.path.exists(secpath): edouard@3884: confnodesroot.logger.write_error( edouard@3884: 'Error: Pre-Shared-Key Secret in %s is missing!\n' % secpath) edouard@3884: return None edouard@3884: secret = open(secpath).read().partition(':')[2].rstrip('\n\r') edouard@3884: transport = SSLPSKClientTransport(host, port, (secret, ID)) edouard@3884: else: edouard@3884: # TODO if serial URI then edouard@3884: # transport = erpc.transport.SerialTransport(device, baudrate) edouard@3884: edouard@3884: transport = erpc.transport.TCPTransport(host, port, False) edouard@3884: edouard@3884: clientManager = erpc.client.ClientManager(transport, erpc.basic_codec.BasicCodec) edouard@3884: client = PLCObjectERPCProxy(clientManager) edouard@3884: edouard@3884: except Exception as e: edouard@3884: confnodesroot.logger.write_error( edouard@3884: _("Connection to {loc} failed with exception {ex}\n").format( edouard@3884: loc=locator, ex=str(e))) edouard@3884: return None edouard@3884: edouard@3884: # Check connection is effective. edouard@3884: IDPSK = client.GetPLCID() edouard@3884: if IDPSK: edouard@3884: ID, secret = IDPSK edouard@3884: PSK.UpdateID(confnodesroot.ProjectPath, ID, secret, uri) edouard@3884: else: edouard@3884: confnodesroot.logger.write_warning(_("PLC did not provide identity and security infomation.\n")) edouard@3884: edouard@3884: return client