Add ExtendedCall to PLCObject as a replacement of RemoteExec and reflect it in eRPC interface.
authorEdouard Tisserant <edouard@beremiz.fr>
Mon, 28 Oct 2024 16:02:47 +0100
changeset 4032 1ffff67678ad
parent 4031 a89ed401fbc6
child 4033 208a808d1081
Add ExtendedCall to PLCObject as a replacement of RemoteExec and reflect it in eRPC interface.

ExtendedCall is a generic remote procedure call that runtime code can register to and that IDE extensions can call.
For example a fieldbus extension can use ExtendedCall to browse the fieldbus devices connected to the runtime.
C_runtime/PLCObject.cpp
C_runtime/PLCObject.hpp
C_runtime/c_erpc_PLCObject_client.cpp
C_runtime/c_erpc_PLCObject_client.h
C_runtime/c_erpc_PLCObject_server.cpp
C_runtime/c_erpc_PLCObject_server.h
C_runtime/erpc_PLCObject_client.cpp
C_runtime/erpc_PLCObject_client.hpp
C_runtime/erpc_PLCObject_common.h
C_runtime/erpc_PLCObject_common.hpp
C_runtime/erpc_PLCObject_interface.cpp
C_runtime/erpc_PLCObject_interface.hpp
C_runtime/erpc_PLCObject_server.cpp
C_runtime/erpc_PLCObject_server.hpp
ProjectController.py
connectors/ERPC/__init__.py
erpc_interface/__init__.py
erpc_interface/erpc_PLCObject.erpc
erpc_interface/erpc_PLCObject/__init__.py
erpc_interface/erpc_PLCObject/client.py
erpc_interface/erpc_PLCObject/common.py
erpc_interface/erpc_PLCObject/interface.py
erpc_interface/erpc_PLCObject/server.py
runtime/PLCObject.py
runtime/eRPCServer.py
--- a/C_runtime/PLCObject.cpp	Fri Oct 25 14:51:38 2024 +0200
+++ b/C_runtime/PLCObject.cpp	Mon Oct 28 16:02:47 2024 +0100
@@ -675,3 +675,13 @@
         "TraceThreadProc ended normally");
 }
 
+uint32_t PLCObject::ExtendedCall(const char * method, const binary_t * argument, binary_t * answer)
+{
+    // TODO
+
+    answer->data = (uint8_t*)"";
+    answer->dataLength = 0;
+
+    return 0;
+}
+
--- a/C_runtime/PLCObject.hpp	Fri Oct 25 14:51:38 2024 +0200
+++ b/C_runtime/PLCObject.hpp	Mon Oct 28 16:02:47 2024 +0100
@@ -71,6 +71,7 @@
         uint32_t SetTraceVariablesList(const list_trace_order_1_t * orders, int32_t * debugtoken);
         uint32_t StartPLC(void);
         uint32_t StopPLC(bool * success);
+        uint32_t ExtendedCall(const char * method, const binary_t * argument, binary_t * answer);
 
         // Public interface used by runtime
         uint32_t AutoLoad();
--- a/C_runtime/c_erpc_PLCObject_client.cpp	Fri Oct 25 14:51:38 2024 +0200
+++ b/C_runtime/c_erpc_PLCObject_client.cpp	Mon Oct 28 16:02:47 2024 +0100
@@ -1,5 +1,5 @@
 /*
- * Generated by erpcgen 1.12.0 on Mon May 20 17:54:19 2024.
+ * Generated by erpcgen 1.12.0 on Mon Oct 28 15:10:14 2024.
  *
  * AUTOGENERATED - DO NOT EDIT
  */
@@ -132,6 +132,14 @@
     return result;
 }
 
