C++ runtime: WIP. Continue PLCObject implementation.
authorEdouard Tisserant <edouard.tisserant@gmail.com>
Sat, 18 May 2024 23:59:32 +0200
changeset 3945 d303aab8f68b
parent 3944 ebd25bbe5a73
child 3946 8815b44be31e
C++ runtime: WIP. Continue PLCObject implementation.
C_runtime/PLCObject.cpp
C_runtime/PLCObject.hpp
C_runtime/README.md
targets/plc_debug.c
--- a/C_runtime/PLCObject.cpp	Wed May 15 18:50:26 2024 +0200
+++ b/C_runtime/PLCObject.cpp	Sat May 18 23:59:32 2024 +0200
@@ -23,6 +23,13 @@
 
 PLCObject::PLCObject(void)
 {
+    m_status.PLCstatus = Empty;
+    m_handle = NULL;
+    m_debugToken = 0;
+    m_plcID.ID = "N/A"; // TODO
+    m_plcID.PSK = "N/A"; // TODO
+    m_argc = 0;
+    m_argv = NULL;
 }
 
 PLCObject::~PLCObject(void)
@@ -114,7 +121,37 @@
 uint32_t PLCObject::GetTraceVariables(
     uint32_t debugToken, TraceVariables *traces)
 {
-    // XXX TODO
+    if(debugToken != m_debugToken)
+    {
+        return EINVAL;
+    }
+
+    // Check if there are any traces
+    m_tracesMutex.lock();
+    size_t sz = m_traces.size();
+    if(sz == 0)
+    {
+        m_tracesMutex.unlock();
+        return ENOENT;
+    }
+
+    // Allocate memory for traces
+    traces->traces.elements = (trace_sample *)malloc(sz * sizeof(trace_sample));
+    if(traces->traces.elements == NULL)
+    {
+        m_tracesMutex.unlock();
+        return ENOMEM;
+    }
+    traces->traces.elementsCount = sz;
+
+    // Copy traces from vector
+    memcpy(traces->traces.elements, m_traces.data(), sz * sizeof(trace_sample));
+
+    // Clear the vector
+    // note that the data is not freed here, it is meant to be freed by eRPC server code
+    m_traces.clear();
+    m_tracesMutex.unlock();
+
     return 0;
 }
 
@@ -169,6 +206,22 @@
     const char *md5sum, const binary_t *plcObjectBlobID,
     const list_extra_file_1_t *extrafiles, bool *success)
 {
+    if(m_status.PLCstatus == Started)
+    {
+        return EBUSY;
+    }
+
+    if(m_status.PLCstatus == Broken)
+    {
+        return EINVAL;
+    }
+
+    // Unload the PLC object
+    UnLoadPLC();
+
+    // Purge the PLC object
+    PurgePLC();
+
     // Concatenate md5sum and shared object extension to obtain filename
     std::filesystem::path filename =
         std::filesystem::path(md5sum) += SHARED_OBJECT_EXT;
@@ -238,8 +291,11 @@
 {
     // Unload the shared object file
     FOR_EACH_PLC_SYMBOLS_DO(ULSYM);
-    dlclose(m_handle);
-    
+    if(m_handle != NULL)
+    {
+        dlclose(m_handle);
+        m_handle = NULL;
+    }
     return 0;
 }
 
@@ -256,9 +312,54 @@
     return 0;
 }
 
+uint32_t PLCObject::PurgePLC(void){
+    // Purge all blobs
+    PurgeBlobs();
+
+    // Open the extra files list
+    std::ifstream extra_files_log(std::string(ExtraFilesList), std::ios::binary);
+
+    // Remove extra files
+    std::string extra_file;
+    while (std::getline(extra_files_log, extra_file))
+    {
+        std::filesystem::remove(extra_file);
+    }
+
+    // Load the last transferred PLC md5 hex digest
+    std::string md5sum;
+    std::ifstream(std::string(LastTransferredPLC), std::ios::binary) >> md5sum;
+
+    // Concatenate md5sum and shared object extension to obtain filename
+    std::filesystem::path filename =
+        std::filesystem::path(md5sum) += SHARED_OBJECT_EXT;
+
+    // Remove the PLC object shared object file
+    std::filesystem::remove(filename);
+
+    // Remove the last transferred PLC md5 hex digest
+    std::filesystem::remove(std::string(LastTransferredPLC));
+
+    // Remove the extra files list
+    std::filesystem::remove(std::string(ExtraFilesList));
+
+    return 0;
+}
+
 uint32_t PLCObject::RepairPLC(void)
 {
-    // XXX TODO
+    // Repair the PLC object
+
+    if(m_status.PLCstatus == Broken)
+    {
+        // Unload the PLC object
+        UnLoadPLC();
+
+        // Purge the PLC object
+        PurgePLC();
+    }
+
+
     LogMessage(LOG_WARNING, "RepairPLC not implemented");
     return 0;
 }
@@ -301,11 +402,53 @@
 }
 
 uint32_t PLCObject::SetTraceVariablesList(
-    const list_trace_order_1_t *orders, uint32_t *debugtoken)
-{
-    // XXX TODO
-    LogMessage(LOG_WARNING, "SetTraceVariablesList not implemented");
-    return 0;
+    const list_trace_order_1_t *orders, int32_t *debugtoken)
+{
+    // increment debug token
+    m_debugToken++;
+
+    if(orders->elementsCount == 0)
+    {
+        // actually disables debug
+        m_PLCSyms.suspendDebug(1);
+        *debugtoken = -5; // DEBUG_SUSPENDED
+        return 0;
+    }
+
+    // suspend debug before any operation
+    int res = m_PLCSyms.suspendDebug(0);
+    if(res == 0)
+    {
+        // forget about all previous debug variables
+        m_PLCSyms.ResetDebugVariables();
+
+        // call RegisterTraceVariables for each trace order
+        for (int i = 0; i < orders->elementsCount; i++)
+        {
+            trace_order *order = orders->elements + i;
+            res = m_PLCSyms.RegisterDebugVariable(order->idx, order->force.data, order->force.dataLength);
+            if(res != 0)
+            {
+                // if any error, disable debug
+                // since debug is already suspended, resume it first
+                m_PLCSyms.ResumeDebug();
+                m_PLCSyms.suspendDebug(1);
+                *debugtoken = -res;
+                return EINVAL;
+            }
+        }
+
+        // Start debug thread if not already started
+        if(!m_traceThread.joinable())
+        {
+            m_traceThread = std::thread(&PLCObject::TraceThreadProc, this);
+        }
+
+        m_PLCSyms.ResumeDebug();
+        *debugtoken = m_debugToken;
+        return 0;
+    }
+    return res;
 }
 
 uint32_t PLCObject::StartPLC(void)
@@ -318,6 +461,7 @@
         return res;
     }
     m_status.PLCstatus = Started;
+
     return 0;
 }
 
@@ -325,13 +469,20 @@
 {
     LogMessage(LOG_INFO, "Stopping PLC");
     uint32_t res = m_PLCSyms.stopPLC();
-    if(res != 0)
-    {
+    if(res == 0)
+    {
+        m_status.PLCstatus = Stopped;
+    } else {
         m_status.PLCstatus = Broken;
-        return res;
-    }
-    m_status.PLCstatus = Stopped;
-    return 0;
+    }
+
+    // Stop debug thread
+    if(m_traceThread.joinable())
+    {
+        m_traceThread.join();
+    }
+
+    return res;
 }
 
 uint32_t PLCObject::LogMessage(uint8_t level, std::string message)
@@ -339,3 +490,64 @@
     // Log std::string message with given level
     return m_PLCSyms.LogMessage(level, (char *)message.c_str(), message.size());
 }
+
+void PLCObject::TraceThreadProc(void)
+{
+    uint32_t err = 0;
+
+    m_PLCSyms.ResumeDebug();
+
+    while(m_status.PLCstatus == Started)
+    {
+        unsigned int tick;
+        unsigned int size;
+        void * buff;
+
+        // Data allocated here is meant to be freed by eRPC server code
+        uint8_t* ourData = NULL;
+
+        m_PLClibMutex.lock();
+
+        int res = m_PLCSyms.GetDebugData(&tick, &size, &buff);
+
+        if(res == 0)
+        {   
+            ourData = (uint8_t *)malloc(size);
+            if(ourData != NULL)
+            {
+                memcpy(ourData, buff, size);
+            }
+            m_PLCSyms.FreeDebugData();
+        }
+
+        m_PLClibMutex.unlock();
+
+        if(ourData == NULL)
+        {
+            err = res == 0 ? ENOMEM : res;
+            break;
+
+        } else {   
+
+            m_tracesMutex.lock();
+
+            m_traces.push_back(trace_sample{tick, binary_t{ourData, size}});
+
+            m_tracesMutex.unlock();
+        }
+    }
+
+    // Free trace buffer
+    m_tracesMutex.lock();
+    for(trace_sample s : m_traces){
+        free(s.TraceBuffer.data);
+    }
+    m_traces.clear();
+    m_tracesMutex.unlock();
+
+    LogMessage(err ? LOG_CRITICAL : LOG_INFO,
+        err == ENOMEM ? "Out of memory in TraceThreadProc" : 
+        err ? "TraceThreadProc ended because of error" : 
+        "TraceThreadProc ended normally");
+}
+
--- a/C_runtime/PLCObject.hpp	Wed May 15 18:50:26 2024 +0200
+++ b/C_runtime/PLCObject.hpp	Sat May 18 23:59:32 2024 +0200
@@ -8,6 +8,9 @@
 
 #include <map>
 #include <vector>
