svghmi/svghmi.c
changeset 3302 c89fc366bebd
parent 3295 0375d801fff7
child 3374 9a82918e063c
equal deleted inserted replaced
2744:577118ebd179 3302:c89fc366bebd
       
     1 #include <pthread.h>
       
     2 #include <errno.h>
       
     3 #include "iec_types_all.h"
       
     4 #include "POUS.h"
       
     5 #include "config.h"
       
     6 #include "beremiz.h"
       
     7 
       
     8 #define DEFAULT_REFRESH_PERIOD_MS 100
       
     9 #define HMI_BUFFER_SIZE %(buffer_size)d
       
    10 #define HMI_ITEM_COUNT %(item_count)d
       
    11 #define HMI_HASH_SIZE 8
       
    12 #define MAX_CONNECTIONS %(max_connections)d
       
    13 
       
    14 static uint8_t hmi_hash[HMI_HASH_SIZE] = {%(hmi_hash_ints)s};
       
    15 
       
    16 /* PLC reads from that buffer */
       
    17 static char rbuf[HMI_BUFFER_SIZE];
       
    18 
       
    19 /* PLC writes to that buffer */
       
    20 static char wbuf[HMI_BUFFER_SIZE];
       
    21 
       
    22 /* TODO change that in case of multiclient... */
       
    23 /* worst biggest send buffer. FIXME : use dynamic alloc ? */
       
    24 static char sbuf[HMI_HASH_SIZE +  HMI_BUFFER_SIZE + (HMI_ITEM_COUNT * sizeof(uint32_t))];
       
    25 static unsigned int sbufidx;
       
    26 
       
    27 %(extern_variables_declarations)s
       
    28 
       
    29 #define ticktime_ns %(PLC_ticktime)d
       
    30 static uint16_t ticktime_ms = (ticktime_ns>1000000)?
       
    31                      ticktime_ns/1000000:
       
    32                      1;
       
    33 
       
    34 typedef enum {
       
    35     buf_free = 0,
       
    36     buf_new,
       
    37     buf_set,
       
    38     buf_tosend
       
    39 } buf_state_t;
       
    40 
       
    41 static int global_write_dirty = 0;
       
    42 
       
    43 typedef struct {
       
    44     void *ptr;
       
    45     __IEC_types_enum type;
       
    46     uint32_t buf_index;
       
    47 
       
    48     /* publish/write/send */
       
    49     long wlock;
       
    50     buf_state_t wstate[MAX_CONNECTIONS];
       
    51 
       
    52     /* zero means not subscribed */
       
    53     uint16_t refresh_period_ms[MAX_CONNECTIONS];
       
    54     uint16_t age_ms[MAX_CONNECTIONS];
       
    55 
       
    56     /* retrieve/read/recv */
       
    57     long rlock;
       
    58     buf_state_t rstate;
       
    59 
       
    60 } hmi_tree_item_t;
       
    61 
       
    62 static hmi_tree_item_t hmi_tree_item[] = {
       
    63 %(variable_decl_array)s
       
    64 };
       
    65 
       
    66 typedef int(*hmi_tree_iterator)(uint32_t, hmi_tree_item_t*);
       
    67 static int traverse_hmi_tree(hmi_tree_iterator fp)
       
    68 {
       
    69     unsigned int i;
       
    70     for(i = 0; i < sizeof(hmi_tree_item)/sizeof(hmi_tree_item_t); i++){
       
    71         hmi_tree_item_t *dsc = &hmi_tree_item[i];
       
    72         int res = (*fp)(i, dsc);
       
    73         if(res != 0){
       
    74             return res;
       
    75         }
       
    76     }
       
    77     return 0;
       
    78 }
       
    79 
       
    80 #define __Unpack_desc_type hmi_tree_item_t
       
    81 
       
    82 %(var_access_code)s
       
    83 
       
    84 static int write_iterator(uint32_t index, hmi_tree_item_t *dsc)
       
    85 {
       
    86     uint32_t session_index = 0;
       
    87     int value_changed = 0;
       
    88     if(AtomicCompareExchange(&dsc->wlock, 0, 1) == 0) {
       
    89         void *dest_p = &wbuf[dsc->buf_index];
       
    90         void *real_value_p = NULL;
       
    91         char flags = 0;
       
    92         void *visible_value_p = UnpackVar(dsc, &real_value_p, &flags);
       
    93         USINT sz = __get_type_enum_size(dsc->type);
       
    94         if(__Is_a_string(dsc)){
       
    95             sz = ((STRING*)visible_value_p)->len + 1;
       
    96         }
       
    97         while(session_index < MAX_CONNECTIONS) {
       
    98             if(dsc->wstate[session_index] == buf_set){
       
    99                 /* if being subscribed */
       
   100                 if(dsc->refresh_period_ms[session_index]){
       
   101                     if(dsc->age_ms[session_index] + ticktime_ms < dsc->refresh_period_ms[session_index]){
       
   102                         dsc->age_ms[session_index] += ticktime_ms;
       
   103                     }else{
       
   104                         dsc->wstate[session_index] = buf_tosend;
       
   105                         global_write_dirty = 1;
       
   106                     }
       
   107                 }
       
   108             }
       
   109 
       
   110             if(dsc->wstate[session_index] == buf_new /* just subscribed 
       
   111                or already subscribed having value change */
       
   112                || (dsc->refresh_period_ms[session_index] > 0 
       
   113                    && (value_changed || (value_changed=memcmp(dest_p, visible_value_p, sz))) != 0)){
       
   114                 /* if not already marked/signaled, do it */
       
   115                 if(dsc->wstate[session_index] != buf_set && dsc->wstate[session_index] != buf_tosend) {
       
   116                     if(dsc->wstate[session_index] == buf_new || ticktime_ms > dsc->refresh_period_ms[session_index]){
       
   117                         dsc->wstate[session_index] = buf_tosend;
       
   118                         global_write_dirty = 1;
       
   119                     } else {
       
   120                         dsc->wstate[session_index] = buf_set;
       
   121                     }
       
   122                     dsc->age_ms[session_index] = 0;
       
   123                 }
       
   124             }
       
   125 
       
   126             session_index++;
       
   127         }
       
   128         /* copy value if changed (and subscribed) */
       
   129         if(value_changed)
       
   130             memcpy(dest_p, visible_value_p, sz);
       
   131         AtomicCompareExchange(&dsc->wlock, 1, 0);
       
   132     }
       
   133     // else ... : PLC can't wait, variable will be updated next turn
       
   134     return 0;
       
   135 }
       
   136 
       
   137 static uint32_t send_session_index;
       
   138 static int send_iterator(uint32_t index, hmi_tree_item_t *dsc)
       
   139 {
       
   140     while(AtomicCompareExchange(&dsc->wlock, 0, 1))
       
   141         nRT_reschedule();
       
   142 
       
   143     if(dsc->wstate[send_session_index] == buf_tosend)
       
   144     {
       
   145         uint32_t sz = __get_type_enum_size(dsc->type);
       
   146         if(sbufidx + sizeof(uint32_t) + sz <=  sizeof(sbuf))
       
   147         {
       
   148             void *src_p = &wbuf[dsc->buf_index];
       
   149             void *dst_p = &sbuf[sbufidx];
       
   150             if(__Is_a_string(dsc)){
       
   151                 sz = ((STRING*)src_p)->len + 1;
       
   152             }
       
   153             /* TODO : force into little endian */
       
   154             memcpy(dst_p, &index, sizeof(uint32_t));
       
   155             memcpy(dst_p + sizeof(uint32_t), src_p, sz);
       
   156             dsc->wstate[send_session_index] = buf_free;
       
   157             sbufidx += sizeof(uint32_t) /* index */ + sz;
       
   158         }
       
   159         else
       
   160         {
       
   161             printf("BUG!!! %%d + %%ld + %%d >  %%ld \n", sbufidx, sizeof(uint32_t), sz,  sizeof(sbuf));
       
   162             AtomicCompareExchange(&dsc->wlock, 1, 0);
       
   163             return EOVERFLOW;
       
   164         }
       
   165     }
       
   166 
       
   167     AtomicCompareExchange(&dsc->wlock, 1, 0);
       
   168     return 0;
       
   169 }
       
   170 
       
   171 static int read_iterator(uint32_t index, hmi_tree_item_t *dsc)
       
   172 {
       
   173     if(AtomicCompareExchange(&dsc->rlock, 0, 1) == 0)
       
   174     {
       
   175         if(dsc->rstate == buf_set)
       
   176         {
       
   177             void *src_p = &rbuf[dsc->buf_index];
       
   178             void *real_value_p = NULL;
       
   179             char flags = 0;
       
   180             void *visible_value_p = UnpackVar(dsc, &real_value_p, &flags);
       
   181             memcpy(real_value_p, src_p, __get_type_enum_size(dsc->type));
       
   182             dsc->rstate = buf_free;
       
   183         }
       
   184         AtomicCompareExchange(&dsc->rlock, 1, 0);
       
   185     }
       
   186     // else ... : PLC can't wait, variable will be updated next turn
       
   187     return 0;
       
   188 }
       
   189 
       
   190 void update_refresh_period(hmi_tree_item_t *dsc, uint32_t session_index, uint16_t refresh_period_ms)
       
   191 {
       
   192     while(AtomicCompareExchange(&dsc->wlock, 0, 1)) 
       
   193         nRT_reschedule();
       
   194 
       
   195     if(refresh_period_ms) {
       
   196         if(!dsc->refresh_period_ms[session_index])
       
   197         {
       
   198             dsc->wstate[session_index] = buf_new;
       
   199         }
       
   200     } else {
       
   201         dsc->wstate[session_index] = buf_free;
       
   202     }
       
   203     dsc->refresh_period_ms[session_index] = refresh_period_ms;
       
   204     AtomicCompareExchange(&dsc->wlock, 1, 0);
       
   205 }
       
   206 
       
   207 static uint32_t reset_session_index;
       
   208 static int reset_iterator(uint32_t index, hmi_tree_item_t *dsc)
       
   209 {
       
   210     update_refresh_period(dsc, reset_session_index, 0);
       
   211     return 0;
       
   212 }
       
   213 
       
   214 static void *svghmi_handle;
       
   215 
       
   216 void SVGHMI_SuspendFromPythonThread(void)
       
   217 {
       
   218     wait_RT_to_nRT_signal(svghmi_handle);
       
   219 }
       
   220 
       
   221 void SVGHMI_WakeupFromRTThread(void)
       
   222 {
       
   223     unblock_RT_to_nRT_signal(svghmi_handle);
       
   224 }
       
   225 
       
   226 int svghmi_continue_collect;
       
   227 
       
   228 int __init_svghmi()
       
   229 {
       
   230     memset(rbuf,0,sizeof(rbuf));
       
   231     memset(wbuf,0,sizeof(wbuf));
       
   232 
       
   233     svghmi_continue_collect = 1;
       
   234 
       
   235     /* create svghmi_pipe */
       
   236     svghmi_handle = create_RT_to_nRT_signal("SVGHMI_pipe");
       
   237 
       
   238     if(!svghmi_handle) 
       
   239         return 1;
       
   240 
       
   241     return 0;
       
   242 }
       
   243 
       
   244 void __cleanup_svghmi()
       
   245 {
       
   246     svghmi_continue_collect = 0;
       
   247     SVGHMI_WakeupFromRTThread();
       
   248     delete_RT_to_nRT_signal(svghmi_handle);
       
   249 }
       
   250 
       
   251 void __retrieve_svghmi()
       
   252 {
       
   253     traverse_hmi_tree(read_iterator);
       
   254 }
       
   255 
       
   256 void __publish_svghmi()
       
   257 {
       
   258     global_write_dirty = 0;
       
   259     traverse_hmi_tree(write_iterator);
       
   260     if(global_write_dirty) {
       
   261         SVGHMI_WakeupFromRTThread();
       
   262     }
       
   263 }
       
   264 
       
   265 /* PYTHON CALLS */
       
   266 int svghmi_wait(void){
       
   267 
       
   268     SVGHMI_SuspendFromPythonThread();
       
   269 }
       
   270 
       
   271 int svghmi_send_collect(uint32_t session_index, uint32_t *size, char **ptr){
       
   272 
       
   273     if(svghmi_continue_collect) {
       
   274         int res;
       
   275         sbufidx = HMI_HASH_SIZE;
       
   276         send_session_index = session_index;
       
   277         if((res = traverse_hmi_tree(send_iterator)) == 0)
       
   278         {
       
   279             if(sbufidx > HMI_HASH_SIZE){
       
   280                 memcpy(&sbuf[0], &hmi_hash[0], HMI_HASH_SIZE);
       
   281                 *ptr = &sbuf[0];
       
   282                 *size = sbufidx;
       
   283                 return 0;
       
   284             }
       
   285             return ENODATA;
       
   286         }
       
   287         // printf("collected BAD result %%d\n", res);
       
   288         return res;
       
   289     }
       
   290     else
       
   291     {
       
   292         return EINTR;
       
   293     }
       
   294 }
       
   295 
       
   296 typedef enum {
       
   297     setval = 0,
       
   298     reset = 1,
       
   299     subscribe = 2
       
   300 } cmd_from_JS;
       
   301 
       
   302 int svghmi_reset(uint32_t session_index){
       
   303     reset_session_index = session_index;
       
   304     traverse_hmi_tree(reset_iterator);
       
   305     return 1;
       
   306 }
       
   307 
       
   308 // Returns :
       
   309 //   0 is OK, <0 is error, 1 is heartbeat
       
   310 int svghmi_recv_dispatch(uint32_t session_index, uint32_t size, const uint8_t *ptr){
       
   311     const uint8_t* cursor = ptr + HMI_HASH_SIZE;
       
   312     const uint8_t* end = ptr + size;
       
   313 
       
   314     int was_hearbeat = 0;
       
   315 
       
   316     /* match hmitree fingerprint */
       
   317     if(size <= HMI_HASH_SIZE || memcmp(ptr, hmi_hash, HMI_HASH_SIZE) != 0)
       
   318     {
       
   319         printf("svghmi_recv_dispatch MISMATCH !!\n");
       
   320         return -EINVAL;
       
   321     }
       
   322 
       
   323     while(cursor < end)
       
   324     {
       
   325         uint32_t progress;
       
   326         cmd_from_JS cmd = *(cursor++);
       
   327         switch(cmd)
       
   328         {
       
   329             case setval:
       
   330             {
       
   331                 uint32_t index = *(uint32_t*)(cursor);
       
   332                 uint8_t const *valptr = cursor + sizeof(uint32_t);
       
   333 
       
   334                 if(index == heartbeat_index)
       
   335                     was_hearbeat = 1;
       
   336 
       
   337                 if(index < HMI_ITEM_COUNT)
       
   338                 {
       
   339                     hmi_tree_item_t *dsc = &hmi_tree_item[index];
       
   340                     void *real_value_p = NULL;
       
   341                     char flags = 0;
       
   342                     void *visible_value_p = UnpackVar(dsc, &real_value_p, &flags);
       
   343                     void *dst_p = &rbuf[dsc->buf_index];
       
   344                     uint32_t sz = __get_type_enum_size(dsc->type);
       
   345 
       
   346                     if(__Is_a_string(dsc)){
       
   347                         sz = ((STRING*)valptr)->len + 1;
       
   348                     }
       
   349 
       
   350                     if((valptr + sz) <= end)
       
   351                     {
       
   352                         // rescheduling spinlock until free
       
   353                         while(AtomicCompareExchange(&dsc->rlock, 0, 1)) 
       
   354                             nRT_reschedule();
       
   355 
       
   356                         memcpy(dst_p, valptr, sz);
       
   357                         dsc->rstate = buf_set;
       
   358 
       
   359                         AtomicCompareExchange(&dsc->rlock, 1, 0);
       
   360                         progress = sz + sizeof(uint32_t) /* index */;
       
   361                     }
       
   362                     else
       
   363                     {
       
   364                         return -EINVAL;
       
   365                     }
       
   366                 }
       
   367                 else
       
   368                 {
       
   369                     return -EINVAL;
       
   370                 }
       
   371             }
       
   372             break;
       
   373 
       
   374             case reset:
       
   375             {
       
   376                 progress = 0;
       
   377                 reset_session_index = session_index;
       
   378                 traverse_hmi_tree(reset_iterator);
       
   379             }
       
   380             break;
       
   381 
       
   382             case subscribe:
       
   383             {
       
   384                 uint32_t index = *(uint32_t*)(cursor);
       
   385                 uint16_t refresh_period_ms = *(uint32_t*)(cursor + sizeof(uint32_t));
       
   386 
       
   387                 if(index < HMI_ITEM_COUNT)
       
   388                 {
       
   389                     hmi_tree_item_t *dsc = &hmi_tree_item[index];
       
   390                     update_refresh_period(dsc, session_index, refresh_period_ms);
       
   391                 }
       
   392                 else
       
   393                 {
       
   394                     return -EINVAL;
       
   395                 }
       
   396 
       
   397                 progress = sizeof(uint32_t) /* index */ +
       
   398                            sizeof(uint16_t) /* refresh period */;
       
   399             }
       
   400             break;
       
   401             default:
       
   402                 printf("svghmi_recv_dispatch unknown %%d\n",cmd);
       
   403 
       
   404         }
       
   405         cursor += progress;
       
   406     }
       
   407     return was_hearbeat;
       
   408 }
       
   409