+uint32_t ExtendedCall(const char * method, const binary_t * argument, binary_t * answer)
+{
+    uint32_t result;
+    result = s_BeremizPLCObjectService_client->ExtendedCall(method, argument, answer);
+
+    return result;
+}
+
 void initBeremizPLCObjectService_client(erpc_client_t client)
 {
 #if ERPC_ALLOCATION_POLICY == ERPC_ALLOCATION_POLICY_DYNAMIC
--- a/C_runtime/c_erpc_PLCObject_client.h	Fri Oct 25 14:51:38 2024 +0200
+++ b/C_runtime/c_erpc_PLCObject_client.h	Mon Oct 28 16:02:47 2024 +0100
@@ -1,5 +1,5 @@
 /*
- * Generated by erpcgen 1.12.0 on Mon May 20 17:54:19 2024.
+ * Generated by erpcgen 1.12.0 on Mon Oct 28 15:10:14 2024.
  *
  * AUTOGENERATED - DO NOT EDIT
  */
@@ -38,6 +38,7 @@
     kBeremizPLCObjectService_SetTraceVariablesList_id = 12,
     kBeremizPLCObjectService_StartPLC_id = 13,
     kBeremizPLCObjectService_StopPLC_id = 14,
+    kBeremizPLCObjectService_ExtendedCall_id = 15,
 };
 
 //! @name BeremizPLCObjectService
@@ -69,6 +70,8 @@
 uint32_t StartPLC(void);
 
 uint32_t StopPLC(bool * success);
+
+uint32_t ExtendedCall(const char * method, const binary_t * argument, binary_t * answer);
 //@}
 
 #endif // ERPC_FUNCTIONS_DEFINITIONS
--- a/C_runtime/c_erpc_PLCObject_server.cpp	Fri Oct 25 14:51:38 2024 +0200
+++ b/C_runtime/c_erpc_PLCObject_server.cpp	Mon Oct 28 16:02:47 2024 +0100
@@ -1,5 +1,5 @@
 /*
- * Generated by erpcgen 1.12.0 on Mon May 20 17:54:19 2024.
+ * Generated by erpcgen 1.12.0 on Mon Oct 28 15:10:14 2024.
  *
  * AUTOGENERATED - DO NOT EDIT
  */
@@ -132,6 +132,14 @@
 
             return result;
         }
+
+        uint32_t ExtendedCall(const char * method, const binary_t * argument, binary_t * answer)
+        {
+            uint32_t result;
+            result = ::ExtendedCall(method, argument, answer);
+
+            return result;
+        }
 };
 
 ERPC_MANUALLY_CONSTRUCTED_STATIC(BeremizPLCObjectService_service, s_BeremizPLCObjectService_service);
--- a/C_runtime/c_erpc_PLCObject_server.h	Fri Oct 25 14:51:38 2024 +0200
+++ b/C_runtime/c_erpc_PLCObject_server.h	Mon Oct 28 16:02:47 2024 +0100
@@ -1,5 +1,5 @@
 /*
- * Generated by erpcgen 1.12.0 on Mon May 20 17:54:19 2024.
+ * Generated by erpcgen 1.12.0 on Mon Oct 28 15:10:14 2024.
  *
  * AUTOGENERATED - DO NOT EDIT
  */
@@ -39,6 +39,7 @@
     kBeremizPLCObjectService_SetTraceVariablesList_id = 12,
     kBeremizPLCObjectService_StartPLC_id = 13,
     kBeremizPLCObjectService_StopPLC_id = 14,
+    kBeremizPLCObjectService_ExtendedCall_id = 15,
 };
 
 //! @name BeremizPLCObjectService
@@ -70,6 +71,8 @@
 uint32_t StartPLC(void);
 
 uint32_t StopPLC(bool * success);
+
+uint32_t ExtendedCall(const char * method, const binary_t * argument, binary_t * answer);
 //@}
 
 
--- a/C_runtime/erpc_PLCObject_client.cpp	Fri Oct 25 14:51:38 2024 +0200
+++ b/C_runtime/erpc_PLCObject_client.cpp	Mon Oct 28 16:02:47 2024 +0100
@@ -1,5 +1,5 @@
 /*
- * Generated by erpcgen 1.12.0 on Mon May 20 17:54:19 2024.
+ * Generated by erpcgen 1.12.0 on Mon Oct 28 15:10:14 2024.
  *
  * AUTOGENERATED - DO NOT EDIT
  */
@@ -1213,3 +1213,74 @@
 
     return result;
 }
