Edouard@2765: #include <pthread.h>
Edouard@2776: #include <errno.h>
Edouard@2764: #include "iec_types_all.h"
Edouard@2764: #include "POUS.h"
Edouard@2764: #include "config.h"
Edouard@2764: #include "beremiz.h"
Edouard@2750: 
Edouard@2768: #define DEFAULT_REFRESH_PERIOD_MS 100
Edouard@2765: #define HMI_BUFFER_SIZE %(buffer_size)d
Edouard@2776: #define HMI_ITEM_COUNT %(item_count)d
Edouard@2788: #define HMI_HASH_SIZE 8
Edouard@2788: static uint8_t hmi_hash[HMI_HASH_SIZE] = {%(hmi_hash_ints)s};
Edouard@2765: 
Edouard@2765: /* PLC reads from that buffer */
Edouard@2765: static char rbuf[HMI_BUFFER_SIZE];
Edouard@2765: 
Edouard@2765: /* PLC writes to that buffer */
Edouard@2765: static char wbuf[HMI_BUFFER_SIZE];
Edouard@2765: 
Edouard@2788: /* TODO change that in case of multiclient... */
Edouard@2788: /* worst biggest send buffer. FIXME : use dynamic alloc ? */
Edouard@2788: static char sbuf[HMI_HASH_SIZE +  HMI_BUFFER_SIZE + (HMI_ITEM_COUNT * sizeof(uint32_t))];
Edouard@2788: static unsigned int sbufidx;
Edouard@2788: 
Edouard@2768: %(extern_variables_declarations)s
Edouard@2765: 
Edouard@2771: #define ticktime_ns %(PLC_ticktime)d
Edouard@2788: static uint16_t ticktime_ms = (ticktime_ns>1000000)?
Edouard@2768:                      ticktime_ns/1000000:
Edouard@2768:                      1;
Edouard@2768: 
Edouard@2768: typedef enum {
Edouard@2768:     buf_free = 0,
Edouard@2805:     buf_new,
Edouard@2768:     buf_set,
Edouard@2768:     buf_tosend
Edouard@2768: } buf_state_t;
Edouard@2768: 
Edouard@2788: static int global_write_dirty = 0;
Edouard@2764: 
Edouard@2771: typedef struct {
Edouard@2764:     void *ptr;
Edouard@2764:     __IEC_types_enum type;
Edouard@2765:     uint32_t buf_index;
Edouard@2768: 
Edouard@2768:     /* publish/write/send */
Edouard@2771:     long wlock;
Edouard@2788:     buf_state_t wstate;
Edouard@2788: 
Edouard@2768:     /* zero means not subscribed */
Edouard@2768:     uint16_t refresh_period_ms;
Edouard@2768:     uint16_t age_ms;
Edouard@2768: 
Edouard@2768:     /* retrieve/read/recv */
Edouard@2771:     long rlock;
Edouard@2768:     buf_state_t rstate;
Edouard@2768: 
Edouard@2764: } hmi_tree_item_t;
Edouard@2764: 
Edouard@2764: static hmi_tree_item_t hmi_tree_item[] = {
Edouard@2764: %(variable_decl_array)s
Edouard@2764: };
Edouard@2764: 
Edouard@2789: typedef int(*hmi_tree_iterator)(uint32_t, hmi_tree_item_t*);
Edouard@2788: static int traverse_hmi_tree(hmi_tree_iterator fp)
Edouard@2765: {
Edouard@2765:     unsigned int i;
Edouard@2765:     for(i = 0; i < sizeof(hmi_tree_item)/sizeof(hmi_tree_item_t); i++){
Edouard@2765:         hmi_tree_item_t *dsc = &hmi_tree_item[i];
Edouard@2799:         int res = (*fp)(i, dsc);
Edouard@2799:         if(res != 0){
Edouard@2788:             return res;
Edouard@2799:         }
Edouard@2799:     }
Edouard@2799:     return 0;
Edouard@2765: }
Edouard@2765: 
Edouard@2767: #define __Unpack_desc_type hmi_tree_item_t
Edouard@2766: 
Edouard@2767: %(var_access_code)s
Edouard@2765: 
Edouard@2789: static int write_iterator(uint32_t index, hmi_tree_item_t *dsc)
Edouard@2788: {
Edouard@2788:     if(AtomicCompareExchange(&dsc->wlock, 0, 1) == 0)
Edouard@2788:     {
Edouard@2788:         if(dsc->wstate == buf_set){
Edouard@2788:             /* if being subscribed */
Edouard@2788:             if(dsc->refresh_period_ms){
Edouard@2788:                 if(dsc->age_ms + ticktime_ms < dsc->refresh_period_ms){
Edouard@2788:                     dsc->age_ms += ticktime_ms;
Edouard@2788:                 }else{
Edouard@2788:                     dsc->wstate = buf_tosend;
Edouard@2805:                     global_write_dirty = 1;
Edouard@2788:                 }
Edouard@2788:             }
Edouard@2788:         }
Edouard@2788: 
Edouard@2788:         void *dest_p = &wbuf[dsc->buf_index];
Edouard@2788:         void *real_value_p = NULL;
Edouard@2788:         char flags = 0;
Edouard@2788:         void *visible_value_p = UnpackVar(dsc, &real_value_p, &flags);
Edouard@2788: 
Edouard@2788:         /* if new value differs from previous one */
Edouard@2788:         USINT sz = __get_type_enum_size(dsc->type);
edouard@2826:         if(__Is_a_string(dsc)){
edouard@2826:             sz = ((STRING*)visible_value_p)->len + 1;
edouard@2826:         }
Edouard@2805:         if(dsc->wstate == buf_new || memcmp(dest_p, visible_value_p, sz) != 0){
Edouard@2788:             /* copy and flag as set */
Edouard@2788:             memcpy(dest_p, visible_value_p, sz);
Edouard@2805:             if(dsc->wstate == buf_new || dsc->wstate == buf_free) {
Edouard@2805:                 if(dsc->wstate == buf_new || ticktime_ms > dsc->refresh_period_ms){
Edouard@2805:                     dsc->wstate = buf_tosend;
Edouard@2805:                     global_write_dirty = 1;
Edouard@2805:                 } else {
Edouard@2805:                     dsc->wstate = buf_set;
Edouard@2805:                 }
Edouard@2788:                 dsc->age_ms = 0;
Edouard@2788:             }
Edouard@2788:         }
Edouard@2788: 
Edouard@2788:         AtomicCompareExchange(&dsc->wlock, 1, 0);
Edouard@2788:     }
Edouard@2788:     // else ... : PLC can't wait, variable will be updated next turn
Edouard@2788:     return 0;
Edouard@2788: }
Edouard@2788: 
Edouard@2789: static int send_iterator(uint32_t index, hmi_tree_item_t *dsc)
Edouard@2788: {
Edouard@2788:     while(AtomicCompareExchange(&dsc->wlock, 0, 1)) sched_yield();
Edouard@2788: 
Edouard@2788:     if(dsc->wstate == buf_tosend)
Edouard@2788:     {
Edouard@2788:         uint32_t sz = __get_type_enum_size(dsc->type);
Edouard@2799:         if(sbufidx + sizeof(uint32_t) + sz <=  sizeof(sbuf))
Edouard@2788:         {
Edouard@2788:             void *src_p = &wbuf[dsc->buf_index];
Edouard@2788:             void *dst_p = &sbuf[sbufidx];
edouard@2826:             if(__Is_a_string(dsc)){
edouard@2826:                 sz = ((STRING*)src_p)->len + 1;
edouard@2826:             }
Edouard@2799:             /* TODO : force into little endian */
Edouard@2788:             memcpy(dst_p, &index, sizeof(uint32_t));
Edouard@2788:             memcpy(dst_p + sizeof(uint32_t), src_p, sz);
Edouard@2788:             dsc->wstate = buf_free;
Edouard@2788:             sbufidx += sizeof(uint32_t) /* index */ + sz;
Edouard@2788:         }
Edouard@2788:         else
Edouard@2788:         {
Edouard@2799:             printf("BUG!!! %%d + %%ld + %%d >  %%ld \n", sbufidx, sizeof(uint32_t), sz,  sizeof(sbuf));
Edouard@2799:             AtomicCompareExchange(&dsc->wlock, 1, 0);
Edouard@2799:             return EOVERFLOW; 
Edouard@2788:         }
Edouard@2788:     }
Edouard@2788: 
Edouard@2768:     AtomicCompareExchange(&dsc->wlock, 1, 0);
Edouard@2799:     return 0; 
Edouard@2788: }
Edouard@2788: 
Edouard@2789: static int read_iterator(uint32_t index, hmi_tree_item_t *dsc)
Edouard@2788: {
Edouard@2788:     if(AtomicCompareExchange(&dsc->rlock, 0, 1) == 0)
Edouard@2788:     {
Edouard@2788:         if(dsc->rstate == buf_set)
Edouard@2788:         {
Edouard@2788:             void *src_p = &rbuf[dsc->buf_index];
Edouard@2788:             void *real_value_p = NULL;
Edouard@2788:             char flags = 0;
Edouard@2788:             void *visible_value_p = UnpackVar(dsc, &real_value_p, &flags);
Edouard@2788:             memcpy(real_value_p, src_p, __get_type_enum_size(dsc->type));
Edouard@2788:             dsc->rstate = buf_free;
Edouard@2788:         }
Edouard@2788:         AtomicCompareExchange(&dsc->rlock, 1, 0);
Edouard@2788:     }
Edouard@2788:     // else ... : PLC can't wait, variable will be updated next turn
Edouard@2788:     return 0;
Edouard@2788: }
Edouard@2788: 
Edouard@2812: void update_refresh_period(hmi_tree_item_t *dsc, uint16_t refresh_period_ms)
Edouard@2768: {
Edouard@2768:     while(AtomicCompareExchange(&dsc->wlock, 0, 1)) sched_yield();
Edouard@2788:     dsc->refresh_period_ms = refresh_period_ms;
Edouard@2805:     if(refresh_period_ms) {
Edouard@2809:         /* TODO : maybe only if was null before for optimization */
Edouard@2805:         dsc->wstate = buf_new;
Edouard@2805:     } else {
Edouard@2805:         dsc->wstate = buf_free;
Edouard@2805:     }
Edouard@2768:     AtomicCompareExchange(&dsc->wlock, 1, 0);
Edouard@2766: }
Edouard@2766: 
Edouard@2789: static int reset_iterator(uint32_t index, hmi_tree_item_t *dsc)
Edouard@2789: {
Edouard@2789:     update_refresh_period(dsc, 0);
Edouard@2788:     return 0;
Edouard@2765: }
Edouard@2765: 
Edouard@2820: void SVGHMI_SuspendFromPythonThread(void);
Edouard@2820: void SVGHMI_WakeupFromRTThread(void);
Edouard@2775: 
Edouard@2776: static int continue_collect;
Edouard@2776: 
Edouard@2764: int __init_svghmi()
Edouard@2764: {
Edouard@2765:     bzero(rbuf,sizeof(rbuf));
Edouard@2765:     bzero(wbuf,sizeof(wbuf));
Edouard@2819: 
Edouard@2775:     continue_collect = 1;
Edouard@2765: 
Edouard@2764:     return 0;
Edouard@2750: }
Edouard@2764: 
Edouard@2764: void __cleanup_svghmi()
Edouard@2764: {
Edouard@2775:     continue_collect = 0;
Edouard@2820:     SVGHMI_WakeupFromRTThread();
Edouard@2764: }
Edouard@2764: 
Edouard@2764: void __retrieve_svghmi()
Edouard@2764: {
Edouard@2768:     traverse_hmi_tree(read_iterator);
Edouard@2764: }
Edouard@2764: 
Edouard@2764: void __publish_svghmi()
Edouard@2764: {
Edouard@2768:     global_write_dirty = 0;
Edouard@2768:     traverse_hmi_tree(write_iterator);
Edouard@2768:     if(global_write_dirty) {
Edouard@2820:         SVGHMI_WakeupFromRTThread();
Edouard@2775:     }
Edouard@2775: }
Edouard@2775: 
Edouard@2775: /* PYTHON CALLS */
Edouard@2779: int svghmi_send_collect(uint32_t *size, char **ptr){
Edouard@2775: 
Edouard@2820:     SVGHMI_SuspendFromPythonThread();
Edouard@2820: 
Edouard@2820:     if(continue_collect) {
Edouard@2788:         int res;
Edouard@2788:         sbufidx = HMI_HASH_SIZE;
Edouard@2788:         if((res = traverse_hmi_tree(send_iterator)) == 0)
Edouard@2788:         {
Edouard@2799:             if(sbufidx > HMI_HASH_SIZE){
Edouard@2799:                 memcpy(&sbuf[0], &hmi_hash[0], HMI_HASH_SIZE);
Edouard@2799:                 *ptr = &sbuf[0];
Edouard@2799:                 *size = sbufidx;
Edouard@2799:                 return 0;
Edouard@2799:             }
Edouard@2799:             return ENODATA;
Edouard@2799:         }
Edouard@2799:         // printf("collected BAD result %%d\n", res);
Edouard@2788:         return res;
Edouard@2775:     }
Edouard@2775:     else
Edouard@2775:     {
Edouard@2775:         return EINTR;
Edouard@2775:     }
Edouard@2775: }
Edouard@2775: 
Edouard@2788: typedef enum {
Edouard@2788:     setval = 0,
Edouard@2788:     reset = 1,
Edouard@2798:     subscribe = 2
Edouard@2788: } cmd_from_JS;
Edouard@2788: 
Edouard@2822: // Returns :
Edouard@2822: //   0 is OK, <0 is error, 1 is heartbeat
Edouard@2788: int svghmi_recv_dispatch(uint32_t size, const uint8_t *ptr){
Edouard@2788:     const uint8_t* cursor = ptr + HMI_HASH_SIZE;
Edouard@2788:     const uint8_t* end = ptr + size;
Edouard@2788: 
Edouard@2822:     int was_hearbeat = 0;
Edouard@2788: 
Edouard@2788:     /* match hmitree fingerprint */
Edouard@2789:     if(size <= HMI_HASH_SIZE || memcmp(ptr, hmi_hash, HMI_HASH_SIZE) != 0)
Edouard@2788:     {
Edouard@2788:         printf("svghmi_recv_dispatch MISMATCH !!\n");
Edouard@2822:         return -EINVAL;
Edouard@2788:     }
Edouard@2788: 
Edouard@2788:     while(cursor < end)
Edouard@2788:     {
Edouard@2788:         uint32_t progress;
Edouard@2788:         cmd_from_JS cmd = *(cursor++);
Edouard@2788:         switch(cmd)
Edouard@2788:         {
Edouard@2788:             case setval:
Edouard@2788:             {
Edouard@2788:                 uint32_t index = *(uint32_t*)(cursor);
Edouard@2789:                 uint8_t const *valptr = cursor + sizeof(uint32_t);
Edouard@2822:                 
Edouard@2822:                 if(index == heartbeat_index)
Edouard@2822:                     was_hearbeat = 1;
Edouard@2788: 
Edouard@2788:                 if(index < HMI_ITEM_COUNT)
Edouard@2788:                 {
Edouard@2788:                     hmi_tree_item_t *dsc = &hmi_tree_item[index];
Edouard@2788:                     void *real_value_p = NULL;
Edouard@2788:                     char flags = 0;
Edouard@2788:                     void *visible_value_p = UnpackVar(dsc, &real_value_p, &flags);
Edouard@2788:                     void *dst_p = &rbuf[dsc->buf_index];
Edouard@2788:                     uint32_t sz = __get_type_enum_size(dsc->type);
Edouard@2829: 
Edouard@2829:                     if(__Is_a_string(dsc)){
Edouard@2829:                         sz = ((STRING*)valptr)->len + 1;
Edouard@2829:                     }
Edouard@2788: 
Edouard@2802:                     if((valptr + sz) <= end)
Edouard@2788:                     {
Edouard@2788:                         // rescheduling spinlock until free
Edouard@2788:                         while(AtomicCompareExchange(&dsc->rlock, 0, 1)) sched_yield();
Edouard@2788: 
Edouard@2788:                         memcpy(dst_p, valptr, sz);
Edouard@2788:                         dsc->rstate = buf_set;
Edouard@2788: 
Edouard@2788:                         AtomicCompareExchange(&dsc->rlock, 1, 0);
Edouard@2788:                         progress = sz + sizeof(uint32_t) /* index */;
Edouard@2788:                     }
Edouard@2799:                     else 
Edouard@2799:                     {
Edouard@2799:                         return -EINVAL;
Edouard@2799:                     }
Edouard@2799:                 }
Edouard@2799:                 else 
Edouard@2799:                 {
Edouard@2799:                     return -EINVAL;
Edouard@2799:                 }
Edouard@2788:             }
Edouard@2788:             break;
Edouard@2788: 
Edouard@2788:             case reset:
Edouard@2788:             {
Edouard@2788:                 progress = 0;
Edouard@2788:                 traverse_hmi_tree(reset_iterator);
Edouard@2788:             }
Edouard@2788:             break;
Edouard@2788: 
Edouard@2788:             case subscribe:
Edouard@2788:             {
Edouard@2788:                 uint32_t index = *(uint32_t*)(cursor);
Edouard@2788:                 uint16_t refresh_period_ms = *(uint32_t*)(cursor + sizeof(uint32_t));
Edouard@2788: 
Edouard@2788:                 if(index < HMI_ITEM_COUNT)
Edouard@2788:                 {
Edouard@2788:                     hmi_tree_item_t *dsc = &hmi_tree_item[index];
Edouard@2789:                     update_refresh_period(dsc, refresh_period_ms);
Edouard@2799:                 }
Edouard@2799:                 else 
Edouard@2799:                 {
Edouard@2799:                     return -EINVAL;
Edouard@2799:                 }
Edouard@2788: 
Edouard@2788:                 progress = sizeof(uint32_t) /* index */ + 
Edouard@2788:                            sizeof(uint16_t) /* refresh period */;
Edouard@2788:             }
Edouard@2788:             break;
Edouard@2799:             default:
Edouard@2799:                 printf("svghmi_recv_dispatch unknown %%d\n",cmd);
Edouard@2788: 
Edouard@2788:         }
Edouard@2788:         cursor += progress;
Edouard@2788:     }
Edouard@2822:     return was_hearbeat;
Edouard@2822: }
Edouard@2822: