Generally speaking, when using exOS, and you have a problem with the data communication, check the ExData logger, the ExDeploy messages you refer to comes from the deployment mechanism that just tells you that “something went wrong”, but not how. The ExData will have more information on the process itself. Especially when you have problems getting everything to work, and before you start exchanging cyclic data, you can adjust your logger level to DEBUG, VERBOSE, and such to get more information. Please see exOS FAQ & Getting Started - Share Info & Ideas - B&R Community (br-automation.com) for more info on troubleshooting.
To your question: You can run exOS with pthreads on the Linux environment, only take care to handle the same datamodel instance in the same thread. Theres no implicit thread safety in creating datasets in one thread and handling them in another for example. It is generally possible to access the same data from Automation runtime from different threads, I have no ready-made package compiled for that, but here’s a sample that should give you some ideas on how it works:
#include <unistd.h>
#include <pthread.h>
#include <string.h>
#include <stdlib.h>
#include "termination.h"
#define EXOS_ASSERT_LOG logger
#include "exos_log.h"
#include "exos_threads.h"
#define APP_ASSERT(_expected_, _expression_) \
if (_expected_ != _expression_) \
{ \
exos_log_error(logger, "%s Error in file %s:%d", #_expression_, __FILE__, __LINE__); \
}
#define SUCCESS(_format_, ...) exos_log_success(logger, EXOS_LOG_TYPE_USER, _format_, ##__VA_ARGS__);
#define INFO(_format_, ...) exos_log_info(logger, EXOS_LOG_TYPE_USER, _format_, ##__VA_ARGS__);
#define VERBOSE(_format_, ...) exos_log_debug(logger, EXOS_LOG_TYPE_USER + EXOS_LOG_TYPE_VERBOSE, _format_, ##__VA_ARGS__);
#define ERROR(_format_, ...) exos_log_error(logger, _format_, ##__VA_ARGS__);
typedef struct
{
char *thread_name;
char *request_name;
char *response_name;
uint8_t request_data;
uint8_t response_data;
exos_log_handle_t logger;
exos_artefact_handle_t artefact;
exos_value_handle_t value;
exos_value_handle_t response;
int sleep_time;
} thread_data_t;
static void valueChanged(exos_value_handle_t *value)
{
thread_data_t *data = (thread_data_t *)value->user_context;
exos_log_handle_t *logger = &data->logger; //needed for the LOG macros
VERBOSE("value %s changed!", value->name);
//the only thing we subscribe to is the request_data in each thread
//connection between the exos_value_handle_t *value and the actual response_data / request_data is done in the exos_value_init for each thread
data->response_data = data->request_data;
//we wait a configured amount of time
usleep(data->sleep_time);
//and the only thing we publish is the response
exos_value_publish(&data->response);
}
static void valuePublished(exos_value_handle_t *value, uint32_t queue_items)
{
thread_data_t *data = (thread_data_t *)value->user_context;
exos_log_handle_t *logger = &data->logger; //needed for the LOG macros
VERBOSE("value %s published! queue size:%i", value->name, queue_items);
}
static void valueConnectionChanged(exos_value_handle_t *value)
{
thread_data_t *data = (thread_data_t *)value->user_context;
exos_log_handle_t *logger = &data->logger; //needed for the LOG macros
INFO("value %s changed state to %s", value->name, exos_state_string(value->connection_state));
switch (value->connection_state)
{
case EXOS_STATE_DISCONNECTED:
break;
case EXOS_STATE_CONNECTED:
break;
case EXOS_STATE_OPERATIONAL:
break;
case EXOS_STATE_ABORTED:
ERROR("value %s error %d (%s) occured", value->name, value->error, exos_error_string(value->error));
break;
}
}
static void connectionChanged(exos_artefact_handle_t *artefact)
{
thread_data_t *data = (thread_data_t *)artefact->user_context;
exos_log_handle_t *logger = &data->logger; //needed for the LOG macros
INFO("application changed state to %s", exos_state_string(artefact->connection_state));
switch (artefact->connection_state)
{
case EXOS_STATE_DISCONNECTED:
break;
case EXOS_STATE_CONNECTED:
break;
case EXOS_STATE_OPERATIONAL:
SUCCESS("Threads operational!");
break;
case EXOS_STATE_ABORTED:
ERROR("application error %d (%s) occured", artefact->error, exos_error_string(artefact->error));
break;
}
}
void *thread_function(void *args)
{
thread_data_t *data = (thread_data_t *)args;
exos_log_handle_t *logger = &data->logger; //needed for the LOG macros
exos_log_init(logger, data->thread_name);
SUCCESS("Starting thread %s ..", data->thread_name);
EXOS_ASSERT_OK(exos_artefact_init(&data->artefact, "Threads"));
EXOS_ASSERT_OK(exos_value_init(&data->value, &data->artefact, data->request_name, &data->request_data, sizeof(data->request_data)));
EXOS_ASSERT_OK(exos_value_init(&data->response, &data->artefact, data->response_name, &data->response_data, sizeof(data->response_data)));
data->artefact.user_context = args;
data->value.user_context = args;
data->response.user_context = args;
EXOS_ASSERT_OK(exos_artefact_register_threads(&data->artefact, connectionChanged));
EXOS_ASSERT_OK(exos_value_register_subscription(&data->value, valueConnectionChanged, valueChanged));
EXOS_ASSERT_OK(exos_value_register_publisher(&data->response, valueConnectionChanged, valuePublished));
while (true)
{
exos_log_cyclic(logger);
EXOS_ASSERT_OK(exos_artefact_cyclic(&data->artefact));
if (is_terminated())
{
SUCCESS("Threads %s terminated, closing..", data->value.name);
break;
}
}
EXOS_ASSERT_OK(exos_value_unregister_publisher(&data->response));
EXOS_ASSERT_OK(exos_value_unregister_subscription(&data->value));
EXOS_ASSERT_OK(exos_artefact_unregister(&data->artefact));
//first delete the values, then the artefact
EXOS_ASSERT_OK(exos_value_delete(&data->value));
EXOS_ASSERT_OK(exos_value_delete(&data->response));
EXOS_ASSERT_OK(exos_artefact_delete(&data->artefact));
//finish with deleting the log
exos_log_delete(logger);
if (data->request_name)
free(data->request_name);
if (data->response_name)
free(data->response_name);
if (data->thread_name)
free(data->thread_name);
return NULL;
}
void set_thread_data(thread_data_t *data, int sleep_time, const char *thread_name, const char *request_name, const char *response_name)
{
data->sleep_time = sleep_time;
data->thread_name = strdup(thread_name);
data->request_name = strdup(request_name);
data->response_name = strdup(response_name);
}
int main()
{
pthread_t thread_t1;
pthread_t thread_t2;
pthread_t thread_t3;
thread_data_t t1_data = {};
thread_data_t t2_data = {};
thread_data_t t3_data = {};
exos_log_handle_t main_logger;
exos_log_handle_t *logger = &main_logger; //needed for the LOG macros
exos_log_init(logger, "Threads_Main");
SUCCESS("starting Threads application..");
catch_termination();
set_thread_data(&t1_data, 0, "Threads_T1", "request.t1", "response.t1");
APP_ASSERT(0, pthread_create(&thread_t1, NULL, thread_function, &t1_data));
set_thread_data(&t2_data, 200000, "Threads_T2", "request.t2", "response.t2");
APP_ASSERT(0, pthread_create(&thread_t2, NULL, thread_function, &t2_data));
set_thread_data(&t3_data, 500000, "Threads_T3", "request.t3", "response.t3");
APP_ASSERT(0, pthread_create(&thread_t3, NULL, thread_function, &t3_data));
while (true)
{
exos_log_cyclic(logger);
if (is_terminated())
{
SUCCESS("Threads application terminated, closing..");
break;
}
usleep(10000);
}
//wait for the threads to finish
APP_ASSERT(0, pthread_join(thread_t1, NULL));
APP_ASSERT(0, pthread_join(thread_t2, NULL));
APP_ASSERT(0, pthread_join(thread_t3, NULL));
//finish with deleting the log
exos_log_delete(logger);
return 0;
}
Happy coding