+
+// BeremizPLCObjectService interface ExtendedCall function client shim.
+uint32_t BeremizPLCObjectService_client::ExtendedCall(const char * method, const binary_t * argument, binary_t * answer)
+{
+    erpc_status_t err = kErpcStatus_Success;
+
+    uint32_t result;
+
+#if ERPC_PRE_POST_ACTION
+    pre_post_action_cb preCB = m_clientManager->getPreCB();
+    if (preCB)
+    {
+        preCB();
+    }
+#endif
+
+    // Get a new request.
+    RequestContext request = m_clientManager->createRequest(false);
+
+    // Encode the request.
+    Codec * codec = request.getCodec();
+
+    if (codec == NULL)
+    {
+        err = kErpcStatus_MemoryError;
+    }
+    else
+    {
+        codec->startWriteMessage(message_type_t::kInvocationMessage, m_serviceId, m_ExtendedCallId, request.getSequence());
+
+        {
+            uint32_t method_len = strlen((const char*)method);
+
+            codec->writeString(method_len, (const char*)method);
+        }
+
+        write_binary_t_struct(codec, argument);
+
+        // Send message to server
+        // Codec status is checked inside this function.
+        m_clientManager->performRequest(request);
+
+        read_binary_t_struct(codec, answer);
+
+        codec->read(result);
+
+        err = codec->getStatus();
+    }
+
+    // Dispose of the request.
+    m_clientManager->releaseRequest(request);
+
+    // Invoke error handler callback function
+    m_clientManager->callErrorHandler(err, m_ExtendedCallId);
+
+#if ERPC_PRE_POST_ACTION
+    pre_post_action_cb postCB = m_clientManager->getPostCB();
+    if (postCB)
+    {
+        postCB();
+    }
+#endif
+
+
+    if (err != kErpcStatus_Success)
+    {
+        result = 0xFFFFFFFFU;
+    }
+
+    return result;
+}
--- a/C_runtime/erpc_PLCObject_client.hpp	Fri Oct 25 14:51:38 2024 +0200
+++ b/C_runtime/erpc_PLCObject_client.hpp	Mon Oct 28 16:02:47 2024 +0100
@@ -1,5 +1,5 @@
 /*
- * Generated by erpcgen 1.12.0 on Mon May 20 17:54:19 2024.
+ * Generated by erpcgen 1.12.0 on Mon Oct 28 15:10:14 2024.
  *
  * AUTOGENERATED - DO NOT EDIT
  */
@@ -50,6 +50,8 @@
 
         virtual uint32_t StopPLC(bool * success);
 
+        virtual uint32_t ExtendedCall(const char * method, const binary_t * argument, binary_t * answer);
+
     protected:
         erpc::ClientManager *m_clientManager;
 };
--- a/C_runtime/erpc_PLCObject_common.h	Fri Oct 25 14:51:38 2024 +0200
+++ b/C_runtime/erpc_PLCObject_common.h	Mon Oct 28 16:02:47 2024 +0100
@@ -1,5 +1,5 @@
 /*
- * Generated by erpcgen 1.12.0 on Mon May 20 17:54:19 2024.
+ * Generated by erpcgen 1.12.0 on Mon Oct 28 15:10:14 2024.
  *
  * AUTOGENERATED - DO NOT EDIT
  */
--- a/C_runtime/erpc_PLCObject_common.hpp	Fri Oct 25 14:51:38 2024 +0200
+++ b/C_runtime/erpc_PLCObject_common.hpp	Mon Oct 28 16:02:47 2024 +0100
@@ -1,5 +1,5 @@
 /*
- * Generated by erpcgen 1.12.0 on Mon May 20 17:54:19 2024.
+ * Generated by erpcgen 1.12.0 on Mon Oct 28 15:10:14 2024.
  *
  * AUTOGENERATED - DO NOT EDIT
  */
--- a/C_runtime/erpc_PLCObject_interface.cpp	Fri Oct 25 14:51:38 2024 +0200
+++ b/C_runtime/erpc_PLCObject_interface.cpp	Mon Oct 28 16:02:47 2024 +0100
@@ -1,5 +1,5 @@
 /*
- * Generated by erpcgen 1.12.0 on Mon May 20 17:54:19 2024.
+ * Generated by erpcgen 1.12.0 on Mon Oct 28 15:10:14 2024.
  *
  * AUTOGENERATED - DO NOT EDIT
  */
