--- a/targets/Linux/plc_Linux_main.c Tue Feb 28 09:39:51 2023 +0100
+++ b/targets/Linux/plc_Linux_main.c Wed Mar 01 10:54:54 2023 +0100
@@ -15,6 +15,16 @@
+#define _Log(level,text,...) \ + snprintf(mstr, 255, text, ##__VA_ARGS__);\ + LogMessage(LOG_CRITICAL, mstr, strlen(mstr));\ +#define _LogError(text,...) _Log(LOG_CRITICAL, text, ##__VA_ARGS__) +#define _LogWarning(text,...) _Log(LOG_WARNING, text, ##__VA_ARGS__) static unsigned long __debug_tick;
static pthread_t PLC_thread;
@@ -43,7 +53,7 @@
static long long period_ns = 0;
-struct timespec next_abs_time;
+struct timespec next_cycle_time; static void inc_timespec(struct timespec *ts, unsigned long long value_ns)
@@ -64,8 +74,8 @@
printf("SetTimer(%lld,%lld)\n",next, period);
- clock_gettime(CLOCK_MONOTONIC, &next_abs_time);
- inc_timespec(&next_abs_time, next);
+ clock_gettime(CLOCK_MONOTONIC, &next_cycle_time); + inc_timespec(&next_cycle_time, next); // interrupt clock_nanpsleep
pthread_kill(PLC_thread, SIGUSR1);
@@ -88,32 +98,79 @@
+#define MAX_JITTER period_ns/10 +#define MIN_IDLE_TIME_NS 1000000 /* 1ms */ +/* Macro to compare timespec, evaluate to True if a is past b */ +#define timespec_gt(a,b) (a.tv_sec > b.tv_sec || (a.tv_sec == b.tv_sec && a.tv_nsec > b.tv_nsec)) void PLC_thread_proc(void *arg)
+ /* initialize next occurence and period */ + period_ns = common_ticktime__; + clock_gettime(CLOCK_MONOTONIC, &next_cycle_time); + struct timespec plc_end_time; + struct timespec deadline_time; + struct timespec plc_start_time; // Sleep until next PLC run
- // TODO check result of clock_nanosleep and wait again or exit eventually
- int res = clock_nanosleep(CLOCK_MONOTONIC, TIMER_ABSTIME, &next_abs_time, NULL);
+ res = clock_nanosleep(CLOCK_MONOTONIC, TIMER_ABSTIME, &next_cycle_time, NULL); - printf("PLC thread died with error %d \n", res);
+ _LogError("PLC thread timer returned error %d \n", res); + // timer overrun detection + clock_gettime(CLOCK_MONOTONIC, &plc_start_time); + deadline_time=next_cycle_time; + inc_timespec(&deadline_time, MAX_JITTER); + if(timespec_gt(plc_start_time, deadline_time)){ + _LogWarning("PLC thread woken up too late. PLC cyclic task interval is too small.\n"); PLC_GetTime(&__CURRENT_TIME);
- inc_timespec(&next_abs_time, period_ns);
+ // ensure next PLC cycle occurence is in the future + clock_gettime(CLOCK_MONOTONIC, &plc_end_time); + while(timespec_gt(plc_end_time, next_cycle_time)){ + inc_timespec(&next_cycle_time, period_ns); + // plc execution time overrun detection + // Mitigate CPU hogging, in case of too small cyclic task interval: + // - since cycle deadline already missed, better keep system responsive + // - test if next cycle occurs after minimal idle + // - enforce minimum idle time if not + struct timespec earliest_possible_time = plc_end_time; + inc_timespec(&earliest_possible_time, MIN_IDLE_TIME_NS); + while(timespec_gt(earliest_possible_time, next_cycle_time)){ + inc_timespec(&next_cycle_time, period_ns); + // increment tick count anyhow, so that task scheduling keeps consistent + _LogWarning("PLC execution time is longer than requested PLC cyclic task interval. %d cycles skipped\n", periods); -#define _LogError(text,...) \
- snprintf(mstr, 255, text, ##__VA_ARGS__);\
- LogMessage(LOG_CRITICAL, mstr, strlen(mstr));\
#define maxval(a,b) ((a>b)?a:b)
int startPLC(int argc,char **argv)
@@ -181,10 +238,6 @@
/* install signal handler for manual break */
signal(SIGINT, catch_signal);
- /* initialize next occurence and period */
- period_ns = common_ticktime__;
- clock_gettime(CLOCK_MONOTONIC, &next_abs_time);
ret = pthread_create(&PLC_thread, pattr, (void*) &PLC_thread_proc, NULL);
_LogError("create pthread failed\n");