targets/Xenomai/plc_Xenomai_main.c
author Edouard Tisserant <edouard.tisserant@gmail.com>
Tue, 17 May 2022 13:22:20 +0200
changeset 3465 2f5b5c14a650
parent 3398 7ca3924be865
child 3947 9b5cb90297e4
child 3953 91c39139420f
permissions -rw-r--r--
Tests: No need to archive test results before uploading them as action artifacts.
/**
 * Xenomai Linux specific code
 **/

#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <time.h>
#include <signal.h>
#include <stdlib.h>
#include <errno.h>
#include <sys/mman.h>
#include <sys/fcntl.h>

#include <alchemy/task.h>
#include <alchemy/timer.h>
#include <alchemy/sem.h>
#include <alchemy/pipe.h>

unsigned int PLC_state = 0;
#define PLC_STATE_TASK_CREATED                 1
#define PLC_STATE_DEBUG_PIPE_CREATED           2
#define PLC_STATE_PYTHON_PIPE_CREATED          8
#define PLC_STATE_WAITDEBUG_PIPE_CREATED       16
#define PLC_STATE_WAITPYTHON_PIPE_CREATED      32

#define PIPE_SIZE                    1

// rt-pipes commands

#define PYTHON_PENDING_COMMAND 1
#define PYTHON_FINISH 2

#define DEBUG_FINISH 2

#define DEBUG_PENDING_DATA 1
#define DEBUG_UNLOCK 1

long AtomicCompareExchange(long* atomicvar,long compared, long exchange)
{
    return __sync_val_compare_and_swap(atomicvar, compared, exchange);
}
long long AtomicCompareExchange64(long long* atomicvar, long long compared, long long exchange)
{
    return __sync_val_compare_and_swap(atomicvar, compared, exchange);
}

void PLC_GetTime(IEC_TIME *CURRENT_TIME)
{
    RTIME current_time = rt_timer_read();
    CURRENT_TIME->tv_sec = current_time / 1000000000;
    CURRENT_TIME->tv_nsec = current_time % 1000000000;
}

RT_TASK PLC_task;
void *WaitDebug_handle;
void *WaitPython_handle;
void *Debug_handle;
void *Python_handle;
void *svghmi_handle;

struct RT_to_nRT_signal_s {
    int used;
    RT_PIPE pipe;
    int pipe_fd;
    char *name;
};
typedef struct RT_to_nRT_signal_s RT_to_nRT_signal_t;

#define max_RT_to_nRT_signals 16

static RT_to_nRT_signal_t RT_to_nRT_signal_pool[max_RT_to_nRT_signals];

int recv_RT_to_nRT_signal(void* handle, char* payload){
    RT_to_nRT_signal_t *sig = (RT_to_nRT_signal_t*)handle;
    if(!sig->used) return -EINVAL;
    return read(sig->pipe_fd, payload, 1);
}

int send_RT_to_nRT_signal(void* handle, char payload){
    RT_to_nRT_signal_t *sig = (RT_to_nRT_signal_t*)handle;
    if(!sig->used) return -EINVAL;
    return rt_pipe_write(&sig->pipe, &payload, 1, P_NORMAL);
}


int PLC_shutdown = 0;

void PLC_SetTimer(unsigned long long next, unsigned long long period)
{
  RTIME current_time = rt_timer_read();
  rt_task_set_periodic(&PLC_task, current_time + next, rt_timer_ns2ticks(period));
}

void PLC_task_proc(void *arg)
{
    PLC_SetTimer(common_ticktime__, common_ticktime__);

    while (!PLC_shutdown) {
        PLC_GetTime(&__CURRENT_TIME);
        __run();
        if (PLC_shutdown) break;
        rt_task_wait_period(NULL);
    }
    /* since xenomai 3 it is not enough to close()
       file descriptor to unblock read()... */
    {
        /* explicitely finish python thread */
        char msg = PYTHON_FINISH;
        send_RT_to_nRT_signal(WaitPython_handle, msg);
    }
    {
        /* explicitely finish debug thread */
        char msg = DEBUG_FINISH;
        send_RT_to_nRT_signal(WaitDebug_handle, msg);
    }
}

static unsigned long __debug_tick;

#define _Log(text, err) \
    {\
        char mstr[256];\
        snprintf(mstr, 255, text " for %s (%d)", name, err);\
        LogMessage(LOG_CRITICAL, mstr, strlen(mstr));\
    }

void *create_RT_to_nRT_signal(char* name){
    int new_index = -1;
    int ret;
    RT_to_nRT_signal_t *sig;
    char pipe_dev[64];

    /* find a free slot */
    for(int i=0; i < max_RT_to_nRT_signals; i++){
        sig = &RT_to_nRT_signal_pool[i];
        if(!sig->used){
            new_index = i;
            break;
        }
    }

    /* fail if none found */
    if(new_index == -1) {
    	_Log("Maximum count of RT-PIPE reached while creating pipe", max_RT_to_nRT_signals);
        return NULL;
    }

    /* create rt pipe */
    if(ret = rt_pipe_create(&sig->pipe, name, new_index, PIPE_SIZE) < 0){
    	_Log("Failed opening real-time end of RT-PIPE", ret);
        return NULL;
    }

    /* open pipe's userland */
    snprintf(pipe_dev, 63, "/dev/rtp%d", new_index);
    if((sig->pipe_fd = open(pipe_dev, O_RDWR)) == -1){
        rt_pipe_delete(&sig->pipe);
    	_Log("Failed opening non-real-time end of RT-PIPE", errno);
        return NULL;
    }

    sig->used = 1;
    sig->name = name;

    return sig;
}

void delete_RT_to_nRT_signal(void* handle){
    int ret;
    RT_to_nRT_signal_t *sig = (RT_to_nRT_signal_t*)handle;
    char *name = sig->name;

    if(!sig->used) return;

    if(ret = rt_pipe_delete(&sig->pipe) != 0){
    	_Log("Failed closing real-time end of RT-PIPE", ret);
    }

    if(close(sig->pipe_fd) != 0){
    	_Log("Failed closing non-real-time end of RT-PIPE", errno);
    }

    sig->used = 0;
}

int wait_RT_to_nRT_signal(void* handle){
    char cmd;
    int ret = recv_RT_to_nRT_signal(handle, &cmd);
    return (ret == 1) ? 0 : ((ret == 0) ? ENODATA : -ret);
}

int unblock_RT_to_nRT_signal(void* handle){
    int ret = send_RT_to_nRT_signal(handle, 0);
    return (ret == 1) ? 0 : ((ret == 0) ? EINVAL : -ret);
}

void nRT_reschedule(void){
    sched_yield();
}

void PLC_cleanup_all(void)
{
    if (PLC_state & PLC_STATE_TASK_CREATED) {
        rt_task_delete(&PLC_task);
        PLC_state &= ~PLC_STATE_TASK_CREATED;
    }

    if (PLC_state & PLC_STATE_WAITDEBUG_PIPE_CREATED) {
        delete_RT_to_nRT_signal(WaitDebug_handle);
        PLC_state &= ~PLC_STATE_WAITDEBUG_PIPE_CREATED;
    }

    if (PLC_state & PLC_STATE_WAITPYTHON_PIPE_CREATED) {
        delete_RT_to_nRT_signal(WaitPython_handle);
        PLC_state &= ~PLC_STATE_WAITPYTHON_PIPE_CREATED;
    }

    if (PLC_state & PLC_STATE_DEBUG_PIPE_CREATED) {
        delete_RT_to_nRT_signal(Debug_handle);
        PLC_state &= ~PLC_STATE_DEBUG_PIPE_CREATED;
    }

    if (PLC_state & PLC_STATE_PYTHON_PIPE_CREATED) {
        delete_RT_to_nRT_signal(Python_handle);
        PLC_state &= ~PLC_STATE_PYTHON_PIPE_CREATED;
    }
}

int stopPLC()
{
    /* Stop the PLC */
    PLC_shutdown = 1;

    /* Wait until PLC task stops */
    rt_task_join(&PLC_task);

    PLC_cleanup_all();
    __cleanup();
    __debug_tick = -1;
    return 0;
}

//
void catch_signal(int sig)
{
    stopPLC();
//  signal(SIGTERM, catch_signal);
    signal(SIGINT, catch_signal);
    printf("Got Signal %d\n",sig);
    exit(0);
}

#define _startPLCLog(text) \
    {\
    	char mstr[] = text;\
        LogMessage(LOG_CRITICAL, mstr, sizeof(mstr));\
        goto error;\
    }

#define FO "Failed opening "

#define max_val(a,b) ((a>b)?a:b)
int startPLC(int argc,char **argv)
{
    signal(SIGINT, catch_signal);

    /* no memory swapping for that process */
    mlockall(MCL_CURRENT | MCL_FUTURE);

    /* memory initialization */
    PLC_shutdown = 0;
    bzero(RT_to_nRT_signal_pool, sizeof(RT_to_nRT_signal_pool));

    /*** RT Pipes ***/
    /* create Debug_pipe */
    if(!(Debug_handle = create_RT_to_nRT_signal("Debug_pipe"))) goto error;
    PLC_state |= PLC_STATE_DEBUG_PIPE_CREATED;

    /* create Python_pipe */
    if(!(Python_handle = create_RT_to_nRT_signal("Python_pipe"))) goto error;
    PLC_state |= PLC_STATE_PYTHON_PIPE_CREATED;

    /* create WaitDebug_pipe */
    if(!(WaitDebug_handle = create_RT_to_nRT_signal("WaitDebug_pipe"))) goto error;
    PLC_state |= PLC_STATE_WAITDEBUG_PIPE_CREATED;

    /* create WaitPython_pipe */
    if(!(WaitPython_handle = create_RT_to_nRT_signal("WaitPython_pipe"))) goto error;
    PLC_state |= PLC_STATE_WAITPYTHON_PIPE_CREATED;

    /*** create PLC task ***/
    if(rt_task_create(&PLC_task, "PLC_task", 0, 50, T_JOINABLE))
        _startPLCLog("Failed creating PLC task");
    PLC_state |= PLC_STATE_TASK_CREATED;

    if(__init(argc,argv)) goto error;

    /* start PLC task */
    if(rt_task_start(&PLC_task, &PLC_task_proc, NULL))
        _startPLCLog("Failed starting PLC task");

    return 0;

error:

    PLC_cleanup_all();
    return 1;
}

#define DEBUG_FREE 0
#define DEBUG_BUSY 1
static long debug_state = DEBUG_FREE;

int TryEnterDebugSection(void)
{
    if(AtomicCompareExchange(
        &debug_state,
        DEBUG_FREE,
        DEBUG_BUSY) == DEBUG_FREE){
        if(__DEBUG){
            return 1;
        }
        AtomicCompareExchange( &debug_state, DEBUG_BUSY, DEBUG_FREE);
    }
    return 0;
}

void LeaveDebugSection(void)
{
    if(AtomicCompareExchange( &debug_state,
        DEBUG_BUSY, DEBUG_FREE) == DEBUG_BUSY){
        char msg = DEBUG_UNLOCK;
        /* signal to NRT for wakeup */
        send_RT_to_nRT_signal(Debug_handle, msg);
    }
}

extern unsigned long __tick;

int WaitDebugData(unsigned long *tick)
{
    char cmd;
    int res;
    if (PLC_shutdown) return -1;
    /* Wait signal from PLC thread */
    res = recv_RT_to_nRT_signal(WaitDebug_handle, &cmd);
    if (res == 1 && cmd == DEBUG_PENDING_DATA){
        *tick = __debug_tick;
        return 0;
    }
    return -1;
}

/* Called by PLC thread when debug_publish finished
 * This is supposed to unlock debugger thread in WaitDebugData*/
void InitiateDebugTransfer()
{
    char msg = DEBUG_PENDING_DATA;
    /* remember tick */
    __debug_tick = __tick;
    /* signal debugger thread it can read data */
    send_RT_to_nRT_signal(WaitDebug_handle, msg);
}

int suspendDebug(int disable)
{
    char cmd = DEBUG_UNLOCK;
    if (PLC_shutdown) return -1;
    while(AtomicCompareExchange(
            &debug_state,
            DEBUG_FREE,
            DEBUG_BUSY) != DEBUG_FREE &&
            cmd == DEBUG_UNLOCK){
       if(recv_RT_to_nRT_signal(Debug_handle, &cmd) != 1){
           return -1;
       }
    }
    __DEBUG = !disable;
    if (disable)
        AtomicCompareExchange( &debug_state, DEBUG_BUSY, DEBUG_FREE);
    return 0;
}

void resumeDebug(void)
{
    __DEBUG = 1;
    AtomicCompareExchange( &debug_state, DEBUG_BUSY, DEBUG_FREE);
}

#define PYTHON_FREE 0
#define PYTHON_BUSY 1
static long python_state = PYTHON_FREE;

int WaitPythonCommands(void)
{
    char cmd;
    if (PLC_shutdown) return -1;
    /* Wait signal from PLC thread */
    if(recv_RT_to_nRT_signal(WaitPython_handle, &cmd) == 1 && cmd==PYTHON_PENDING_COMMAND){
        return 0;
    }
    return -1;
}

/* Called by PLC thread on each new python command*/
void UnBlockPythonCommands(void)
{
    char msg = PYTHON_PENDING_COMMAND;
    send_RT_to_nRT_signal(WaitPython_handle, msg);
}

int TryLockPython(void)
{
    return AtomicCompareExchange(
        &python_state,
        PYTHON_FREE,
        PYTHON_BUSY) == PYTHON_FREE;
}

#define UNLOCK_PYTHON 1
void LockPython(void)
{
    char cmd = UNLOCK_PYTHON;
    if (PLC_shutdown) return;
    while(AtomicCompareExchange(
            &python_state,
            PYTHON_FREE,
            PYTHON_BUSY) != PYTHON_FREE &&
            cmd == UNLOCK_PYTHON){
       recv_RT_to_nRT_signal(Python_handle, &cmd);
    }
}

void UnLockPython(void)
{
    if(AtomicCompareExchange(
            &python_state,
            PYTHON_BUSY,
            PYTHON_FREE) == PYTHON_BUSY){
        if(rt_task_self()){/*is that the real time task ?*/
           char cmd = UNLOCK_PYTHON;
           send_RT_to_nRT_signal(Python_handle, cmd);
        }/* otherwise, no signaling from non real time */
    }    /* as plc does not wait for lock. */
}

#ifndef HAVE_RETAIN
int CheckRetainBuffer(void)
{
	return 1;
}

void ValidateRetainBuffer(void)
{
}

void InValidateRetainBuffer(void)
{
}

void Retain(unsigned int offset, unsigned int count, void *p)
{
}

void Remind(unsigned int offset, unsigned int count, void *p)
{
}

void CleanupRetain(void)
{
}

void InitRetain(void)
{
}
#endif // !HAVE_RETAIN