+#include <mutex>
+#include <thread>
+ 
 #include "blob.hpp"
 
 #include "erpc_PLCObject_interface.hpp"
@@ -36,7 +39,7 @@
     void (*ResetDebugVariables)(void);
     int (*RegisterDebugVariable)(unsigned int idx, void* force, size_t force_size);
     void (*FreeDebugData)(void);
-    int (*GetDebugData)(unsigned long *tick, unsigned long *size, void **buffer);
+    int (*GetDebugData)(unsigned int *tick, unsigned int *size, void **buffer);
     int (*suspendDebug)(int disable);
     void (*ResumeDebug)(void);
     void (*ResetLogCount)(void);
@@ -64,12 +67,10 @@
         uint32_t RepairPLC(void);
         uint32_t ResetLogCount(void);
         uint32_t SeedBlob(const binary_t * seed, binary_t * blobID);
-        uint32_t SetTraceVariablesList(const list_trace_order_1_t * orders, uint32_t * debugtoken);
+        uint32_t SetTraceVariablesList(const list_trace_order_1_t * orders, int32_t * debugtoken);
         uint32_t StartPLC(void);
         uint32_t StopPLC(bool * success);
 
-        // 
-
     private:
         // A map of all the blobs
         std::map<std::vector<uint8_t>, Blob*> m_mapBlobIDToBlob;
@@ -77,6 +78,9 @@
         // PLC object library handle
         void * m_handle;
 
+        // Shared object mutex
+        std::mutex m_PLClibMutex;
+
         // Symbols resolved from the PLC object
         PLCSyms m_PLCSyms;
 
@@ -90,16 +94,24 @@
         // PLC ID
         PSKID m_plcID;
 
+        // Debug token, used for consistency check of traces
+        uint32_t m_debugToken;
+
+        // Trace thread
+        std::thread m_traceThread;
+
+        // Trace thread mutex
+        std::mutex m_tracesMutex;
+
+        // Trace double buffer
+        std::vector<trace_sample> m_traces;
+
         uint32_t BlobAsFile(const binary_t * BlobID, std::filesystem::path filename);
         uint32_t LoadPLC(void);
         uint32_t UnLoadPLC(void);
         uint32_t LogMessage(uint8_t level, std::string message);
- 
-
-
-
-
-    
+        uint32_t PurgePLC(void);
+        void TraceThreadProc(void);
 };
 
 #endif
\ No newline at end of file
--- a/C_runtime/README.md	Wed May 15 18:50:26 2024 +0200
+++ b/C_runtime/README.md	Sat May 18 23:59:32 2024 +0200
@@ -8,4 +8,8 @@
 
 ## Description
 
-Beremiz C++ runtime. Loads shared object produced by Beremiz but doesn't use python.
\ No newline at end of file
+Beremiz C++ runtime. Loads shared object produced by Beremiz but doesn't use python.
+
+## ERPC interface
+
+erpcgen ../erpc_interface/erpc_PLCObject.erpc
\ No newline at end of file
--- a/targets/plc_debug.c	Wed May 15 18:50:26 2024 +0200
+++ b/targets/plc_debug.c	Sat May 18 23:59:32 2024 +0200
@@ -492,7 +492,7 @@
 }
 int WaitDebugData(unsigned long *tick);
 /* Wait until debug data ready and return pointer to it */
-int GetDebugData(unsigned long *tick, unsigned long *size, void **buffer){
+int GetDebugData(unsigned int *tick, unsigned int *size, void **buffer){
     int wait_error = WaitDebugData(tick);
     if(!wait_error){
         *size = trace_buffer_cursor - trace_buffer;