Edouard@2765: #include Edouard@2776: #include 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@2828: #warning TODO: size of string in recv 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: