# HG changeset patch # User Edouard Tisserant # Date 1716069572 -7200 # Node ID d303aab8f68b3fe43f2a06d17726b38122d99bda # Parent ebd25bbe5a73cb9f31ca720be5919ea8b99988ed C++ runtime: WIP. Continue PLCObject implementation. diff -r ebd25bbe5a73 -r d303aab8f68b C_runtime/PLCObject.cpp --- 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"); +} + diff -r ebd25bbe5a73 -r d303aab8f68b C_runtime/PLCObject.hpp --- 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 #include +#include +#include + #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, 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 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 diff -r ebd25bbe5a73 -r d303aab8f68b C_runtime/README.md --- 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 diff -r ebd25bbe5a73 -r d303aab8f68b targets/plc_debug.c --- 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;