--- a/C_runtime/erpc_PLCObject_interface.hpp	Fri Oct 25 14:51:38 2024 +0200
+++ b/C_runtime/erpc_PLCObject_interface.hpp	Mon Oct 28 16:02:47 2024 +0100
@@ -1,5 +1,5 @@
 /*
- * Generated by erpcgen 1.12.0 on Mon May 20 17:54:19 2024.
+ * Generated by erpcgen 1.12.0 on Mon Oct 28 15:10:14 2024.
  *
  * AUTOGENERATED - DO NOT EDIT
  */
@@ -33,6 +33,7 @@
         static const uint8_t m_SetTraceVariablesListId = 12;
         static const uint8_t m_StartPLCId = 13;
         static const uint8_t m_StopPLCId = 14;
+        static const uint8_t m_ExtendedCallId = 15;
 
         virtual ~BeremizPLCObjectService_interface(void);
 
@@ -63,6 +64,8 @@
         virtual uint32_t StartPLC(void) = 0;
 
         virtual uint32_t StopPLC(bool * success) = 0;
+
+        virtual uint32_t ExtendedCall(const char * method, const binary_t * argument, binary_t * answer) = 0;
 private:
 };
 } // erpcShim
--- a/C_runtime/erpc_PLCObject_server.cpp	Fri Oct 25 14:51:38 2024 +0200
+++ b/C_runtime/erpc_PLCObject_server.cpp	Mon Oct 28 16:02:47 2024 +0100
@@ -1,5 +1,5 @@
 /*
- * Generated by erpcgen 1.12.0 on Mon May 20 17:54:19 2024.
+ * Generated by erpcgen 1.12.0 on Mon Oct 28 15:10:14 2024.
  *
  * AUTOGENERATED - DO NOT EDIT
  */
@@ -501,6 +501,12 @@
             break;
         }
 
+        case BeremizPLCObjectService_interface::m_ExtendedCallId:
+        {
+            erpcStatus = ExtendedCall_shim(codec, messageFactory, transport, sequence);
+            break;
+        }
+
         default:
         {
             erpcStatus = kErpcStatus_InvalidArgument;
@@ -1313,3 +1319,92 @@
 
     return err;
 }
+
+// Server shim for ExtendedCall of BeremizPLCObjectService interface.
+erpc_status_t BeremizPLCObjectService_service::ExtendedCall_shim(Codec * codec, MessageBufferFactory *messageFactory, Transport * transport, uint32_t sequence)
+{
+    erpc_status_t err = kErpcStatus_Success;
+
+    char * method = NULL;
+    binary_t *argument = NULL;
+    argument = (binary_t *) erpc_malloc(sizeof(binary_t));
+    if (argument == NULL)
+    {
+        codec->updateStatus(kErpcStatus_MemoryError);
+    }
+    binary_t *answer = NULL;
+    uint32_t result;
+
+    // startReadMessage() was already called before this shim was invoked.
+
+    {
+        uint32_t method_len;
+        char * method_local;
+        codec->readString(method_len, &method_local);
+        method = (char*) erpc_malloc((method_len + 1) * sizeof(char));
+        if ((method == NULL) || (method_local == NULL))
+        {
+            codec->updateStatus(kErpcStatus_MemoryError);
+        }
+        else
+        {
+            memcpy(method, method_local, method_len);
+            (method)[method_len] = 0;
+        }
+    }
+
+    read_binary_t_struct(codec, argument);
+
+    answer = (binary_t *) erpc_malloc(sizeof(binary_t));
+    if (answer == NULL)
+    {
+        codec->updateStatus(kErpcStatus_MemoryError);
+    }
+
+    err = codec->getStatus();
+    if (err == kErpcStatus_Success)
+    {
+        // Invoke the actual served function.
+#if ERPC_NESTED_CALLS_DETECTION
+        nestingDetection = true;
+#endif
+        result = m_handler->ExtendedCall(method, argument, answer);
+#if ERPC_NESTED_CALLS_DETECTION
+        nestingDetection = false;
+#endif
+
+        // preparing MessageBuffer for serializing data
+        err = messageFactory->prepareServerBufferForSend(codec->getBufferRef(), transport->reserveHeaderSize());
+    }
+
+    if (err == kErpcStatus_Success)
+    {
+        // preparing codec for serializing data
+        codec->reset(transport->reserveHeaderSize());
+
+        // Build response message.
+        codec->startWriteMessage(message_type_t::kReplyMessage, BeremizPLCObjectService_interface::m_serviceId, BeremizPLCObjectService_interface::m_ExtendedCallId, sequence);
+
+        write_binary_t_struct(codec, answer);
+
+        codec->write(result);
+
+        err = codec->getStatus();
+    }
+
+    erpc_free(method);
+
+    if (argument)
+    {
+        free_binary_t_struct(argument);
+    }
+    erpc_free(argument);
+
+    if (answer)
+    {
+        free_binary_t_struct(answer);
+    }
+    erpc_free(answer);
+
+    return err;
+}
--- a/C_runtime/erpc_PLCObject_server.hpp	Fri Oct 25 14:51:38 2024 +0200
+++ b/C_runtime/erpc_PLCObject_server.hpp	Mon Oct 28 16:02:47 2024 +0100
@@ -1,5 +1,5 @@
 /*
- * Generated by erpcgen 1.12.0 on Mon May 20 17:54:19 2024.
+ * Generated by erpcgen 1.12.0 on Mon Oct 28 15:10:14 2024.
  *
  * AUTOGENERATED - DO NOT EDIT
  */
@@ -80,6 +80,9 @@
 
     /*! @brief Server shim for StopPLC of BeremizPLCObjectService interface. */
     erpc_status_t StopPLC_shim(erpc::Codec * codec, erpc::MessageBufferFactory *messageFactory, erpc::Transport * transport, uint32_t sequence);
+
+    /*! @brief Server shim for ExtendedCall of BeremizPLCObjectService interface. */
+    erpc_status_t ExtendedCall_shim(erpc::Codec * codec, erpc::MessageBufferFactory *messageFactory, erpc::Transport * transport, uint32_t sequence);
 };
 
 } // erpcShim
--- a/ProjectController.py	Fri Oct 25 14:51:38 2024 +0200
+++ b/ProjectController.py	Mon Oct 28 16:02:47 2024 +0100
@@ -1919,6 +1919,9 @@
             # Oups.
             self.logger.write_error(_("Connection failed to %s!\n") % uri)
         else:
+            VersionsInfoBytes = self._connector.ExtendedCall("GetVersions", bytes())
+            VersionsInfo = VersionsInfoBytes.decode()
+            self.logger.write(f"Version string: {VersionsInfo}\n")
             self.CompareLocalAndRemotePLC()
 
             # Init with actual PLC status and print it
--- a/connectors/ERPC/__init__.py	Fri Oct 25 14:51:38 2024 +0200
+++ b/connectors/ERPC/__init__.py	Mon Oct 28 16:02:47 2024 +0100
@@ -61,6 +61,7 @@
     "SeedBlob":ReturnAsLastOutput,
     "SetTraceVariablesList": ReturnAsLastOutput,
     "StopPLC":ReturnAsLastOutput,
+    "ExtendedCall":ReturnAsLastOutput,
 }
 
 ArgsWrappers = {
--- a/erpc_interface/__init__.py	Fri Oct 25 14:51:38 2024 +0200
+++ b/erpc_interface/__init__.py	Mon Oct 28 16:02:47 2024 +0100
@@ -1,5 +1,5 @@
 #
-# Generated by erpcgen 1.12.0 on Mon May 20 17:53:37 2024.
+# Generated by erpcgen 1.12.0 on Mon Oct 28 11:30:03 2024.
 #
 # AUTOGENERATED - DO NOT EDIT
 #
--- a/erpc_interface/erpc_PLCObject.erpc	Fri Oct 25 14:51:38 2024 +0200
+++ b/erpc_interface/erpc_PLCObject.erpc	Mon Oct 28 16:02:47 2024 +0100
@@ -61,11 +61,11 @@
     MatchMD5(in string MD5, out bool match) -> uint32
     NewPLC(in string md5sum, in binary plcObjectBlobID, in list<extra_file> extrafiles, out bool success) -> uint32
     PurgeBlobs() -> uint32
-    /* NOT TO DO : RemoteExec(in ) -> uint32 */
     RepairPLC() -> uint32
     ResetLogCount() -> uint32
     SeedBlob(in binary seed, out binary blobID) -> uint32
     SetTraceVariablesList(in list<trace_order> orders, out int32 debugtoken) -> uint32
     StartPLC() -> uint32
     StopPLC(out bool success) -> uint32
+    ExtendedCall(in string method, in binary argument, out binary answer ) -> uint32
 }
