svghmi/svghmi.c
branchsvghmi
changeset 2788 2ed9ff826d03
parent 2779 75c6a31caca6
child 2789 ba0dd2ec6dc4
equal deleted inserted replaced
2787:86a572fb05f8 2788:2ed9ff826d03
     6 #include "beremiz.h"
     6 #include "beremiz.h"
     7 
     7 
     8 #define DEFAULT_REFRESH_PERIOD_MS 100
     8 #define DEFAULT_REFRESH_PERIOD_MS 100
     9 #define HMI_BUFFER_SIZE %(buffer_size)d
     9 #define HMI_BUFFER_SIZE %(buffer_size)d
    10 #define HMI_ITEM_COUNT %(item_count)d
    10 #define HMI_ITEM_COUNT %(item_count)d
       
    11 #define HMI_HASH_SIZE 8
       
    12 static uint8_t hmi_hash[HMI_HASH_SIZE] = {%(hmi_hash_ints)s};
    11 
    13 
    12 /* PLC reads from that buffer */
    14 /* PLC reads from that buffer */
    13 static char rbuf[HMI_BUFFER_SIZE];
    15 static char rbuf[HMI_BUFFER_SIZE];
    14 
    16 
    15 /* PLC writes to that buffer */
    17 /* PLC writes to that buffer */
    16 static char wbuf[HMI_BUFFER_SIZE];
    18 static char wbuf[HMI_BUFFER_SIZE];
    17 
    19 
       
    20 /* TODO change that in case of multiclient... */
       
    21 /* worst biggest send buffer. FIXME : use dynamic alloc ? */
       
    22 static char sbuf[HMI_HASH_SIZE +  HMI_BUFFER_SIZE + (HMI_ITEM_COUNT * sizeof(uint32_t))];
       
    23 static unsigned int sbufidx;
       
    24 
    18 %(extern_variables_declarations)s
    25 %(extern_variables_declarations)s
    19 
    26 
    20 #define ticktime_ns %(PLC_ticktime)d
    27 #define ticktime_ns %(PLC_ticktime)d
    21 uint16_t ticktime_ms = (ticktime_ns>1000000)?
    28 static uint16_t ticktime_ms = (ticktime_ns>1000000)?
    22                      ticktime_ns/1000000:
    29                      ticktime_ns/1000000:
    23                      1;
    30                      1;
    24 
    31 
    25 typedef enum {
    32 typedef enum {
    26     buf_free = 0,
    33     buf_free = 0,
    27     buf_set,
    34     buf_set,
    28     buf_tosend
    35     buf_tosend
    29 } buf_state_t;
    36 } buf_state_t;
    30 
    37 
    31 int global_write_dirty = 0;
    38 static int global_write_dirty = 0;
    32 
    39 
    33 typedef struct {
    40 typedef struct {
    34     void *ptr;
    41     void *ptr;
    35     __IEC_types_enum type;
    42     __IEC_types_enum type;
    36     uint32_t buf_index;
    43     uint32_t buf_index;
    37 
    44 
    38     /* publish/write/send */
    45     /* publish/write/send */
    39     long wlock;
    46     long wlock;
       
    47     buf_state_t wstate;
       
    48 
    40     /* zero means not subscribed */
    49     /* zero means not subscribed */
    41     uint16_t refresh_period_ms;
    50     uint16_t refresh_period_ms;
    42     uint16_t age_ms;
    51     uint16_t age_ms;
    43 
    52 
    44     buf_state_t wstate;
       
    45 
       
    46     /* retrieve/read/recv */
    53     /* retrieve/read/recv */
    47     long rlock;
    54     long rlock;
    48     buf_state_t rstate;
    55     buf_state_t rstate;
    49 
    56 
    50 } hmi_tree_item_t;
    57 } hmi_tree_item_t;
    51 
    58 
    52 static hmi_tree_item_t hmi_tree_item[] = {
    59 static hmi_tree_item_t hmi_tree_item[] = {
    53 %(variable_decl_array)s
    60 %(variable_decl_array)s
    54 };
    61 };
    55 
    62 
    56 static char sendbuf[HMI_BUFFER_SIZE];
    63 typedef int(*hmi_tree_iterator)(uint32_t*, hmi_tree_item_t*);
    57 
    64 static int traverse_hmi_tree(hmi_tree_iterator fp)
    58 typedef void(*hmi_tree_iterator)(hmi_tree_item_t*);
       
    59 void traverse_hmi_tree(hmi_tree_iterator fp)
       
    60 {
    65 {
    61     unsigned int i;
    66     unsigned int i;
    62     for(i = 0; i < sizeof(hmi_tree_item)/sizeof(hmi_tree_item_t); i++){
    67     for(i = 0; i < sizeof(hmi_tree_item)/sizeof(hmi_tree_item_t); i++){
       
    68         int res;
    63         hmi_tree_item_t *dsc = &hmi_tree_item[i];
    69         hmi_tree_item_t *dsc = &hmi_tree_item[i];
    64         if(dsc->type != UNKNOWN_ENUM) 
    70         if(res = (*fp)(i, dsc))
    65             (*fp)(dsc);
    71             return res;
    66     }
    72     }
    67 }
    73 }
    68 
    74 
    69 #define __Unpack_desc_type hmi_tree_item_t
    75 #define __Unpack_desc_type hmi_tree_item_t
    70 
    76 
    71 %(var_access_code)s
    77 %(var_access_code)s
    72 
    78 
    73 void write_iterator(hmi_tree_item_t *dsc)
    79 inline int write_iterator(uint32_t index, hmi_tree_item_t *dsc)
    74 {
    80 {
    75     void *dest_p = &wbuf[dsc->buf_index];
    81     if(AtomicCompareExchange(&dsc->wlock, 0, 1) == 0)
    76     void *real_value_p = NULL;
    82     {
    77     char flags = 0;
    83         if(dsc->wstate == buf_set){
    78 
    84             /* if being subscribed */
    79     void *visible_value_p = UnpackVar(dsc, &real_value_p, &flags);
    85             if(dsc->refresh_period_ms){
    80 
    86                 if(dsc->age_ms + ticktime_ms < dsc->refresh_period_ms){
    81     /* Try take lock */
    87                     dsc->age_ms += ticktime_ms;
    82     long was_locked = AtomicCompareExchange(&dsc->wlock, 0, 1);
    88                 }else{
    83 
    89                     dsc->wstate = buf_tosend;
    84     if(was_locked) {
    90                 }
    85         /* was locked. give up*/
    91             }
    86         return;
    92         }
    87     }
    93 
    88 
    94         void *dest_p = &wbuf[dsc->buf_index];
    89     if(dsc->wstate == buf_set){
    95         void *real_value_p = NULL;
    90         /* if being subscribed */
    96         char flags = 0;
    91         if(dsc->refresh_period_ms){
    97         void *visible_value_p = UnpackVar(dsc, &real_value_p, &flags);
    92             if(dsc->age_ms + ticktime_ms < dsc->refresh_period_ms){
    98 
    93                 dsc->age_ms += ticktime_ms;
    99         /* if new value differs from previous one */
    94             }else{
   100         USINT sz = __get_type_enum_size(dsc->type);
    95                 dsc->wstate = buf_tosend;
   101         if(memcmp(dest_p, visible_value_p, sz) != 0){
    96             }
   102             /* copy and flag as set */
    97         }
   103             memcpy(dest_p, visible_value_p, sz);
    98     }
   104             if(dsc->wstate == buf_free) {
    99 
   105                 dsc->wstate = buf_set;
   100     /* if new value differs from previous one */
   106                 dsc->age_ms = 0;
   101     if(memcmp(dest_p, visible_value_p, __get_type_enum_size(dsc->type)) != 0){
   107             }
   102         /* copy and flag as set */
   108             global_write_dirty = 1;
   103         memcpy(dest_p, visible_value_p, __get_type_enum_size(dsc->type));
   109         }
   104         if(dsc->wstate == buf_free) {
   110 
   105             dsc->wstate = buf_set;
   111         AtomicCompareExchange(&dsc->wlock, 1, 0);
   106             dsc->age_ms = 0;
   112     }
   107         }
   113     // else ... : PLC can't wait, variable will be updated next turn
   108         global_write_dirty = 1;
   114     return 0;
   109     }
   115 }
   110 
   116 
   111     /* unlock - use AtomicComparExchange to have memory barrier */
   117 inline int send_iterator(uint32_t index, hmi_tree_item_t *dsc)
       
   118 {
       
   119     int res = 0;
       
   120     while(AtomicCompareExchange(&dsc->wlock, 0, 1)) sched_yield();
       
   121 
       
   122     if(dsc->wstate == buf_tosend)
       
   123     {
       
   124         uint32_t sz = __get_type_enum_size(dsc->type);
       
   125         if(sbufidx + sizeof(uint32_t) + sz <  sizeof(sbuf))
       
   126         {
       
   127             void *src_p = &wbuf[dsc->buf_index];
       
   128             void *dst_p = &sbuf[sbufidx];
       
   129             memcpy(dst_p, &index, sizeof(uint32_t));
       
   130             memcpy(dst_p + sizeof(uint32_t), src_p, sz);
       
   131             dsc->wstate = buf_free;
       
   132             sbufidx += sizeof(uint32_t) /* index */ + sz;
       
   133         }
       
   134         else
       
   135         {
       
   136             res = EOVERFLOW;
       
   137         }
       
   138     }
       
   139 
   112     AtomicCompareExchange(&dsc->wlock, 1, 0);
   140     AtomicCompareExchange(&dsc->wlock, 1, 0);
   113 }
   141     return res; 
   114 
   142 }
   115 struct timespec sending_now;
   143 
   116 struct timespec next_sending;
   144 inline int read_iterator(uint32_t index, hmi_tree_item_t *dsc)
   117 void send_iterator(hmi_tree_item_t *dsc)
   145 {
       
   146     if(AtomicCompareExchange(&dsc->rlock, 0, 1) == 0)
       
   147     {
       
   148         if(dsc->rstate == buf_set)
       
   149         {
       
   150             void *src_p = &rbuf[dsc->buf_index];
       
   151             void *real_value_p = NULL;
       
   152             char flags = 0;
       
   153             void *visible_value_p = UnpackVar(dsc, &real_value_p, &flags);
       
   154             memcpy(real_value_p, src_p, __get_type_enum_size(dsc->type));
       
   155             dsc->rstate = buf_free;
       
   156         }
       
   157         AtomicCompareExchange(&dsc->rlock, 1, 0);
       
   158     }
       
   159     // else ... : PLC can't wait, variable will be updated next turn
       
   160     return 0;
       
   161 }
       
   162 
       
   163 inline void update_refresh_period(hmi_tree_item_t *dsc, uint16_t refresh_period_ms)
   118 {
   164 {
   119     while(AtomicCompareExchange(&dsc->wlock, 0, 1)) sched_yield();
   165     while(AtomicCompareExchange(&dsc->wlock, 0, 1)) sched_yield();
   120 
   166     dsc->refresh_period_ms = refresh_period_ms;
   121     // check for variable being modified
       
   122     if(dsc->wstate == buf_tosend){
       
   123         // send 
       
   124 
       
   125         // TODO pack data in buffer
       
   126 
       
   127         dsc->wstate = buf_free;
       
   128     }
       
   129 
       
   130     AtomicCompareExchange(&dsc->wlock, 1, 0);
   167     AtomicCompareExchange(&dsc->wlock, 1, 0);
   131 }
   168 }
   132 
   169 
   133 void read_iterator(hmi_tree_item_t *dsc)
   170 inline int reset_iterator(uint32_t index, hmi_tree_item_t *dsc)
   134 {
   171 {
   135     void *src_p = &rbuf[dsc->buf_index];
   172     update_refresh_period(*dsc, 0);
   136     void *real_value_p = NULL;
   173     return 0;
   137     char flags = 0;
       
   138 
       
   139     void *visible_value_p = UnpackVar(dsc, &real_value_p, &flags);
       
   140 
       
   141 
       
   142     memcpy(visible_value_p, src_p, __get_type_enum_size(dsc->type));
       
   143 }
   174 }
   144 
   175 
   145 static pthread_cond_t svghmi_send_WakeCond = PTHREAD_COND_INITIALIZER;
   176 static pthread_cond_t svghmi_send_WakeCond = PTHREAD_COND_INITIALIZER;
   146 static pthread_mutex_t svghmi_send_WakeCondLock = PTHREAD_MUTEX_INITIALIZER;
   177 static pthread_mutex_t svghmi_send_WakeCondLock = PTHREAD_MUTEX_INITIALIZER;
   147 
   178 
   182 int svghmi_send_collect(uint32_t *size, char **ptr){
   213 int svghmi_send_collect(uint32_t *size, char **ptr){
   183 
   214 
   184     int do_collect;
   215     int do_collect;
   185     pthread_mutex_lock(&svghmi_send_WakeCondLock);
   216     pthread_mutex_lock(&svghmi_send_WakeCondLock);
   186     do_collect = continue_collect;
   217     do_collect = continue_collect;
   187     if(do_collect){
   218     if(do_collect)
       
   219     {
   188         pthread_cond_wait(&svghmi_send_WakeCond, &svghmi_send_WakeCondLock);
   220         pthread_cond_wait(&svghmi_send_WakeCond, &svghmi_send_WakeCondLock);
   189         do_collect = continue_collect;
   221         do_collect = continue_collect;
   190     }
   222     }
   191     pthread_mutex_unlock(&svghmi_send_WakeCondLock);
   223     pthread_mutex_unlock(&svghmi_send_WakeCondLock);
   192 
   224 
   193 
       
   194     if(do_collect) {
   225     if(do_collect) {
   195         traverse_hmi_tree(send_iterator);
   226         int res;
   196         /* TODO set ptr and size to something  */
   227         memcpy(&sbuf[0], &hmi_hash[0], HMI_HASH_SIZE);
   197         return 0;
   228         sbufidx = HMI_HASH_SIZE;
       
   229         if((res = traverse_hmi_tree(send_iterator)) == 0)
       
   230         {
       
   231             *ptr = &sbuf[0];
       
   232             *size = sbufidx;
       
   233         }
       
   234         return res;
   198     }
   235     }
   199     else
   236     else
   200     {
   237     {
   201         return EINTR;
   238         return EINTR;
   202     }
   239     }
   203 }
   240 }
   204 
   241 
   205 int svghmi_recv_dispatch(uint32_t size, char *ptr){
   242 typedef enum {
   206     printf("%%*s",size,ptr);
   243     setval = 0,
   207     /* TODO something with ptr and size
   244     reset = 1,
   208         - subscribe
   245     subscribe = 2,
   209          or
   246     unsubscribe = 3
   210         - spread values
   247 } cmd_from_JS;
   211     */
   248 
   212 }
   249 int svghmi_recv_dispatch(uint32_t size, const uint8_t *ptr){
   213 
   250     const uint8_t* cursor = ptr + HMI_HASH_SIZE;
       
   251     const uint8_t* end = ptr + size;
       
   252 
       
   253     printf("svghmi_recv_dispatch %d\n",size);
       
   254 
       
   255     /* match hmitree fingerprint */
       
   256     if(size <= HMI_HASH_SIZE || memcmp(ptr, hmihash, HMI_HASH_SIZE) != 0)
       
   257     {
       
   258         printf("svghmi_recv_dispatch MISMATCH !!\n");
       
   259         return EINVAL;
       
   260     }
       
   261 
       
   262     while(cursor < end)
       
   263     {
       
   264         uint32_t progress;
       
   265         cmd_from_JS cmd = *(cursor++);
       
   266         switch(cmd)
       
   267         {
       
   268             case setval:
       
   269             {
       
   270                 uint32_t index = *(uint32_t*)(cursor);
       
   271                 uint8_t *valptr = cursor + sizeof(uint32_t);
       
   272 
       
   273                 if(index < HMI_ITEM_COUNT)
       
   274                 {
       
   275                     hmi_tree_item_t *dsc = &hmi_tree_item[index];
       
   276                     void *real_value_p = NULL;
       
   277                     char flags = 0;
       
   278                     void *visible_value_p = UnpackVar(dsc, &real_value_p, &flags);
       
   279                     void *dst_p = &rbuf[dsc->buf_index];
       
   280                     uint32_t sz = __get_type_enum_size(dsc->type);
       
   281 
       
   282                     if(valptr + sz < end)
       
   283                     {
       
   284                         // rescheduling spinlock until free
       
   285                         while(AtomicCompareExchange(&dsc->rlock, 0, 1)) sched_yield();
       
   286 
       
   287                         memcpy(dst_p, valptr, sz);
       
   288                         dsc->rstate = buf_set;
       
   289 
       
   290                         AtomicCompareExchange(&dsc->rlock, 1, 0);
       
   291                         progress = sz + sizeof(uint32_t) /* index */;
       
   292                     }
       
   293                     else return -EINVAL;
       
   294                 }
       
   295                 else return -EINVAL;
       
   296             }
       
   297             break;
       
   298 
       
   299             case reset:
       
   300             {
       
   301                 progress = 0;
       
   302                 traverse_hmi_tree(reset_iterator);
       
   303             }
       
   304             break;
       
   305 
       
   306             case subscribe:
       
   307             {
       
   308                 uint32_t index = *(uint32_t*)(cursor);
       
   309                 uint16_t refresh_period_ms = *(uint32_t*)(cursor + sizeof(uint32_t));
       
   310 
       
   311                 if(index < HMI_ITEM_COUNT)
       
   312                 {
       
   313                     hmi_tree_item_t *dsc = &hmi_tree_item[index];
       
   314                     update_refresh_period(*dsc, refresh_period_ms);
       
   315                 }
       
   316                 else return -EINVAL;
       
   317 
       
   318                 progress = sizeof(uint32_t) /* index */ + 
       
   319                            sizeof(uint16_t) /* refresh period */;
       
   320             }
       
   321             break;
       
   322 
       
   323             case unsubscribe:
       
   324             {
       
   325                 if(index < HMI_ITEM_COUNT)
       
   326                 {
       
   327                     hmi_tree_item_t *dsc = &hmi_tree_item[index];
       
   328                     reset_iterator(index, dsc);
       
   329                 }
       
   330                 else return -EINVAL;
       
   331 
       
   332                 progress = sizeof(uint32_t) /* index */;
       
   333             }
       
   334             break;
       
   335         }
       
   336         cursor += progress;
       
   337     }
       
   338     return 0;
       
   339 }
       
   340