--- a/.github/workflows/run_tests_in_docker.yml Mon May 27 11:16:27 2024 +0200
+++ b/.github/workflows/run_tests_in_docker.yml Wed Jun 05 15:05:54 2024 +0200
@@ -18,7 +18,7 @@
- uses: actions/checkout@v3
with:
repository: beremiz/matiec
- ref: e5be6a1f036d21cd7b5ee75ef352783a7cfcc1a7
+ ref: d3196244bf50b2d6d512a0240e10dfa75edbeb7b
path: matiec
- uses: actions/checkout@v3
--- a/.gitignore Mon May 27 11:16:27 2024 +0200
+++ b/.gitignore Wed Jun 05 15:05:54 2024 +0200
@@ -7,3 +7,11 @@
**/my_*.der
**/my_*.pem
tests/tools/Docker/requirements.txt
+**/management.json
+**/.secret
+
+doc/_build/**
+doc/locale/**
+
+C_runtime/**/*.d
+C_runtime/**/*.o
\ No newline at end of file
--- a/C_runtime/Makefile Mon May 27 11:16:27 2024 +0200
+++ b/C_runtime/Makefile Wed Jun 05 15:05:54 2024 +0200
@@ -7,13 +7,17 @@
RUNTIME_ROOT = $(abspath $(dir $(lastword $(MAKEFILE_LIST))))
ERPC_ROOT ?= $(abspath $(RUNTIME_ROOT)/../../erpc)
ERPC_C_ROOT = $(ERPC_ROOT)/erpc_c
+BEREMIZ_ROOT = $(abspath $(RUNTIME_ROOT)/..)
+MATIEC_ROOT ?= $(abspath $(RUNTIME_ROOT)/../../matiec)
INCLUDES += $(ERPC_C_ROOT)/infra \
$(ERPC_C_ROOT)/port \
$(ERPC_C_ROOT)/setup \
$(ERPC_C_ROOT)/transports \
$(ERPC_ROOT)/test/common/config \
- $(ERPC_ROOT)/erpcgen/src
+ $(ERPC_ROOT)/erpcgen/src \
+ $(BEREMIZ_ROOT)/targets \
+ $(MATIEC_ROOT)/lib/C
INCLUDES := $(foreach includes, $(INCLUDES), -I $(includes))
@@ -37,11 +41,11 @@
$(ERPC_C_ROOT)/transports/erpc_serial_transport.cpp \
$(ERPC_C_ROOT)/transports/erpc_tcp_transport.cpp
-SOURCES += $(RUNTIME_ROOT)/c_erpc_PLCObject_client.cpp \
- $(RUNTIME_ROOT)/c_erpc_PLCObject_server.cpp \
- $(RUNTIME_ROOT)/erpc_PLCObject_client.cpp \
+SOURCES += $(RUNTIME_ROOT)/erpc_PLCObject_client.cpp \
$(RUNTIME_ROOT)/erpc_PLCObject_interface.cpp \
$(RUNTIME_ROOT)/erpc_PLCObject_server.cpp \
+ $(RUNTIME_ROOT)/md5.cpp \
+ $(RUNTIME_ROOT)/blob.cpp \
$(RUNTIME_ROOT)/posix_main.cpp \
$(RUNTIME_ROOT)/PLCObject.cpp
--- a/C_runtime/PLCObject.cpp Mon May 27 11:16:27 2024 +0200
+++ b/C_runtime/PLCObject.cpp Wed Jun 05 15:05:54 2024 +0200
@@ -1,80 +1,644 @@
-#include <stdlib.h>
+#include <string.h>
+#include <filesystem>
+#include <dlfcn.h>
+#include <fstream>
+#include <iostream>
+#include <vector>
#include "Logging.hpp"
#include "PLCObject.hpp"
+#include "beremiz.h"
+
+
+// File name of the last transferred PLC md5 hex digest
+// with typo in the name, for compatibility with Python runtime
+#define LastTransferredPLC "lasttransferedPLC.md5"
+
+// File name of the extra files list
+#define ExtraFilesList "extra_files.txt"
+
+
+
+PLCObject::PLCObject(void)
+{
+ m_status.PLCstatus = Empty;
+ m_handle = NULL;
+ m_debugToken = 0;
+ m_argc = 0;
+ m_argv = NULL;
+ m_PSK_ID = "";
+ m_PSK_secret = "";
+}
+
PLCObject::~PLCObject(void)
{
}
-uint32_t PLCObject::AppendChunkToBlob(const binary_t * data, const binary_t * blobID, binary_t * newBlobID)
-{
- return 0;
-}
-
-uint32_t PLCObject::GetLogMessage(uint8_t level, uint32_t msgID, log_message * message)
-{
- return 0;
-}
-
-uint32_t PLCObject::GetPLCID(PSKID * plcID)
-{
- return 0;
-}
-
-uint32_t PLCObject::GetPLCstatus(PLCstatus * status)
-{
- return 0;
-}
-
-uint32_t PLCObject::GetTraceVariables(uint32_t debugToken, TraceVariables * traces)
-{
- return 0;
-}
-
-uint32_t PLCObject::MatchMD5(const char * MD5, bool * match)
-{
- return 0;
-}
-
-uint32_t PLCObject::NewPLC(const char * md5sum, const binary_t * plcObjectBlobID, const list_extra_file_1_t * extrafiles, bool * success)
-{
+uint32_t PLCObject::AppendChunkToBlob(
+ const binary_t *data, const binary_t *blobID, binary_t *newBlobID)
+{
+ // Append data to blob with given blobID
+ // Output new blob's md5 into newBlobID
+ // Return 0 if success
+
+ newBlobID->data = (uint8_t *)malloc(MD5::digestsize);
+ if (newBlobID->data == NULL)
+ {
+ return ENOMEM;
+ }
+
+ std::vector<uint8_t> k(blobID->data, blobID->data + blobID->dataLength);
+
+ auto nh = m_mapBlobIDToBlob.extract(k);
+ if (nh.empty())
+ {
+ return ENOENT;
+ }
+
+ Blob *blob = nh.mapped();
+
+ uint32_t res = blob->appendChunk(data->data, data->dataLength);
+ if (res != 0)
+ {
+ return res;
+ }
+
+ MD5::digest_t digest = blob->digest();
+
+ std::vector<uint8_t> nk((uint8_t*)digest.data, (uint8_t*)digest.data + MD5::digestsize);
+ nh.key() = nk;
+
+ m_mapBlobIDToBlob.insert(std::move(nh));
+
+ memcpy(newBlobID->data, digest.data, MD5::digestsize);
+ newBlobID->dataLength = MD5::digestsize;
+
+ return 0;
+}
+
+#define LOG_READ_BUFFER_SIZE 1 << 10 // 1KB
+
+uint32_t PLCObject::GetLogMessage(
+ uint8_t level, uint32_t msgID, log_message *message)
+{
+ char buf[LOG_READ_BUFFER_SIZE];
+ uint32_t tick;
+ uint32_t tv_sec;
+ uint32_t tv_nsec;
+
+ uint32_t resultLen = m_PLCSyms.GetLogMessage(
+ level, msgID, buf, LOG_READ_BUFFER_SIZE - 1,
+ &tick, &tv_sec, &tv_nsec);
+
+ if (resultLen == 0)
+ {
+ return ENOENT;
+ }
+
+ // Get log message with given msgID
+ message->msg = (char *)malloc(resultLen);
+ if (message->msg == NULL)
+ {
+ return ENOMEM;
+ }
+ // Copy the log message into eRPC message
+ memcpy(message->msg, buf, resultLen);
+ message->msg[resultLen + 1] = '\0';
+
+ message->tick = tick;
+ message->sec = tv_sec;
+ message->nsec = tv_nsec;
+
+ return 0;
+}
+
+uint32_t PLCObject::GetPLCID(PSKID *plcID)
+{
+ // Get PSK ID
+ plcID->ID = (char *)malloc(m_PSK_ID.size() + 1);
+ if (plcID->ID == NULL)
+ {
+ return ENOMEM;
+ }
+ memcpy(plcID->ID, m_PSK_ID.c_str(), m_PSK_ID.size());
+ plcID->ID[m_PSK_ID.size()] = '\0';
+
+ // Get PSK secret
+ plcID->PSK = (char *)malloc(m_PSK_secret.size() + 1);
+ if (plcID->PSK == NULL)
+ {
+ free(plcID->ID);
+ return ENOMEM;
+ }
+ memcpy(plcID->PSK, m_PSK_secret.c_str(), m_PSK_secret.size());
+ plcID->PSK[m_PSK_secret.size()] = '\0';
+
+ return 0;
+}
+
+uint32_t PLCObject::GetPLCstatus(PLCstatus *status)
+{
+ // Get PLC status
+ *status = m_status;
+ return 0;
+}
+
+uint32_t PLCObject::GetTraceVariables(
+ uint32_t debugToken, TraceVariables *traces)
+{
+ if(debugToken != m_debugToken)
+ {
+ return EINVAL;
+ }
+
+ // Check if there are any traces
+ m_tracesMutex.lock();
+ size_t sz = m_traces.size();
+ if(sz > 0)
+ {
+ // Allocate memory for traces
+ traces->traces.elements = (trace_sample *)malloc(sz * sizeof(trace_sample));
+ if(traces->traces.elements == NULL)
+ {
+ m_tracesMutex.unlock();
+ return ENOMEM;
+ }
+ // 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();
+
+ traces->traces.elementsCount = sz;
+ traces->PLCstatus = m_status.PLCstatus;
+
+ return 0;
+}
+
+uint32_t PLCObject::MatchMD5(const char *MD5, bool *match)
+{
+ // an empty PLC is never considered to match
+ if(m_status.PLCstatus == Empty)
+ {
+ *match = false;
+ return 0;
+ }
+
+ // Load the last transferred PLC md5 hex digest
+ std::string md5sum;
+ try {
+ std::ifstream(std::string(LastTransferredPLC), std::ios::binary) >> md5sum;
+ } catch (std::exception e) {
+ *match = false;
+ return 0;
+ }
+
+ // Compare the given MD5 with the last transferred PLC md5
+ *match = (md5sum == MD5);
+
+ return 0;
+}
+
+#if defined(_WIN32) || defined(_WIN64)
+// For Windows platform
+#define SHARED_OBJECT_EXT ".dll"
+#elif defined(__APPLE__) || defined(__MACH__)
+// For MacOS platform
+#define SHARED_OBJECT_EXT ".dylib"
+#else
+// For Linux/Unix platform
+#define SHARED_OBJECT_EXT ".so"
+#endif
+
+uint32_t PLCObject::BlobAsFile(
+ const binary_t *BlobID, std::filesystem::path filename)
+{
+ // Extract the blob from the map
+ auto nh = m_mapBlobIDToBlob.extract(
+ std::vector<uint8_t>(BlobID->data, BlobID->data + BlobID->dataLength));
+ if (nh.empty())
+ {
+ return ENOENT;
+ }
+ Blob *blob = nh.mapped();
+
+ // Realize the blob into a file
+ uint32_t res = blob->asFile(filename);
+
+ delete blob;
+
+ if (res != 0)
+ {
+ return res;
+ }
+ return 0;
+}
+
+uint32_t PLCObject::NewPLC(
+ const char *md5sum, const binary_t *plcObjectBlobID,
+ const list_extra_file_1_t *extrafiles, bool *success)
+{
+ if(m_status.PLCstatus == Started)
+ {
+ *success = false;
+ return EBUSY;
+ }
+
+ if(m_status.PLCstatus == Broken)
+ {
+ *success = false;
+ 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;
+
+ // Create the PLC object shared object file
+ BlobAsFile(plcObjectBlobID, filename);
+
+ // create "lasttransferedPLC.md5" file and Save md5sum in it
+ std::ofstream(std::string(LastTransferredPLC), std::ios::binary) << md5sum;
+
+ // create "extra_files.txt" file
+ std::ofstream extra_files_log(std::string(ExtraFilesList), std::ios::binary);
+
+ // Create extra files
+ for (int i = 0; i < extrafiles->elementsCount; i++)
+ {
+ extra_file *extrafile = extrafiles->elements + i;
+
+ BlobAsFile(plcObjectBlobID, extrafile->fname);
+
+ // Save the extra file name in "extra_files.txt"
+ extra_files_log << extrafile->fname << std::endl;
+ }
+
+ // Load the PLC object
+ uint32_t res = LoadPLC();
+ if (res != 0)
+ {
+ *success = false;
+ return res;
+ }
+
+ m_status.PLCstatus = Stopped;
+ *success = true;
+
+ return 0;
+}
+
+#define DLSYM(sym) \
+ do \
+ { \
+ m_PLCSyms.sym = (decltype(m_PLCSyms.sym))dlsym(m_handle, #sym); \
+ if (m_PLCSyms.sym == NULL) \
+ { \
+ /* TODO: use log instead */ \
+ std::cout << "Error dlsym " #sym ": " << dlerror() << std::endl; \
+ return errno; \
+ } \
+ } while (0);
+
+uint32_t PLCObject::LoadPLC(void)
+{
+ // Load the last transferred PLC md5 hex digest
+ std::string md5sum;
+ try {
+ std::ifstream(std::string(LastTransferredPLC), std::ios::binary) >> md5sum;
+ } catch (std::exception e) {
+ return ENOENT;
+ }
+
+ // Concatenate md5sum and shared object extension to obtain filename
+ std::filesystem::path filename(md5sum + SHARED_OBJECT_EXT);
+
+ // Load the shared object file
+ m_handle = dlopen(std::filesystem::absolute(filename).c_str(), RTLD_NOW);
+ if (m_handle == NULL)
+ {
+ std::cout << "Error: " << dlerror() << std::endl;
+ return errno;
+ }
+
+ // Resolve shared object symbols
+ FOR_EACH_PLC_SYMBOLS_DO(DLSYM);
+
+ // Set content of PLC_ID to md5sum
+ m_PLCSyms.PLC_ID = (uint8_t *)malloc(md5sum.size() + 1);
+ if (m_PLCSyms.PLC_ID == NULL)
+ {
+ return ENOMEM;
+ }
+ memcpy(m_PLCSyms.PLC_ID, md5sum.c_str(), md5sum.size());
+ m_PLCSyms.PLC_ID[md5sum.size()] = '\0';
+
+ return 0;
+}
+
+#define ULSYM(sym) \
+ do \
+ { \
+ m_PLCSyms.sym = NULL; \
+ } while (0);
+
+uint32_t PLCObject::UnLoadPLC(void)
+{
+ // Unload the shared object file
+ FOR_EACH_PLC_SYMBOLS_DO(ULSYM);
+ if(m_handle != NULL)
+ {
+ dlclose(m_handle);
+ m_handle = NULL;
+ }
return 0;
}
uint32_t PLCObject::PurgeBlobs(void)
{
+ // Purge all blobs
+
+ for (auto &blob : m_mapBlobIDToBlob)
+ {
+ delete blob.second;
+ }
+ m_mapBlobIDToBlob.clear();
+
+ return 0;
+}
+
+uint32_t PLCObject::PurgePLC(void)
+{
+
+ // 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;
+ try {
+ std::ifstream(std::string(LastTransferredPLC), std::ios::binary) >> md5sum;
+
+ // Remove the PLC object shared object file
+ std::filesystem::remove(md5sum + SHARED_OBJECT_EXT);
+ } catch (std::exception e) {
+ // ignored
+ }
+
+ try {
+ // 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));
+ } catch (std::exception e) {
+ // ignored
+ }
+
return 0;
}
uint32_t PLCObject::RepairPLC(void)
{
+ // 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;
}
uint32_t PLCObject::ResetLogCount(void)
{
- return 0;
-}
-
-uint32_t PLCObject::SeedBlob(const binary_t * seed, binary_t * blobID)
-{
- return 0;
-}
-
-uint32_t PLCObject::SetTraceVariablesList(const list_trace_order_1_t * orders, uint32_t * debugtoken)
-{
- return 0;
+ m_PLCSyms.ResetLogCount();
+ return 0;
+}
+
+uint32_t PLCObject::SeedBlob(const binary_t *seed, binary_t *blobID)
+{
+ // Create a blob with given seed
+ // Output new blob's md5 into blobID
+ // Return 0 if success
+
+ Blob *blob = NULL;
+ try
+ {
+ blob = new Blob(seed->data, seed->dataLength);
+ }
+ catch (int e)
+ {
+ return e;
+ }
+
+ MD5::digest_t digest = blob->digest();
+
+ std::vector<uint8_t> k((uint8_t*)digest.data, (uint8_t*)digest.data + MD5::digestsize);
+
+ m_mapBlobIDToBlob[k] = blob;
+
+ blobID->data = (uint8_t *)malloc(MD5::digestsize);
+ if (blobID->data == NULL)
+ {
+ return ENOMEM;
+ }
+ memcpy(blobID->data, digest.data, MD5::digestsize);
+ blobID->dataLength = MD5::digestsize;
+
+ return 0;
+}
+void PLCObject::PurgeTraceBuffer(void)
+{
+ // Free trace buffer
+ m_tracesMutex.lock();
+ for(trace_sample s : m_traces){
+ free(s.TraceBuffer.data);
+ }
+ m_traces.clear();
+ m_tracesMutex.unlock();
+}
+
+uint32_t PLCObject::SetTraceVariablesList(
+ const list_trace_order_1_t *orders, int32_t *debugtoken)
+{
+ if(m_status.PLCstatus == Empty)
+ {
+ return EINVAL;
+ }
+
+ // 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;
+ }
+ }
+
+ // old traces are not valid anymore
+ PurgeTraceBuffer();
+
+ // 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)
{
- return 0;
-}
-
-uint32_t PLCObject::StopPLC(bool * success)
-{
- return 0;
-}
+ LogMessage(LOG_INFO, "Starting PLC");
+ uint32_t res = m_PLCSyms.startPLC(m_argc, m_argv);
+ if(res != 0)
+ {
+ m_status.PLCstatus = Broken;
+ return res;
+ }
+ m_status.PLCstatus = Started;
+
+ return 0;
+}
+
+uint32_t PLCObject::StopPLC(bool *success)
+{
+ LogMessage(LOG_INFO, "Stopping PLC");
+ uint32_t res = m_PLCSyms.stopPLC();
+ if(res == 0)
+ {
+ m_status.PLCstatus = Stopped;
+ } else {
+ m_status.PLCstatus = Broken;
+ }
+
+ // Stop debug thread
+ if(m_traceThread.joinable())
+ {
+ m_traceThread.join();
+ }
+
+ return res;
+}
+
+uint32_t PLCObject::LogMessage(uint8_t level, std::string message)
+{
+ // if PLC isn't loaded, log to stdout
+ if(m_PLCSyms.LogMessage == NULL)
+ {
+ std::cout << level << message << std::endl;
+ return ENOSYS;
+ }
+
+ // 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();
+ }
+ }
+
+ PurgeTraceBuffer();
+
+ 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 Mon May 27 11:16:27 2024 +0200
+++ b/C_runtime/PLCObject.hpp Wed Jun 05 15:05:54 2024 +0200
@@ -6,30 +6,115 @@
#if !defined(_PLCObject_hpp_)
#define _PLCObject_hpp_
+#include <map>
+#include <vector>
+#include <mutex>
+#include <thread>
+
+#include "blob.hpp"
+
#include "erpc_PLCObject_interface.hpp"
using namespace erpcShim;
+#define FOR_EACH_PLC_SYMBOLS_DO(ACTION) \
+ ACTION(PLC_ID)\
+ ACTION(startPLC)\
+ ACTION(stopPLC)\
+ ACTION(ResetDebugVariables)\
+ ACTION(RegisterDebugVariable)\
+ ACTION(FreeDebugData)\
+ ACTION(GetDebugData)\
+ ACTION(suspendDebug)\
+ ACTION(resumeDebug)\
+ ACTION(ResetLogCount)\
+ ACTION(GetLogCount)\
+ ACTION(LogMessage)\
+ ACTION(GetLogMessage)
+
+extern "C" {
+ typedef struct s_PLCSyms{
+ uint8_t *PLC_ID;
+ int (*startPLC)(int argc,char **argv);
+ int (*stopPLC)(void);
+ void (*ResetDebugVariables)(void);
+ int (*RegisterDebugVariable)(unsigned int idx, void* force, size_t force_size);
+ void (*FreeDebugData)(void);
+ int (*GetDebugData)(unsigned int *tick, unsigned int *size, void **buffer);
+ int (*suspendDebug)(int disable);
+ void (*resumeDebug)(void);
+ void (*ResetLogCount)(void);
+ uint32_t (*GetLogCount)(uint8_t level);
+ int (*LogMessage)(uint8_t level, char* buf, uint32_t size);
+ uint32_t (*GetLogMessage)(uint8_t level, uint32_t msgidx, char* buf, uint32_t max_size, uint32_t* tick, uint32_t* tv_sec, uint32_t* tv_nsec);
+ } PLCSyms;
+}
class PLCObject : public BeremizPLCObjectService_interface
{
public:
+ PLCObject(void);
~PLCObject(void);
- virtual uint32_t AppendChunkToBlob(const binary_t * data, const binary_t * blobID, binary_t * newBlobID);
- virtual uint32_t GetLogMessage(uint8_t level, uint32_t msgID, log_message * message);
- virtual uint32_t GetPLCID(PSKID * plcID);
- virtual uint32_t GetPLCstatus(PLCstatus * status);
- virtual uint32_t GetTraceVariables(uint32_t debugToken, TraceVariables * traces);
- virtual uint32_t MatchMD5(const char * MD5, bool * match);
- virtual uint32_t NewPLC(const char * md5sum, const binary_t * plcObjectBlobID, const list_extra_file_1_t * extrafiles, bool * success);
- virtual uint32_t PurgeBlobs(void);
- virtual uint32_t RepairPLC(void);
- virtual uint32_t ResetLogCount(void);
- virtual uint32_t SeedBlob(const binary_t * seed, binary_t * blobID);
- virtual uint32_t SetTraceVariablesList(const list_trace_order_1_t * orders, uint32_t * debugtoken);
- virtual uint32_t StartPLC(void);
- virtual uint32_t StopPLC(bool * success);
+ // ERPC interface
+ uint32_t AppendChunkToBlob(const binary_t * data, const binary_t * blobID, binary_t * newBlobID);
+ uint32_t GetLogMessage(uint8_t level, uint32_t msgID, log_message * message);
+ uint32_t GetPLCID(PSKID * plcID);
+ uint32_t GetPLCstatus(PLCstatus * status);
+ uint32_t GetTraceVariables(uint32_t debugToken, TraceVariables * traces);
+ uint32_t MatchMD5(const char * MD5, bool * match);
+ uint32_t NewPLC(const char * md5sum, const binary_t * plcObjectBlobID, const list_extra_file_1_t * extrafiles, bool * success);
+ uint32_t PurgeBlobs(void);
+ 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, 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;
+
+ // PLC object library handle
+ void * m_handle;
+
+ // Shared object mutex
+ std::mutex m_PLClibMutex;
+
+ // Symbols resolved from the PLC object
+ PLCSyms m_PLCSyms;
+
+ // argc and argv for the PLC object
+ int m_argc;
+ char ** m_argv;
+
+ // PLC status
+ PLCstatus m_status;
+
+ // PSK
+ std::string m_PSK_ID;
+ std::string m_PSK_secret;
+
+ // 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 PurgeTraceBuffer(void);
+ void TraceThreadProc(void);
};
#endif
\ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/C_runtime/README.md Wed Jun 05 15:05:54 2024 +0200
@@ -0,0 +1,15 @@
+# Beremiz C++ runtime
+
+This project is licensed under the [LPGLv2](https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html) license.
+
+## Warning: Work in Progress
+
+**Please note that this project is currently a work in progress.**
+
+## Description
+
+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
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/C_runtime/blob.cpp Wed Jun 05 15:05:54 2024 +0200
@@ -0,0 +1,82 @@
+
+#include <stdlib.h>
+#include <errno.h>
+
+#include "blob.hpp"
+#include <unistd.h>
+
+Blob::Blob(uint8_t *seedData, size_t seedLength)
+{
+ // Create a temporary file to store blob data
+ // not using tmpfile() because we need to know the filename
+ // for later renaming and avoid deletion on close
+
+ // Assume that a tmp directory exists in the current directory
+ uint8_t template_name[] = "tmp/blobXXXXXX";
+ int fd = mkstemp((char *)template_name);
+ if (fd == -1) {
+ throw errno;
+ }
+
+ // Open file for stdlib I/O
+ m_file = fdopen(fd, "w+");
+ if (m_file == NULL) {
+ throw errno;
+ }
+
+ // Save a copy of the filename
+ m_filename = (char *)template_name;
+
+ // Seed the MD5 hash with the seed data
+ md5.update(seedData, seedLength);
+}
+
+Blob::~Blob() {
+ if (m_file != NULL) {
+ std::fclose(m_file);
+ std::remove(m_filename.c_str());
+ }
+}
+
+MD5::digest_t Blob::digest() {
+ return md5.digest();
+}
+
+uint32_t Blob::appendChunk(uint8_t *data, size_t length) {
+ // Write data to file
+ if (std::fwrite(data, 1, length, m_file) != length) {
+ return errno;
+ }
+
+ // Update MD5 hash
+ md5.update(data, length);
+
+ return 0;
+}
+
+uint32_t Blob::asFile(std::filesystem::path &filename)
+{
+ // Flush file
+ if (std::fflush(m_file) != 0) {
+ return errno;
+ }
+
+ // Sync file to disk
+ if (fsync(fileno(m_file)) != 0) {
+ return errno;
+ }
+
+ // Close file
+ if (std::fclose(m_file) != 0) {
+ return errno;
+ }
+
+ m_file = NULL;
+
+ // Rename temp file to final file
+ if (std::rename(m_filename.c_str(), filename.c_str()) != 0) {
+ return errno;
+ }
+
+ return 0;
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/C_runtime/blob.hpp Wed Jun 05 15:05:54 2024 +0200
@@ -0,0 +1,24 @@
+#ifndef BLOB_HPP
+#define BLOB_HPP
+
+#include <string>
+#include <filesystem>
+
+#include "md5.hpp"
+
+class Blob
+{
+public:
+ Blob(uint8_t *seedData, size_t seedLength);
+ ~Blob();
+ MD5::digest_t digest();
+ uint32_t appendChunk(uint8_t *data, size_t length);
+ uint32_t asFile(std::filesystem::path &filename);
+
+private:
+ MD5 md5;
+ std::FILE * m_file;
+ std::filesystem::path m_filename;
+};
+
+#endif // BLOB_HPP
--- a/C_runtime/c_erpc_PLCObject_client.cpp Mon May 27 11:16:27 2024 +0200
+++ b/C_runtime/c_erpc_PLCObject_client.cpp Wed Jun 05 15:05:54 2024 +0200
@@ -1,5 +1,5 @@
/*
- * Generated by erpcgen 1.11.0 on Wed Mar 27 13:43:44 2024.
+ * Generated by erpcgen 1.12.0 on Mon May 20 17:54:19 2024.
*
* AUTOGENERATED - DO NOT EDIT
*/
@@ -108,7 +108,7 @@
return result;
}
-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 result;
result = s_BeremizPLCObjectService_client->SetTraceVariablesList(orders, debugtoken);
--- a/C_runtime/c_erpc_PLCObject_client.h Mon May 27 11:16:27 2024 +0200
+++ b/C_runtime/c_erpc_PLCObject_client.h Wed Jun 05 15:05:54 2024 +0200
@@ -1,5 +1,5 @@
/*
- * Generated by erpcgen 1.11.0 on Wed Mar 27 13:43:44 2024.
+ * Generated by erpcgen 1.12.0 on Mon May 20 17:54:19 2024.
*
* AUTOGENERATED - DO NOT EDIT
*/
@@ -64,7 +64,7 @@
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);
--- a/C_runtime/c_erpc_PLCObject_server.cpp Mon May 27 11:16:27 2024 +0200
+++ b/C_runtime/c_erpc_PLCObject_server.cpp Wed Jun 05 15:05:54 2024 +0200
@@ -1,5 +1,5 @@
/*
- * Generated by erpcgen 1.11.0 on Wed Mar 27 13:43:44 2024.
+ * Generated by erpcgen 1.12.0 on Mon May 20 17:54:19 2024.
*
* AUTOGENERATED - DO NOT EDIT
*/
@@ -109,7 +109,7 @@
return result;
}
- 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 result;
result = ::SetTraceVariablesList(orders, debugtoken);
--- a/C_runtime/c_erpc_PLCObject_server.h Mon May 27 11:16:27 2024 +0200
+++ b/C_runtime/c_erpc_PLCObject_server.h Wed Jun 05 15:05:54 2024 +0200
@@ -1,5 +1,5 @@
/*
- * Generated by erpcgen 1.11.0 on Wed Mar 27 13:43:44 2024.
+ * Generated by erpcgen 1.12.0 on Mon May 20 17:54:19 2024.
*
* AUTOGENERATED - DO NOT EDIT
*/
@@ -65,7 +65,7 @@
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);
--- a/C_runtime/erpc_PLCObject_client.cpp Mon May 27 11:16:27 2024 +0200
+++ b/C_runtime/erpc_PLCObject_client.cpp Wed Jun 05 15:05:54 2024 +0200
@@ -1,5 +1,5 @@
/*
- * Generated by erpcgen 1.11.0 on Wed Mar 27 13:43:44 2024.
+ * Generated by erpcgen 1.12.0 on Mon May 20 17:54:19 2024.
*
* AUTOGENERATED - DO NOT EDIT
*/
@@ -12,7 +12,7 @@
#include "erpc_PLCObject_client.hpp"
#include "erpc_manually_constructed.hpp"
-#if 11100 != ERPC_VERSION_NUMBER
+#if 11200 != ERPC_VERSION_NUMBER
#error "The generated shim code version is different to the rest of eRPC code."
#endif
@@ -1026,7 +1026,7 @@
}
// BeremizPLCObjectService interface SetTraceVariablesList function client shim.
-uint32_t BeremizPLCObjectService_client::SetTraceVariablesList(const list_trace_order_1_t * orders, uint32_t * debugtoken)
+uint32_t BeremizPLCObjectService_client::SetTraceVariablesList(const list_trace_order_1_t * orders, int32_t * debugtoken)
{
erpc_status_t err = kErpcStatus_Success;
--- a/C_runtime/erpc_PLCObject_client.hpp Mon May 27 11:16:27 2024 +0200
+++ b/C_runtime/erpc_PLCObject_client.hpp Wed Jun 05 15:05:54 2024 +0200
@@ -1,5 +1,5 @@
/*
- * Generated by erpcgen 1.11.0 on Wed Mar 27 13:43:44 2024.
+ * Generated by erpcgen 1.12.0 on Mon May 20 17:54:19 2024.
*
* AUTOGENERATED - DO NOT EDIT
*/
@@ -44,7 +44,7 @@
virtual uint32_t SeedBlob(const binary_t * seed, binary_t * blobID);
- virtual uint32_t SetTraceVariablesList(const list_trace_order_1_t * orders, uint32_t * debugtoken);
+ virtual uint32_t SetTraceVariablesList(const list_trace_order_1_t * orders, int32_t * debugtoken);
virtual uint32_t StartPLC(void);
--- a/C_runtime/erpc_PLCObject_common.h Mon May 27 11:16:27 2024 +0200
+++ b/C_runtime/erpc_PLCObject_common.h Wed Jun 05 15:05:54 2024 +0200
@@ -1,5 +1,5 @@
/*
- * Generated by erpcgen 1.11.0 on Wed Mar 27 13:43:44 2024.
+ * Generated by erpcgen 1.12.0 on Mon May 20 17:54:19 2024.
*
* AUTOGENERATED - DO NOT EDIT
*/
@@ -19,7 +19,7 @@
#include "erpc_version.h"
-#if 11100 != ERPC_VERSION_NUMBER
+#if 11200 != ERPC_VERSION_NUMBER
#error "The generated shim code version is different to the rest of eRPC code."
#endif
--- a/C_runtime/erpc_PLCObject_common.hpp Mon May 27 11:16:27 2024 +0200
+++ b/C_runtime/erpc_PLCObject_common.hpp Wed Jun 05 15:05:54 2024 +0200
@@ -1,5 +1,5 @@
/*
- * Generated by erpcgen 1.11.0 on Wed Mar 27 13:43:44 2024.
+ * Generated by erpcgen 1.12.0 on Mon May 20 17:54:19 2024.
*
* AUTOGENERATED - DO NOT EDIT
*/
@@ -15,7 +15,7 @@
#include "erpc_version.h"
-#if 11100 != ERPC_VERSION_NUMBER
+#if 11200 != ERPC_VERSION_NUMBER
#error "The generated shim code version is different to the rest of eRPC code."
#endif
--- a/C_runtime/erpc_PLCObject_interface.cpp Mon May 27 11:16:27 2024 +0200
+++ b/C_runtime/erpc_PLCObject_interface.cpp Wed Jun 05 15:05:54 2024 +0200
@@ -1,5 +1,5 @@
/*
- * Generated by erpcgen 1.11.0 on Wed Mar 27 13:43:44 2024.
+ * Generated by erpcgen 1.12.0 on Mon May 20 17:54:19 2024.
*
* AUTOGENERATED - DO NOT EDIT
*/
@@ -7,7 +7,7 @@
#include "erpc_PLCObject_interface.hpp"
-#if 11100 != ERPC_VERSION_NUMBER
+#if 11200 != ERPC_VERSION_NUMBER
#error "The generated shim code version is different to the rest of eRPC code."
#endif
--- a/C_runtime/erpc_PLCObject_interface.hpp Mon May 27 11:16:27 2024 +0200
+++ b/C_runtime/erpc_PLCObject_interface.hpp Wed Jun 05 15:05:54 2024 +0200
@@ -1,5 +1,5 @@
/*
- * Generated by erpcgen 1.11.0 on Wed Mar 27 13:43:44 2024.
+ * Generated by erpcgen 1.12.0 on Mon May 20 17:54:19 2024.
*
* AUTOGENERATED - DO NOT EDIT
*/
@@ -58,7 +58,7 @@
virtual uint32_t SeedBlob(const binary_t * seed, binary_t * blobID) = 0;
- virtual uint32_t SetTraceVariablesList(const list_trace_order_1_t * orders, uint32_t * debugtoken) = 0;
+ virtual uint32_t SetTraceVariablesList(const list_trace_order_1_t * orders, int32_t * debugtoken) = 0;
virtual uint32_t StartPLC(void) = 0;
--- a/C_runtime/erpc_PLCObject_server.cpp Mon May 27 11:16:27 2024 +0200
+++ b/C_runtime/erpc_PLCObject_server.cpp Wed Jun 05 15:05:54 2024 +0200
@@ -1,5 +1,5 @@
/*
- * Generated by erpcgen 1.11.0 on Wed Mar 27 13:43:44 2024.
+ * Generated by erpcgen 1.12.0 on Mon May 20 17:54:19 2024.
*
* AUTOGENERATED - DO NOT EDIT
*/
@@ -12,7 +12,7 @@
#endif
#include "erpc_manually_constructed.hpp"
-#if 11100 != ERPC_VERSION_NUMBER
+#if 11200 != ERPC_VERSION_NUMBER
#error "The generated shim code version is different to the rest of eRPC code."
#endif
@@ -1182,7 +1182,7 @@
{
codec->updateStatus(kErpcStatus_MemoryError);
}
- uint32_t debugtoken;
+ int32_t debugtoken;
uint32_t result;
// startReadMessage() was already called before this shim was invoked.
--- a/C_runtime/erpc_PLCObject_server.hpp Mon May 27 11:16:27 2024 +0200
+++ b/C_runtime/erpc_PLCObject_server.hpp Wed Jun 05 15:05:54 2024 +0200
@@ -1,5 +1,5 @@
/*
- * Generated by erpcgen 1.11.0 on Wed Mar 27 13:43:44 2024.
+ * Generated by erpcgen 1.12.0 on Mon May 20 17:54:19 2024.
*
* AUTOGENERATED - DO NOT EDIT
*/
@@ -13,7 +13,7 @@
#include "erpc_server.hpp"
#include "erpc_codec.hpp"
-#if 11100 != ERPC_VERSION_NUMBER
+#if 11200 != ERPC_VERSION_NUMBER
#error "The generated shim code version is different to the rest of eRPC code."
#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/C_runtime/md5.cpp Wed Jun 05 15:05:54 2024 +0200
@@ -0,0 +1,325 @@
+/* MD5
+ converted to C++ class by Frank Thilo (thilo@unix-ag.org)
+ for bzflag (http://www.bzflag.org)
+
+ based on:
+
+ md5.h and md5.c
+ reference implemantion of RFC 1321
+
+ Copyright (C) 1991-2, RSA Data Security, Inc. Created 1991. All
+rights reserved.
+
+License to copy and use this software is granted provided that it
+is identified as the "RSA Data Security, Inc. MD5 Message-Digest
+Algorithm" in all material mentioning or referencing this software
+or this function.
+
+License is also granted to make and use derivative works provided
+that such works are identified as "derived from the RSA Data
+Security, Inc. MD5 Message-Digest Algorithm" in all material
+mentioning or referencing the derived work.
+
+RSA Data Security, Inc. makes no representations concerning either
+the merchantability of this software or the suitability of this
+software for any particular purpose. It is provided "as is"
+without express or implied warranty of any kind.
+
+These notices must be retained in any copies of any part of this
+documentation and/or software.
+
+*/
+
+#include <stdlib.h>
+#include <string.h>
+
+/* interface header */
+#include "md5.hpp"
+
+// Constants for MD5Transform routine.
+#define S11 7
+#define S12 12
+#define S13 17
+#define S14 22
+#define S21 5
+#define S22 9
+#define S23 14
+#define S24 20
+#define S31 4
+#define S32 11
+#define S33 16
+#define S34 23
+#define S41 6
+#define S42 10
+#define S43 15
+#define S44 21
+
+///////////////////////////////////////////////
+
+// F, G, H and I are basic MD5 functions.
+inline uint32_t MD5::F(uint32_t x, uint32_t y, uint32_t z)
+{
+ return x & y | ~x & z;
+}
+
+inline uint32_t MD5::G(uint32_t x, uint32_t y, uint32_t z)
+{
+ return x & z | y & ~z;
+}
+
+inline uint32_t MD5::H(uint32_t x, uint32_t y, uint32_t z)
+{
+ return x ^ y ^ z;
+}
+
+inline uint32_t MD5::I(uint32_t x, uint32_t y, uint32_t z)
+{
+ return y ^ (x | ~z);
+}
+
+// rotate_left rotates x left n bits.
+inline uint32_t MD5::rotate_left(uint32_t x, int n)
+{
+ return (x << n) | (x >> (32 - n));
+}
+
+// FF, GG, HH, and II transformations for rounds 1, 2, 3, and 4.
+// Rotation is separate from addition to prevent recomputation.
+inline void MD5::FF(uint32_t &a, uint32_t b, uint32_t c, uint32_t d, uint32_t x, uint32_t s, uint32_t ac)
+{
+ a = rotate_left(a + F(b, c, d) + x + ac, s) + b;
+}
+
+inline void MD5::GG(uint32_t &a, uint32_t b, uint32_t c, uint32_t d, uint32_t x, uint32_t s, uint32_t ac)
+{
+ a = rotate_left(a + G(b, c, d) + x + ac, s) + b;
+}
+
+inline void MD5::HH(uint32_t &a, uint32_t b, uint32_t c, uint32_t d, uint32_t x, uint32_t s, uint32_t ac)
+{
+ a = rotate_left(a + H(b, c, d) + x + ac, s) + b;
+}
+
+inline void MD5::II(uint32_t &a, uint32_t b, uint32_t c, uint32_t d, uint32_t x, uint32_t s, uint32_t ac)
+{
+ a = rotate_left(a + I(b, c, d) + x + ac, s) + b;
+}
+
+//////////////////////////////////////////////
+
+// default ctor, just initailize
+MD5::MD5()
+{
+ init();
+}
+
+//////////////////////////////
+
+void MD5::init()
+{
+ count[0] = 0;
+ count[1] = 0;
+
+ // load magic initialization constants.
+ state[0] = 0x67452301;
+ state[1] = 0xefcdab89;
+ state[2] = 0x98badcfe;
+ state[3] = 0x10325476;
+}
+
+//////////////////////////////
+
+// decodes input (unsigned char) into output (uint32_t). Assumes len is a multiple of 4.
+void MD5::decode(uint32_t output[], const uint8_t input[], size_type len)
+{
+ for (unsigned int i = 0, j = 0; j < len; i++, j += 4)
+ output[i] = ((uint32_t)input[j]) | (((uint32_t)input[j + 1]) << 8) |
+ (((uint32_t)input[j + 2]) << 16) | (((uint32_t)input[j + 3]) << 24);
+}
+
+//////////////////////////////
+
+// encodes input (uint32_t) into output (unsigned char). Assumes len is
+// a multiple of 4.
+void MD5::encode(uint8_t output[], const uint32_t input[], size_type len)
+{
+ for (size_type i = 0, j = 0; j < len; i++, j += 4)
+ {
+ output[j] = input[i] & 0xff;
+ output[j + 1] = (input[i] >> 8) & 0xff;
+ output[j + 2] = (input[i] >> 16) & 0xff;
+ output[j + 3] = (input[i] >> 24) & 0xff;
+ }
+}
+
+//////////////////////////////
+
+// apply MD5 algo on a block
+void MD5::transform(const uint8_t block[blocksize])
+{
+ uint32_t a = state[0], b = state[1], c = state[2], d = state[3], x[16];
+ decode(x, block, blocksize);
+
+ /* Round 1 */
+ FF(a, b, c, d, x[0], S11, 0xd76aa478); /* 1 */
+ FF(d, a, b, c, x[1], S12, 0xe8c7b756); /* 2 */
+ FF(c, d, a, b, x[2], S13, 0x242070db); /* 3 */
+ FF(b, c, d, a, x[3], S14, 0xc1bdceee); /* 4 */
+ FF(a, b, c, d, x[4], S11, 0xf57c0faf); /* 5 */
+ FF(d, a, b, c, x[5], S12, 0x4787c62a); /* 6 */
+ FF(c, d, a, b, x[6], S13, 0xa8304613); /* 7 */
+ FF(b, c, d, a, x[7], S14, 0xfd469501); /* 8 */
+ FF(a, b, c, d, x[8], S11, 0x698098d8); /* 9 */
+ FF(d, a, b, c, x[9], S12, 0x8b44f7af); /* 10 */
+ FF(c, d, a, b, x[10], S13, 0xffff5bb1); /* 11 */
+ FF(b, c, d, a, x[11], S14, 0x895cd7be); /* 12 */
+ FF(a, b, c, d, x[12], S11, 0x6b901122); /* 13 */
+ FF(d, a, b, c, x[13], S12, 0xfd987193); /* 14 */
+ FF(c, d, a, b, x[14], S13, 0xa679438e); /* 15 */
+ FF(b, c, d, a, x[15], S14, 0x49b40821); /* 16 */
+
+ /* Round 2 */
+ GG(a, b, c, d, x[1], S21, 0xf61e2562); /* 17 */
+ GG(d, a, b, c, x[6], S22, 0xc040b340); /* 18 */
+ GG(c, d, a, b, x[11], S23, 0x265e5a51); /* 19 */
+ GG(b, c, d, a, x[0], S24, 0xe9b6c7aa); /* 20 */
+ GG(a, b, c, d, x[5], S21, 0xd62f105d); /* 21 */
+ GG(d, a, b, c, x[10], S22, 0x2441453); /* 22 */
+ GG(c, d, a, b, x[15], S23, 0xd8a1e681); /* 23 */
+ GG(b, c, d, a, x[4], S24, 0xe7d3fbc8); /* 24 */
+ GG(a, b, c, d, x[9], S21, 0x21e1cde6); /* 25 */
+ GG(d, a, b, c, x[14], S22, 0xc33707d6); /* 26 */
+ GG(c, d, a, b, x[3], S23, 0xf4d50d87); /* 27 */
+ GG(b, c, d, a, x[8], S24, 0x455a14ed); /* 28 */
+ GG(a, b, c, d, x[13], S21, 0xa9e3e905); /* 29 */
+ GG(d, a, b, c, x[2], S22, 0xfcefa3f8); /* 30 */
+ GG(c, d, a, b, x[7], S23, 0x676f02d9); /* 31 */
+ GG(b, c, d, a, x[12], S24, 0x8d2a4c8a); /* 32 */
+
+ /* Round 3 */
+ HH(a, b, c, d, x[5], S31, 0xfffa3942); /* 33 */
+ HH(d, a, b, c, x[8], S32, 0x8771f681); /* 34 */
+ HH(c, d, a, b, x[11], S33, 0x6d9d6122); /* 35 */
+ HH(b, c, d, a, x[14], S34, 0xfde5380c); /* 36 */
+ HH(a, b, c, d, x[1], S31, 0xa4beea44); /* 37 */
+ HH(d, a, b, c, x[4], S32, 0x4bdecfa9); /* 38 */
+ HH(c, d, a, b, x[7], S33, 0xf6bb4b60); /* 39 */
+ HH(b, c, d, a, x[10], S34, 0xbebfbc70); /* 40 */
+ HH(a, b, c, d, x[13], S31, 0x289b7ec6); /* 41 */
+ HH(d, a, b, c, x[0], S32, 0xeaa127fa); /* 42 */
+ HH(c, d, a, b, x[3], S33, 0xd4ef3085); /* 43 */
+ HH(b, c, d, a, x[6], S34, 0x4881d05); /* 44 */
+ HH(a, b, c, d, x[9], S31, 0xd9d4d039); /* 45 */
+ HH(d, a, b, c, x[12], S32, 0xe6db99e5); /* 46 */
+ HH(c, d, a, b, x[15], S33, 0x1fa27cf8); /* 47 */
+ HH(b, c, d, a, x[2], S34, 0xc4ac5665); /* 48 */
+
+ /* Round 4 */
+ II(a, b, c, d, x[0], S41, 0xf4292244); /* 49 */
+ II(d, a, b, c, x[7], S42, 0x432aff97); /* 50 */
+ II(c, d, a, b, x[14], S43, 0xab9423a7); /* 51 */
+ II(b, c, d, a, x[5], S44, 0xfc93a039); /* 52 */
+ II(a, b, c, d, x[12], S41, 0x655b59c3); /* 53 */
+ II(d, a, b, c, x[3], S42, 0x8f0ccc92); /* 54 */
+ II(c, d, a, b, x[10], S43, 0xffeff47d); /* 55 */
+ II(b, c, d, a, x[1], S44, 0x85845dd1); /* 56 */
+ II(a, b, c, d, x[8], S41, 0x6fa87e4f); /* 57 */
+ II(d, a, b, c, x[15], S42, 0xfe2ce6e0); /* 58 */
+ II(c, d, a, b, x[6], S43, 0xa3014314); /* 59 */
+ II(b, c, d, a, x[13], S44, 0x4e0811a1); /* 60 */
+ II(a, b, c, d, x[4], S41, 0xf7537e82); /* 61 */
+ II(d, a, b, c, x[11], S42, 0xbd3af235); /* 62 */
+ II(c, d, a, b, x[2], S43, 0x2ad7d2bb); /* 63 */
+ II(b, c, d, a, x[9], S44, 0xeb86d391); /* 64 */
+
+ state[0] += a;
+ state[1] += b;
+ state[2] += c;
+ state[3] += d;
+
+ // Zeroize sensitive information.
+ memset(x, 0, sizeof x);
+}
+
+//////////////////////////////
+
+// MD5 block update operation. Continues an MD5 message-digest
+// operation, processing another message block
+void MD5::update(const unsigned char input[], size_type length)
+{
+ // compute number of bytes mod 64
+ size_type index = count[0] / 8 % blocksize;
+
+ // Update number of bits
+ if ((count[0] += (length << 3)) < (length << 3))
+ count[1]++;
+ count[1] += (length >> 29);
+
+ // number of bytes we need to fill in buffer
+ size_type firstpart = 64 - index;
+
+ size_type i;
+
+ // transform as many times as possible.
+ if (length >= firstpart)
+ {
+ // fill buffer first, transform
+ memcpy(&buffer[index], input, firstpart);
+ transform(buffer);
+
+ // transform chunks of blocksize (64 bytes)
+ for (i = firstpart; i + blocksize <= length; i += blocksize)
+ transform(&input[i]);
+
+ index = 0;
+ }
+ else
+ i = 0;
+
+ // buffer remaining input
+ memcpy(&buffer[index], &input[i], length - i);
+}
+
+//////////////////////////////
+
+
+MD5::digest_t MD5::digest()
+{
+ static const unsigned char padding[64] = {
+ 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
+
+ // Copies of hash state
+ uint8_t _buffer[blocksize];
+ uint32_t _count[2];
+ uint32_t _state[4];
+
+ // Backup hash state at previous block boundary
+ memcpy(_buffer, buffer, blocksize);
+ memcpy(_count, count, 8);
+ memcpy(_state, state, 16);
+
+ // Save number of bits
+ unsigned char bits[8];
+ encode(bits, count, 8);
+
+ // pad out to 56 mod 64.
+ size_type index = count[0] / 8 % 64;
+ size_type padLen = (index < 56) ? (56 - index) : (120 - index);
+ update(padding, padLen);
+
+ // Append length (before padding)
+ update(bits, 8);
+
+ // Store state in digest
+ digest_t result;
+ encode(result.data, state, 16);
+
+ // revert hash state to previous hash boundary
+ memcpy(buffer, _buffer, blocksize);
+ memcpy(count, _count, 8);
+ memcpy(state, _state, 16);
+
+ return result;
+}
\ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/C_runtime/md5.hpp Wed Jun 05 15:05:54 2024 +0200
@@ -0,0 +1,88 @@
+/* MD5
+
+ Modified in 2024 for use in Beremiz by Edouard Tisserant
+
+ converted to C++ class by Frank Thilo (thilo@unix-ag.org)
+ for bzflag (http://www.bzflag.org)
+
+ based on:
+
+ md5.h and md5.c
+ reference implementation of RFC 1321
+
+ Copyright (C) 1991-2, RSA Data Security, Inc. Created 1991. All
+rights reserved.
+
+License to copy and use this software is granted provided that it
+is identified as the "RSA Data Security, Inc. MD5 Message-Digest
+Algorithm" in all material mentioning or referencing this software
+or this function.
+
+License is also granted to make and use derivative works provided
+that such works are identified as "derived from the RSA Data
+Security, Inc. MD5 Message-Digest Algorithm" in all material
+mentioning or referencing the derived work.
+
+RSA Data Security, Inc. makes no representations concerning either
+the merchantability of this software or the suitability of this
+software for any particular purpose. It is provided "as is"
+without express or implied warranty of any kind.
+
+These notices must be retained in any copies of any part of this
+documentation and/or software.
+
+*/
+
+#ifndef BZF_MD5_H
+#define BZF_MD5_H
+
+#include <cstdint>
+
+// a small class for calculating MD5 hashes of strings or byte arrays
+// it is not meant to be fast or secure
+//
+// usage: 1) feed it blocks of uchars with update()
+// 2) get digest() data
+//
+// assumes that char is 8 bit and int is 32 bit
+class MD5
+{
+public:
+ typedef unsigned int size_type; // must be 32bit
+
+ MD5();
+ void update(const unsigned char *buf, size_type length);
+ typedef struct {
+ uint8_t data[16];
+ } digest_t;
+ digest_t digest();
+ enum
+ {
+ blocksize = 64,
+ digestsize = 16
+ };
+
+private:
+ void init();
+
+ void transform(const uint8_t block[blocksize]);
+ static void decode(uint32_t output[], const uint8_t input[], size_type len);
+ static void encode(uint8_t output[], const uint32_t input[], size_type len);
+
+ uint8_t buffer[blocksize]; // bytes that didn't fit in last 64 byte chunk
+ uint32_t count[2]; // 64bit counter for number of bits (lo, hi)
+ uint32_t state[4]; // digest so far
+
+ // low level logic operations
+ static inline uint32_t F(uint32_t x, uint32_t y, uint32_t z);
+ static inline uint32_t G(uint32_t x, uint32_t y, uint32_t z);
+ static inline uint32_t H(uint32_t x, uint32_t y, uint32_t z);
+ static inline uint32_t I(uint32_t x, uint32_t y, uint32_t z);
+ static inline uint32_t rotate_left(uint32_t x, int n);
+ static inline void FF(uint32_t &a, uint32_t b, uint32_t c, uint32_t d, uint32_t x, uint32_t s, uint32_t ac);
+ static inline void GG(uint32_t &a, uint32_t b, uint32_t c, uint32_t d, uint32_t x, uint32_t s, uint32_t ac);
+ static inline void HH(uint32_t &a, uint32_t b, uint32_t c, uint32_t d, uint32_t x, uint32_t s, uint32_t ac);
+ static inline void II(uint32_t &a, uint32_t b, uint32_t c, uint32_t d, uint32_t x, uint32_t s, uint32_t ac);
+};
+
+#endif
\ No newline at end of file
--- a/C_runtime/posix_main.cpp Mon May 27 11:16:27 2024 +0200
+++ b/C_runtime/posix_main.cpp Wed Jun 05 15:05:54 2024 +0200
@@ -1,14 +1,29 @@
+/*
+ * Beremiz C++ runtime
+ *
+ * This file implements Beremiz C++ runtime Command Line Interface for POSIX
+ *
+ * Based on erpcsniffer.cpp, BSD-3-Clause, Copyright 2017 NXP
+ *
+ * Copyright 2024 Beremiz SAS
+ *
+ * See COPYING for licensing details
+ */
#include <stdlib.h>
#include <vector>
#include <filesystem>
+// eRPC includes
#include "erpc_basic_codec.hpp"
#include "erpc_serial_transport.hpp"
#include "erpc_tcp_transport.hpp"
#include "erpc_simple_server.hpp"
+// eRPC generated includes
#include "erpc_PLCObject_server.hpp"
+
+// erpcgen includes (re-uses erpcgen's logging system and options parser)
#include "Logging.hpp"
#include "options.hpp"
@@ -17,13 +32,14 @@
using namespace erpc;
using namespace std;
+#define MSG_SIZE 1024*6
class MyMessageBufferFactory : public MessageBufferFactory
{
public:
virtual MessageBuffer create()
{
- uint8_t *buf = new uint8_t[1024];
- return MessageBuffer(buf, 1024);
+ uint8_t *buf = new uint8_t[MSG_SIZE];
+ return MessageBuffer(buf, MSG_SIZE);
}
virtual void dispose(MessageBuffer *buf)
@@ -294,7 +310,17 @@
m_workingDir = std::filesystem::current_path().c_str();
} else {
m_workingDir = m_positionalArgs[0].c_str();
- }
+ std::filesystem::current_path(m_workingDir);
+ }
+
+ // remove temporary directory if it already exists
+ if (std::filesystem::exists("tmp"))
+ {
+ std::filesystem::remove_all("tmp");
+ }
+
+ // Create temporary directory in working directory
+ std::filesystem::create_directory("tmp");
Transport *_transport;
switch (m_transport)
@@ -330,6 +356,9 @@
}
}
+ Crc16 crc;
+ _transport->setCrc16(&crc);
+
MyMessageBufferFactory _msgFactory;
BasicCodecFactory _basicCodecFactory;
SimpleServer _server;
--- a/README.md Mon May 27 11:16:27 2024 +0200
+++ b/README.md Wed Jun 05 15:05:54 2024 +0200
@@ -142,7 +142,7 @@
### Launch Beremiz IDE
```
-~/Beremiz/venv/python ~/Beremiz/beremiz/Beremiz.py
+~/Beremiz/venv/bin/python ~/Beremiz/beremiz/Beremiz.py
```
## Run standalone Beremiz runtime ##
@@ -151,7 +151,7 @@
```
mkdir ~/beremiz_runtime_workdir
-~/Beremiz/venv/python ~/Beremiz/beremiz/Beremiz_service.py -p 61194 -i localhost -x 0 -a 1 ~/beremiz_runtime_workdir
+~/Beremiz/venv/bin/python ~/Beremiz/beremiz/Beremiz_service.py -p 61194 -i localhost -x 0 -a 1 ~/beremiz_runtime_workdir
```
To connect IDE with runtime, enter target location URI in project's settings (project->Config->BeremizRoot/URI_location) pointed to your running Beremiz service in this case :
--- a/connectors/ERPC/__init__.py Mon May 27 11:16:27 2024 +0200
+++ b/connectors/ERPC/__init__.py Wed Jun 05 15:05:54 2024 +0200
@@ -126,7 +126,7 @@
except erpc.client.RequestError as e:
confnodesroot.logger.write_error(_("ERPC request error: %s\n") % e)
except MissingCallException as e:
- confnodesroot.logger.write_warning(_("Remote call not supported: %s\n") % e.message)
+ confnodesroot.logger.write_warning(_("Remote call not supported: %s\n") % e)
except Exception as e:
errmess = _("Exception calling remote PLC object fucntion %s:\n") % method_name \
+ traceback.format_exc()
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/doc/about.rst Wed Jun 05 15:05:54 2024 +0200
@@ -0,0 +1,29 @@
+About this document
+============================
+
+..
+ TODO: take content from https://hg.beremiz.org/beremiz_docs/
+
+Who Should Read This Document
+----------------------------------------------
+If you are new to the Beremiz IDE and want to get started with it or upgrading from
+previous versions then You should read this document.
+In order to properly understand this document, it is necessary to understand at least basics of the
+PLC and HMIs.
+
+Terminology
+--------------------------------------------------
+Throughout this manual, various phrases are used. Here is a description of some of them.
+
+PLC
+^^^^
+This means programmable logical controllers
+
+HMI
+^^^^
+This means Human Machine Interface
+
+Beremiz IDE
+^^^^^^^^^^^^
+This is the programming software for PLC which is Beremiz IDE
+
--- a/doc/index.rst Mon May 27 11:16:27 2024 +0200
+++ b/doc/index.rst Wed Jun 05 15:05:54 2024 +0200
@@ -8,6 +8,7 @@
.. toctree::
:maxdepth: 3
+ about
overview
install
programming/index
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/erpc_interface/README.md Wed Jun 05 15:05:54 2024 +0200
@@ -0,0 +1,22 @@
+# eRPC Interface
+
+This directory contains the eRPC interface definition and Python generated code for RPC channel used in between IDE and PLC.
+
+## Directory Structure
+
+- `erpc_PLCObject.erpc`: eRPC interface definition, subset of IDL language
+- `erpc_PLCObject/`: This directory contains eRPC interface generated python code
+- `__init__.py`: Useless and empty file also generated by `erpcgen`
+
+
+## eRPC Interface Definition
+
+The eRPC interface definition files define the communication protocol and message formats used by the project. These files are used to generate the necessary code for both the client and server sides.
+
+## Generated Code
+
+The Python code generated from the eRPC interface definition provides the necessary functions and classes to interact with the eRPC server. This code is used to implement both sides of the communication.
+
+```sh
+erpcgen -g py erpc_PLCObject.erpc
+```
\ No newline at end of file
--- a/erpc_interface/__init__.py Mon May 27 11:16:27 2024 +0200
+++ b/erpc_interface/__init__.py Wed Jun 05 15:05:54 2024 +0200
@@ -1,5 +1,5 @@
#
-# Generated by erpcgen 1.11.0 on Mon Jan 22 16:49:00 2024.
+# Generated by erpcgen 1.12.0 on Mon May 20 17:53:37 2024.
#
# AUTOGENERATED - DO NOT EDIT
#
--- a/erpc_interface/erpc_PLCObject.erpc Mon May 27 11:16:27 2024 +0200
+++ b/erpc_interface/erpc_PLCObject.erpc Wed Jun 05 15:05:54 2024 +0200
@@ -65,7 +65,7 @@
RepairPLC() -> uint32
ResetLogCount() -> uint32
SeedBlob(in binary seed, out binary blobID) -> uint32
- SetTraceVariablesList(in list<trace_order> orders, out uint32 debugtoken) -> uint32
+ SetTraceVariablesList(in list<trace_order> orders, out int32 debugtoken) -> uint32
StartPLC() -> uint32
StopPLC(out bool success) -> uint32
}
--- a/erpc_interface/erpc_PLCObject/__init__.py Mon May 27 11:16:27 2024 +0200
+++ b/erpc_interface/erpc_PLCObject/__init__.py Wed Jun 05 15:05:54 2024 +0200
@@ -1,5 +1,5 @@
#
-# Generated by erpcgen 1.11.0 on Mon Jan 22 16:49:00 2024.
+# Generated by erpcgen 1.12.0 on Mon May 20 17:53:37 2024.
#
# AUTOGENERATED - DO NOT EDIT
#
@@ -9,8 +9,8 @@
version = erpc_version.ERPC_VERSION
except ImportError:
version = "unknown"
-if version != "1.11.0":
- raise ValueError("The generated shim code version (1.11.0) is different to the rest of eRPC code (%s). \
+if version != "1.12.0":
+ raise ValueError("The generated shim code version (1.12.0) is different to the rest of eRPC code (%s). \
Install newer version by running \"python setup.py install\" in folder erpc/erpc_python/." % repr(version))
from . import common
--- a/erpc_interface/erpc_PLCObject/client.py Mon May 27 11:16:27 2024 +0200
+++ b/erpc_interface/erpc_PLCObject/client.py Wed Jun 05 15:05:54 2024 +0200
@@ -1,5 +1,5 @@
#
-# Generated by erpcgen 1.11.0 on Mon Jan 22 16:49:00 2024.
+# Generated by erpcgen 1.12.0 on Mon May 20 17:53:37 2024.
#
# AUTOGENERATED - DO NOT EDIT
#
@@ -255,7 +255,7 @@
# Send request and process reply.
self._clientManager.perform_request(request)
- debugtoken.value = codec.read_uint32()
+ debugtoken.value = codec.read_int32()
_result = codec.read_uint32()
return _result
--- a/erpc_interface/erpc_PLCObject/common.py Mon May 27 11:16:27 2024 +0200
+++ b/erpc_interface/erpc_PLCObject/common.py Wed Jun 05 15:05:54 2024 +0200
@@ -1,5 +1,5 @@
#
-# Generated by erpcgen 1.11.0 on Mon Jan 22 16:49:00 2024.
+# Generated by erpcgen 1.12.0 on Mon May 20 17:53:37 2024.
#
# AUTOGENERATED - DO NOT EDIT
#
--- a/erpc_interface/erpc_PLCObject/interface.py Mon May 27 11:16:27 2024 +0200
+++ b/erpc_interface/erpc_PLCObject/interface.py Wed Jun 05 15:05:54 2024 +0200
@@ -1,5 +1,5 @@
#
-# Generated by erpcgen 1.11.0 on Mon Jan 22 16:49:00 2024.
+# Generated by erpcgen 1.12.0 on Mon May 20 17:53:37 2024.
#
# AUTOGENERATED - DO NOT EDIT
#
--- a/erpc_interface/erpc_PLCObject/server.py Mon May 27 11:16:27 2024 +0200
+++ b/erpc_interface/erpc_PLCObject/server.py Wed Jun 05 15:05:54 2024 +0200
@@ -1,5 +1,5 @@
#
-# Generated by erpcgen 1.11.0 on Mon Jan 22 16:49:00 2024.
+# Generated by erpcgen 1.12.0 on Mon May 20 17:53:37 2024.
#
# AUTOGENERATED - DO NOT EDIT
#
@@ -305,7 +305,7 @@
sequence=sequence))
if debugtoken.value is None:
raise ValueError("debugtoken.value is None")
- codec.write_uint32(debugtoken.value)
+ codec.write_int32(debugtoken.value)
codec.write_uint32(_result)
def _handle_StartPLC(self, sequence, codec):
--- a/requirements.txt Mon May 27 11:16:27 2024 +0200
+++ b/requirements.txt Wed Jun 05 15:05:54 2024 +0200
@@ -13,7 +13,7 @@
contourpy==1.0.7
cryptography==40.0.2
cycler==0.11.0
-erpc==1.11.0
+erpc==1.12.0
fonttools==4.39.3
gattrdict==2.0.1
hyperlink==21.0.0
--- a/runtime/PLCObject.py Mon May 27 11:16:27 2024 +0200
+++ b/runtime/PLCObject.py Wed Jun 05 15:05:54 2024 +0200
@@ -98,6 +98,10 @@
self.Traces = []
self.DebugToken = 0
+ # Event to signal when PLC is stopped.
+ self.PlcStopped = Event()
+ self.PlcStopped.set()
+
self._init_blobs()
# First task of worker -> no @RunInMain
@@ -326,7 +330,7 @@
return False
- def PythonRuntimeCall(self, methodname, use_evaluator=True, reverse_order=False):
+ def PythonRuntimeCall(self, methodname, reverse_order=False):
"""
Calls init, start, stop or cleanup method provided by
runtime python files, loaded when new PLC uploaded
@@ -335,10 +339,7 @@
if reverse_order:
methods = reversed(methods)
for method in methods:
- if use_evaluator:
- _res, exp = self.evaluator(method)
- else:
- _res, exp = default_evaluator(method)
+ _res, exp = default_evaluator(method)
if exp is not None:
self.LogMessage(0, '\n'.join(traceback.format_exception(*exp)))
@@ -402,7 +403,7 @@
self.LogMessage(0, traceback.format_exc())
raise
- self.PythonRuntimeCall("init", use_evaluator=False)
+ self.PythonRuntimeCall("init")
self.PythonThreadCondLock = Lock()
self.PythonThreadCmdCond = Condition(self.PythonThreadCondLock)
@@ -417,7 +418,7 @@
if self.python_runtime_vars is not None:
self.PythonThreadCommand("Finish")
self.PythonThread.join()
- self.PythonRuntimeCall("cleanup", use_evaluator=False, reverse_order=True)
+ self.PythonRuntimeCall("cleanup", reverse_order=True)
self.python_runtime_vars = None
@@ -470,6 +471,10 @@
self._PostStartPLC()
self.PythonThreadLoop()
self.PythonRuntimeCall("stop", reverse_order=True)
+
+ # Signal that python runtime has stopped
+ self.PlcStopped.set()
+
elif cmd == "Finish":
self.PythonThreadAcknowledge(cmd)
break
@@ -525,6 +530,11 @@
@RunInMain
def StartPLC(self):
+ # Prevent accidental call to StartPLC when already Started
+ if self.PLCStatus != PlcStatus.Stopped:
+ self.LogMessage(0,_("Problem starting PLC : PLC is not Stopped"))
+ return
+
if self.PLClibraryHandle is None:
if not self.LoadPLC():
self._fail(_("Problem starting PLC : can't load PLC"))
@@ -538,6 +548,7 @@
self.PLCStatus = PlcStatus.Started
self.StatusChange()
self.PythonThreadCommand("Start")
+ self.PlcStopped.clear()
else:
self._fail(_("Problem starting PLC : error %d" % res))
@@ -546,13 +557,18 @@
if self.PLCStatus == PlcStatus.Started:
self.LogMessage("PLC stopped")
self._stopPLC()
- self.PLCStatus = PlcStatus.Stopped
- self.StatusChange()
if self.TraceThread is not None:
self.TraceThread.join()
self.TraceThread = None
- return True
- return False
+
+ # Wait for python runtime stop to complete
+ if self.PlcStopped.wait(timeout=5):
+ self.PLCStatus = PlcStatus.Stopped
+ self.StatusChange()
+ else:
+ self._fail(_("PLC timed out while stopping"))
+
+ return self.PLCStatus == PlcStatus.Stopped
def GetPLCstatus(self):
try:
@@ -734,7 +750,7 @@
return self.DebugToken
else:
self._suspendDebug(True)
- return 5 # DEBUG_SUSPENDED
+ return -5 # DEBUG_SUSPENDED
def _TracesSwap(self):
self.LastSwapTrace = time()
--- a/targets/Linux/plc_Linux_main.c Mon May 27 11:16:27 2024 +0200
+++ b/targets/Linux/plc_Linux_main.c Wed Jun 05 15:05:54 2024 +0200
@@ -25,7 +25,7 @@
#define _LogError(text,...) _Log(LOG_CRITICAL, text, ##__VA_ARGS__)
#define _LogWarning(text,...) _Log(LOG_WARNING, text, ##__VA_ARGS__)
-static unsigned long __debug_tick;
+static unsigned int __debug_tick;
static pthread_t PLC_thread;
static pthread_mutex_t python_wait_mutex = PTHREAD_MUTEX_INITIALIZER;
@@ -305,9 +305,9 @@
return 0;
}
-extern unsigned long __tick;
-
-int WaitDebugData(unsigned long *tick)
+extern unsigned int __tick;
+
+int WaitDebugData(unsigned int *tick)
{
int res;
if (PLC_shutdown) return 1;
--- a/targets/OSX/plc_OSX_main.c Mon May 27 11:16:27 2024 +0200
+++ b/targets/OSX/plc_OSX_main.c Wed Jun 05 15:05:54 2024 +0200
@@ -69,7 +69,7 @@
exit(0);
}
-static unsigned long __debug_tick;
+static unsigned int __debug_tick;
pthread_t PLC_thread;
static pthread_mutex_t python_wait_mutex = PTHREAD_MUTEX_INITIALIZER;
@@ -165,9 +165,9 @@
return 0;
}
-extern unsigned long __tick;
-
-int WaitDebugData(unsigned long *tick)
+extern unsigned int __tick;
+
+int WaitDebugData(unsigned int *tick)
{
int res;
if (PLC_shutdown)
--- a/targets/Win32/plc_Win32_main.c Mon May 27 11:16:27 2024 +0200
+++ b/targets/Win32/plc_Win32_main.c Wed Jun 05 15:05:54 2024 +0200
@@ -146,7 +146,7 @@
}
return 0;
}
-static unsigned long __debug_tick;
+static unsigned int __debug_tick;
int TryEnterDebugSection(void)
{
@@ -187,7 +187,7 @@
}
/* from plc_debugger.c */
-int WaitDebugData(unsigned long *tick)
+int WaitDebugData(unsigned int *tick)
{
DWORD res;
res = WaitForSingleObject(debug_wait_sem, INFINITE);
--- a/targets/Xenomai/plc_Xenomai_main.c Mon May 27 11:16:27 2024 +0200
+++ b/targets/Xenomai/plc_Xenomai_main.c Wed Jun 05 15:05:54 2024 +0200
@@ -348,9 +348,9 @@
}
}
-extern unsigned long __tick;
-
-int WaitDebugData(unsigned long *tick)
+extern unsigned int __tick;
+
+int WaitDebugData(unsigned int *tick)
{
char cmd;
int res;
--- a/targets/plc_debug.c Mon May 27 11:16:27 2024 +0200
+++ b/targets/plc_debug.c Wed Jun 05 15:05:54 2024 +0200
@@ -170,7 +170,7 @@
extern void InitiateDebugTransfer(void);
extern void CleanupRetain(void);
-extern unsigned long __tick;
+extern unsigned int __tick;
void __cleanup_debug(void)
{
@@ -490,9 +490,9 @@
BUFFER_FULL,
BUFFER_EMPTY);
}
-int WaitDebugData(unsigned long *tick);
+int WaitDebugData(unsigned int *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;
--- a/targets/plc_main_head.c Mon May 27 11:16:27 2024 +0200
+++ b/targets/plc_main_head.c Wed Jun 05 15:05:54 2024 +0200
@@ -7,7 +7,7 @@
/*
* Prototypes of functions provided by generated C softPLC
**/
-void config_run__(unsigned long tick);
+void config_run__(unsigned int tick);
void config_init__(void);
/*
@@ -24,13 +24,13 @@
**/
IEC_TIME __CURRENT_TIME;
IEC_BOOL __DEBUG = 0;
-unsigned long __tick = 0;
+unsigned int __tick = 0;
char *PLC_ID = NULL;
/*
* Variable generated by C softPLC and plugins
**/
-extern unsigned long greatest_tick_count__;
+extern unsigned int greatest_tick_count__;
/* Help to quit cleanly when init fail at a certain level */
static int init_level = 0;
--- a/targets/plc_main_tail.c Mon May 27 11:16:27 2024 +0200
+++ b/targets/plc_main_tail.c Wed Jun 05 15:05:54 2024 +0200
@@ -45,7 +45,7 @@
typedef struct {
uint32_t msgidx;
uint32_t msgsize;
- unsigned long tick;
+ unsigned int tick;
IEC_TIME time;
} mTail;
@@ -146,7 +146,7 @@
static long long Tsync = 0;
static long long FreqCorr = 0;
static int Nticks = 0;
-static unsigned long last_tick = 0;
+static unsigned int last_tick = 0;
/*
* Called on each external periodic sync event
--- a/tests/ide_tests/wx_widgets.pytest/test_CustomIntCtrl.py Mon May 27 11:16:27 2024 +0200
+++ b/tests/ide_tests/wx_widgets.pytest/test_CustomIntCtrl.py Wed Jun 05 15:05:54 2024 +0200
@@ -33,15 +33,22 @@
import controls.CustomIntCtrl
+wxApp = None
+
class TestCustomIntCtrl(unittest.TestCase):
def setUp(self):
- self.app = wx.App()
+ global wxApp
+ if wxApp is None:
+ wxApp = wx.App()
self.frame = wx.Frame(None)
def tearDown(self):
+ global wxApp
self.frame.Destroy()
- wx.CallAfter(wx.Exit)
- self.app.MainLoop()
+ if wxApp is not None:
+ wx.CallAfter(wx.Exit)
+ wxApp.MainLoop()
+ wxApp = None
def testMaxLimit(self):
"""Test working upper bound"""