From d117bb38e7e0a7291b113ad92db9184057837d8c Mon Sep 17 00:00:00 2001
From: Arnaud Blanchard <arnaud.blanchard@ensea.fr>
Date: Fri, 15 Oct 2021 01:53:38 +0200
Subject: [PATCH] c interface for blc_channel

---
 src/channel/c_blc_channel.cpp | 294 ++++++++++++++++++++++++++++++++++
 1 file changed, 294 insertions(+)
 create mode 100644 src/channel/c_blc_channel.cpp

diff --git a/src/channel/c_blc_channel.cpp b/src/channel/c_blc_channel.cpp
new file mode 100644
index 0000000..4368557
--- /dev/null
+++ b/src/channel/c_blc_channel.cpp
@@ -0,0 +1,294 @@
+/*
+ Basic Library for C/C++ (blclib)
+ Copyright  ETIS — ENSEA, Université de Cergy-Pontoise, CNRS (2011 - 2015)
+ 
+ Author:  Arnaud Blanchard
+ 
+ This software is governed by the CeCILL v2.1 license under French law and abiding by the rules of distribution of free software.
+ You can use, modify and/ or redistribute the software under the terms of the CeCILL v2.1 license as circulated by CEA, CNRS and INRIA at the following URL "http://www.cecill.info".
+ As a counterpart to the access to the source code and  rights to copy, modify and redistribute granted by the license,
+  users are provided only with a limited warranty and the software's author, the holder of the economic rights,  and the successive licensors have only limited liability.
+  In this respect, the user's attention is drawn to the risks associated with loading, using, modifying and/or developing or reproducing the software by the user in light of its specific status of free software,
+  that may mean  that it is complicated to manipulate, and that also therefore means that it is reserved for developers and experienced professionals having in-depth computer knowledge.
+ Users are therefore encouraged to load and test the software's suitability as regards their requirements in conditions enabling the security of their systems and/or data to be ensured
+  and, more generally, to use and operate it in the same conditions as regards security.
+  The fact that you are presently reading this means that you have had knowledge of the CeCILL v2.1 license and that you accept its terms. *
+ 
+ 
+ Created on: Oct 9, 2014
+ Author: Arnaud Blanchard
+ */
+
+#include "blc_channel.h"
+
+#include <errno.h>
+#include <string.h>
+#include <unistd.h>
+#include <limits.h>
+#include <pthread.h>
+#include <signal.h>
+#include <sys/types.h>
+#include <sys/mman.h> //mmap
+#include <sys/stat.h> // mode S_ ... constants
+#include <sys/time.h> //gettimeofday
+#include <semaphore.h>
+#include <stack>
+#include <vector>
+#include <tuple>
+
+#include "blc_realtime.h"
+#include "blc_text.h"
+
+#define TMP_BUFFER_SIZE 4096
+#define BLC_CHANNELS_LIST_PATH "/tmp/blc_channels.txt"
+
+using namespace std;
+
+static int blc_channel_id_max = 0;
+static sem_t *blc_channel_event[2]={NULL, NULL};
+static int blc_channel_event_id=0;
+static void (*blc_channel_event_callback)(void*)=NULL;
+static void *blc_channel_event_user_data=NULL;
+
+pthread_t pthread;
+
+START_EXTERN_C
+
+// This is especially for binding to external langage (e.g. python)
+blc_channel *blc_channel_new(char const *name, int mode,  uint32_t type, uint32_t format, int dims_nb, int length0, ...){
+    va_list arguments;
+    vector<size_t> lengths(dims_nb, length0);
+
+    va_start(arguments, length0);
+    for (int i=1; i<dims_nb; i++) lengths[i] = va_arg(arguments, size_t);
+    va_end(arguments);
+
+    return new blc_channel(name,  mode,  type,  format, lengths);
+}
+
+blc_channel *blc_channel_new_open(char const *name, int mode){
+    return new  blc_channel(name,  mode);
+}
+
+
+
+// This is especially for binding to external langage (e.g. python)
+// Delete the channel. This is different from destroy which deinits the channel but does not remove the memory.
+void blc_channel_delete(blc_channel *channel){    
+    delete channel;
+}
+
+void blc_remove_channel_with_name(char const *name){
+    
+    FILE *list_file;
+    blc_channel info;
+    fpos_t write_position, read_position;
+    char *buffer[TMP_BUFFER_SIZE];
+    size_t data_read;
+    off_t offset;
+    // char control_filename[NAME_MAX];
+    char tmp_name[NAME_MAX];
+    
+    SYSTEM_ERROR_CHECK(list_file=fopen(BLC_CHANNELS_LIST_PATH, "r+"), NULL, "impossible to open " BLC_CHANNELS_LIST_PATH);
+    
+    while(!feof(list_file))
+    {
+        fgetpos(list_file, &write_position);
+        info.fscan_info(list_file, 1);
+        if (strcmp(info.name, name) == 0)
+        {
+            fgetpos(list_file, &read_position); //we remove the line by copying the end of the file here.
+            do
+            {
+                fsetpos(list_file, &read_position);
+                data_read = fread(buffer, 1, TMP_BUFFER_SIZE, list_file);
+                fgetpos(list_file, &read_position);
+                if (data_read==0) if (!feof(list_file)) EXIT_ON_ERROR("Reading file" BLC_CHANNELS_LIST_PATH);
+                fsetpos(list_file, &write_position);
+                SYSTEM_SUCCESS_CHECK(fwrite(buffer, 1, data_read, list_file), data_read, "");
+                fgetpos(list_file, &write_position);
+            }while(data_read);
+            
+            SYSTEM_ERROR_CHECK(offset = ftell(list_file), -1, "");
+            SYSTEM_ERROR_CHECK(ftruncate(fileno(list_file), offset), -1, "");
+            SYSTEM_ERROR_CHECK(fclose(list_file), -1, "");
+            break;
+        }
+    }
+    if (info.id==blc_channel_id_max) blc_channel_id_max--;
+    
+    SYSTEM_ERROR_CHECK(shm_unlink(name), -1, "unlinking blc_channel '%s'", name);
+    sprintf(tmp_name, "blc_channel%d_sem_new_data0", info.id);
+    SYSTEM_ERROR_CHECK(sem_unlink(tmp_name), -1, "Unlinking sem_new_data '%s' for blc_channel '%s'", tmp_name, name);
+    sprintf(tmp_name, "blc_channel%d_sem_ack_data0", info.id);
+    SYSTEM_ERROR_CHECK(sem_unlink(tmp_name), -1, "Unlinking sem_ack_data '%s' for blc_channel '%s'", tmp_name, name);
+    
+    blc_channel_post_event();
+    
+}
+
+// Envoie un message d'erreur avec name_of_file, name_of_function, number_of_line et affiche le message formate avec les parametres variables. Puis exit le programme avec le parametre EXIT_FAILURE. To be used with EXIT_ON_ERROR.
+void blc_channel_fatal_error(blc_channel *channel, const char *name_of_file, const char* name_of_function, int numero_of_line, const char *message, ...){
+    va_list arguments;
+    va_start(arguments, message);
+    color_eprintf(BLC_BRIGHT_RED, "\n%s: %s \t %s \t %i :\nError: ", blc_program_name, name_of_file, name_of_function, numero_of_line);
+    color_veprintf(BLC_BRIGHT_RED, message, arguments);
+    va_end(arguments);
+    channel->fprint_debug(stderr);
+    fprintf(stderr, "\n\n");
+    fflush(stderr);
+    raise(SIGABRT);
+    exit(EXIT_FAILURE);
+}
+
+void blc_channel_init(blc_channel *channel, const char *name, int mode,  uint32_t type, uint32_t format, int dims_nb, int length0, ...)
+{
+    va_list arguments;
+    vector<size_t> lengths(dims_nb, length0);
+    
+    va_start(arguments, length0);
+    for(int i=1; i<dims_nb; i++) lengths.push_back(va_arg(arguments, size_t));
+    va_end(arguments);
+    new (channel)blc_channel(name, mode, type, format, lengths);
+}
+
+void blc_channel_open(blc_channel *channel, const char *name, int mode){
+    channel->open(name, mode);
+}
+
+void blc_channel_create_or_open(blc_channel *channel, const char *name, int mode,  uint32_t type, uint32_t format, int dims_nb, int length0, ...){
+    int created;
+    va_list arguments;
+    vector<size_t> lengths(dims_nb, length0);
+    va_start(arguments, length0);
+    for (int i=1; i<dims_nb; i++) lengths.push_back(va_arg(arguments, size_t));
+    va_end(arguments);
+    new (channel) blc_channel(name, mode, type,  format, lengths);
+}
+
+//Search for channel name whatever is start by /,: ,^ or .
+int blc_channel_get_info_with_name(blc_channel *info, char const *name){
+    blc_channel tmp_info;
+    FILE *file;
+    int id=-1;
+    
+    file = fopen(BLC_CHANNELS_LIST_PATH, "r");
+    if (file == NULL){
+        if (errno==ENOENT) return -1;
+        else EXIT_ON_SYSTEM_ERROR("opening '" BLC_CHANNELS_LIST_PATH"'.");
+    }
+    
+    while(fscanf(file, "%*[ \t\n]")!=EOF){
+        tmp_info.fscan_info(file, 1);
+        if ((tmp_info.id != -1) && (strcmp(tmp_info.name+1, name+1)==0)){ //We do not test first char '/' or '.'
+            id=tmp_info.id;
+            *info=tmp_info;
+            break;
+        }
+    }
+    fclose(file);
+    return id;
+}
+
+int blc_channel_get_all_infos(struct blc_channel **channels_infos){
+    return blc_channel_get_all_infos_with_filter(channels_infos, nullptr);
+}
+
+int blc_channel_get_all_infos_with_filter(blc_channel **channels_infos, char const *filter){
+    
+    vector<blc_channel> channels = blc_channel::get_all_infos(filter);
+     *channels_infos=MANY_ALLOCATIONS(channels.size(), blc_channel);
+     memcpy(channels_infos, channels.data(), sizeof(blc_channel)*channels.size()); //This copy is to incompatibility between c and c++
+    return channels.size();
+}
+
+void blc_channel_close_all(blc_channel *channels, int channels_nb)
+{
+    blc_channel *channel;
+    FOR_EACH_INV(channel, channels, channels_nb) channel->~blc_channel();
+}
+
+int blc_channel_remove(blc_channel * channel)
+{
+    if(channel == NULL) EXIT_ON_ERROR("The channel is NULL");
+    else channel->remove();
+    return 1;
+}
+
+static void init_blc_channel_sem_event(){
+   SYSTEM_ERROR_CHECK(blc_channel_event[0]=sem_open("blc_channel_event0", O_CREAT, S_IRWXU, 0), SEM_FAILED, NULL);
+   SYSTEM_ERROR_CHECK(blc_channel_event[1]=sem_open("blc_channel_event1", O_CREAT, S_IRWXU, 0), SEM_FAILED, NULL);
+}
+
+void blc_channel_post_event(){
+    if (blc_channel_event[0]==NULL || blc_channel_event[1]==NULL) init_blc_channel_sem_event();
+    
+    //If the event is already free it is not the good one. We free the other one to trigger the event
+    if (sem_trywait(blc_channel_event[blc_channel_event_id])==0){
+        SYSTEM_ERROR_CHECK(sem_post(blc_channel_event[blc_channel_event_id]), -1, NULL);
+        SYSTEM_ERROR_CHECK(sem_post(blc_channel_event[1-blc_channel_event_id]), -1, NULL);
+    }
+    else {     //If the event is not free, we free it to trigger the event
+        if (errno!=EAGAIN) EXIT_ON_SYSTEM_ERROR(NULL);
+        SYSTEM_ERROR_CHECK(sem_post(blc_channel_event[blc_channel_event_id]), -1, NULL);
+    }
+    blc_channel_event_id=1-blc_channel_event_id;
+}
+
+
+/**Block until event. It may produce false positive in connection */
+static void *blc_channel_thread_manager(void*){
+    int i=0;
+    
+    //If the event is already free it is not the good one. We will wait for the other one
+    if (sem_trywait(blc_channel_event[i])==0){
+        SYSTEM_ERROR_CHECK(sem_post(blc_channel_event[i]), -1, NULL);
+        i=1-i;
+    }
+    
+    while(1){
+        //We wait for event
+        SYSTEM_ERROR_CHECK(sem_wait(blc_channel_event[i]), -1, NULL);
+        //We free event for the others
+        SYSTEM_ERROR_CHECK(sem_post(blc_channel_event[i]), -1, NULL);
+        i=1-i;
+        while(sem_trywait(blc_channel_event[i])==0);
+        if (errno!=EAGAIN) EXIT_ON_SYSTEM_ERROR(NULL);
+        blc_channel_event_callback(blc_channel_event_user_data);
+    }
+}
+
+void blc_channel_check_for_event(void (*callback)(void*), void *user_data) {
+    if (blc_channel_event[0]==NULL || blc_channel_event[1]==NULL) init_blc_channel_sem_event();
+    
+    blc_channel_event_callback=callback;
+    blc_channel_event_user_data=user_data;
+    BLC_PTHREAD_CHECK(pthread_create(&pthread, NULL, blc_channel_thread_manager, NULL), NULL);
+}
+
+void blc_channel_destroy(blc_channel *channel){
+    delete channel;
+}
+           
+void blc_channel_wait_new_data(void *channel_pt){
+    blc_channel *channel=(blc_channel*)channel_pt;
+    SYSTEM_ERROR_CHECK(sem_wait(channel->sem_new_data), -1, "Waiting new data for channel '%s'", channel->name);
+}
+
+void blc_channel_wait_ack_data(void *channel_pt){
+    blc_channel *channel=(blc_channel*)channel_pt;
+    SYSTEM_ERROR_CHECK(sem_wait(channel->sem_ack_data), -1, "Waiting ack data for channel '%s'", channel->name);
+}
+
+void blc_channel_post_new_data(void *channel_pt){
+    blc_channel *channel=(blc_channel*)channel_pt;
+    SYSTEM_ERROR_CHECK(sem_post(channel->sem_new_data), -1, "Posting new data for channel '%s'", channel->name);
+}
+
+void blc_channel_post_ack_data(void *channel_pt){
+    blc_channel *channel=(blc_channel*)channel_pt;  
+    SYSTEM_ERROR_CHECK(sem_post(channel->sem_ack_data), -1, "Posting ack data for channel '%s'", channel->name);
+}
+
+END_EXTERN_C
+
-- 
GitLab