--- a/erpc_interface/erpc_PLCObject/__init__.py	Fri Oct 25 14:51:38 2024 +0200
+++ b/erpc_interface/erpc_PLCObject/__init__.py	Mon Oct 28 16:02:47 2024 +0100
@@ -1,5 +1,5 @@
 #
-# Generated by erpcgen 1.12.0 on Mon May 20 17:53:37 2024.
+# Generated by erpcgen 1.12.0 on Mon Oct 28 11:30:03 2024.
 #
 # AUTOGENERATED - DO NOT EDIT
 #
--- a/erpc_interface/erpc_PLCObject/client.py	Fri Oct 25 14:51:38 2024 +0200
+++ b/erpc_interface/erpc_PLCObject/client.py	Mon Oct 28 16:02:47 2024 +0100
@@ -1,5 +1,5 @@
 #
-# Generated by erpcgen 1.12.0 on Mon May 20 17:53:37 2024.
+# Generated by erpcgen 1.12.0 on Mon Oct 28 11:30:03 2024.
 #
 # AUTOGENERATED - DO NOT EDIT
 #
@@ -292,4 +292,28 @@
         _result = codec.read_uint32()
         return _result
 
-
+    def ExtendedCall(self, method, argument, answer):
+        assert type(answer) is erpc.Reference, "out parameter must be a Reference object"
+
+        # Build remote function invocation message.
+        request = self._clientManager.create_request()
+        codec = request.codec
+        codec.start_write_message(erpc.codec.MessageInfo(
+                type=erpc.codec.MessageType.kInvocationMessage,
+                service=self.SERVICE_ID,
+                request=self.EXTENDEDCALL_ID,
+                sequence=request.sequence))
+        if method is None:
+            raise ValueError("method is None")
+        codec.write_string(method)
+        if argument is None:
+            raise ValueError("argument is None")
+        codec.write_binary(argument)
+
+        # Send request and process reply.
+        self._clientManager.perform_request(request)
+        answer.value = codec.read_binary()
+        _result = codec.read_uint32()
+        return _result
+
+
--- a/erpc_interface/erpc_PLCObject/common.py	Fri Oct 25 14:51:38 2024 +0200
+++ b/erpc_interface/erpc_PLCObject/common.py	Mon Oct 28 16:02:47 2024 +0100
@@ -1,5 +1,5 @@
 #
-# Generated by erpcgen 1.12.0 on Mon May 20 17:53:37 2024.
+# Generated by erpcgen 1.12.0 on Mon Oct 28 11:30:03 2024.
 #
 # AUTOGENERATED - DO NOT EDIT
 #
--- a/erpc_interface/erpc_PLCObject/interface.py	Fri Oct 25 14:51:38 2024 +0200
+++ b/erpc_interface/erpc_PLCObject/interface.py	Mon Oct 28 16:02:47 2024 +0100
@@ -1,5 +1,5 @@
 #
-# Generated by erpcgen 1.12.0 on Mon May 20 17:53:37 2024.
+# Generated by erpcgen 1.12.0 on Mon Oct 28 11:30:03 2024.
 #
 # AUTOGENERATED - DO NOT EDIT
 #
@@ -21,6 +21,7 @@
     SETTRACEVARIABLESLIST_ID = 12
     STARTPLC_ID = 13
     STOPPLC_ID = 14
+    EXTENDEDCALL_ID = 15
 
     def AppendChunkToBlob(self, data, blobID, newBlobID):
         raise NotImplementedError()
@@ -64,4 +65,7 @@
     def StopPLC(self, success):
         raise NotImplementedError()
 
+    def ExtendedCall(self, method, argument, answer):
+        raise NotImplementedError()
 
+
--- a/erpc_interface/erpc_PLCObject/server.py	Fri Oct 25 14:51:38 2024 +0200
+++ b/erpc_interface/erpc_PLCObject/server.py	Mon Oct 28 16:02:47 2024 +0100
@@ -1,5 +1,5 @@
 #
-# Generated by erpcgen 1.12.0 on Mon May 20 17:53:37 2024.
+# Generated by erpcgen 1.12.0 on Mon Oct 28 11:30:03 2024.
 #
 # AUTOGENERATED - DO NOT EDIT
 #
@@ -27,6 +27,7 @@
                 interface.IBeremizPLCObjectService.SETTRACEVARIABLESLIST_ID: self._handle_SetTraceVariablesList,
                 interface.IBeremizPLCObjectService.STARTPLC_ID: self._handle_StartPLC,
                 interface.IBeremizPLCObjectService.STOPPLC_ID: self._handle_StopPLC,
+                interface.IBeremizPLCObjectService.EXTENDEDCALL_ID: self._handle_ExtendedCall,
             }
 
     def _handle_AppendChunkToBlob(self, sequence, codec):
@@ -348,4 +349,29 @@
         codec.write_bool(success.value)
         codec.write_uint32(_result)
 
-
+    def _handle_ExtendedCall(self, sequence, codec):
+        # Create reference objects to pass into handler for out/inout parameters.
+        answer = erpc.Reference()
+
+        # Read incoming parameters.
+        method = codec.read_string()
+        argument = codec.read_binary()
+
+        # Invoke user implementation of remote function.
+        _result = self._handler.ExtendedCall(method, argument, answer)
+
+        # Prepare codec for reply message.
+        codec.reset()
+
+        # Construct reply message.
+        codec.start_write_message(erpc.codec.MessageInfo(
+            type=erpc.codec.MessageType.kReplyMessage,
+            service=interface.IBeremizPLCObjectService.SERVICE_ID,
+            request=interface.IBeremizPLCObjectService.EXTENDEDCALL_ID,
+            sequence=sequence))
+        if answer.value is None:
+            raise ValueError("answer.value is None")
+        codec.write_binary(answer.value)
+        codec.write_uint32(_result)
+
+
--- a/runtime/PLCObject.py	Fri Oct 25 14:51:38 2024 +0200
+++ b/runtime/PLCObject.py	Mon Oct 28 16:02:47 2024 +0100
@@ -103,6 +103,9 @@
         self.PlcStopped.set()
 
         self._init_blobs()
+        
+        # initialize extended calls with GetVersions call, ignoring arguments
+        self.extended_calls = {"GetVersions":lambda *_args:self.GetVersions().encode()}
 
     # First task of worker -> no @RunInMain
     def AutoLoad(self, autostart):
@@ -818,16 +821,17 @@
 
         self.TraceThread = None
 
-    def RemoteExec(self, script, *kwargs):
-        try:
-            exec(script, kwargs)
-        except Exception:
-            _e_type, e_value, e_traceback = sys.exc_info()
-            line_no = traceback.tb_lineno(get_last_traceback(e_traceback))
-            return (-1, "RemoteExec script failed!\n\nLine %d: %s\n\t%s" %
-                    (line_no, e_value, script.splitlines()[line_no - 1]))
-        return (0, kwargs.get("returnVal", None))
-
     def GetVersions(self):
         return platform_module.system() + " " + platform_module.release()
 
+    @RunInMain
+    def ExtendedCall(self, method, argument):
+        """ Dispatch argument to registered service """
+        return self.extended_calls[method](argument)
+
+    def RegisterExtendedCall(method, callback):
+        self.extended_calls[method] = callback
+
+    def UnregisterExtendedCall(method):
+        del self.extended_calls[method]
+
--- a/runtime/eRPCServer.py	Fri Oct 25 14:51:38 2024 +0200
+++ b/runtime/eRPCServer.py	Mon Oct 28 16:02:47 2024 +0100
@@ -47,6 +47,7 @@
     "SeedBlob":ReturnAsLastOutput,
     "SetTraceVariablesList": ReturnAsLastOutput,
     "StopPLC":ReturnAsLastOutput,
+    "ExtendedCall":ReturnAsLastOutput,
 }
 
 ArgsWrappers = {