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@3270: #define MAX_CONNECTIONS %(max_connections)d Edouard@3399: #define MAX_CON_INDEX MAX_CONNECTIONS - 1 Edouard@3270: 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: /* 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@3374: static long hmitree_rlock = 0; Edouard@3374: static long hmitree_wlock = 0; Edouard@2764: Edouard@3399: typedef struct hmi_tree_item_s hmi_tree_item_t; Edouard@3399: struct hmi_tree_item_s{ Edouard@2764: void *ptr; Edouard@2764: __IEC_types_enum type; Edouard@2765: uint32_t buf_index; Edouard@2768: Edouard@3399: /* retrieve/read/recv */ Edouard@3399: buf_state_t rstate; Edouard@3399: Edouard@2768: /* publish/write/send */ Edouard@3270: buf_state_t wstate[MAX_CONNECTIONS]; Edouard@2788: Edouard@2768: /* zero means not subscribed */ Edouard@3270: uint16_t refresh_period_ms[MAX_CONNECTIONS]; Edouard@3270: uint16_t age_ms[MAX_CONNECTIONS]; Edouard@2768: Edouard@3399: /* dual linked list for subscriptions */ Edouard@3399: hmi_tree_item_t *subscriptions_next; Edouard@3399: hmi_tree_item_t *subscriptions_prev; Edouard@3399: Edouard@3399: /* single linked list for changes from HMI */ Edouard@3399: hmi_tree_item_t *incoming_prev; Edouard@3399: Edouard@3399: }; Edouard@3399: Edouard@3399: #define HMITREE_ITEM_INITIALIZER(cpath,type,buf_index) { \ Edouard@3399: &(cpath), /*ptr*/ \ Edouard@3399: type, /*type*/ \ Edouard@3399: buf_index, /*buf_index*/ \ Edouard@3399: buf_free, /*rstate*/ \ Edouard@3399: {[0 ... MAX_CON_INDEX] = buf_free}, /*wstate*/ \ Edouard@3399: {[0 ... MAX_CON_INDEX] = 0}, /*refresh_period_ms*/ \ Edouard@3399: {[0 ... MAX_CON_INDEX] = 0}, /*age_ms*/ \ Edouard@3399: NULL, /*subscriptions_next*/\ Edouard@3399: NULL, /*subscriptions_prev*/\ Edouard@3399: NULL} /*incoming_next*/ Edouard@3399: Edouard@3399: Edouard@3399: /* entry for dual linked list for HMI subscriptions */ Edouard@3399: /* points to the end of the list */ Edouard@3399: static hmi_tree_item_t *subscriptions_tail = NULL; Edouard@3399: Edouard@3399: /* entry for single linked list for changes from HMI */ Edouard@3399: /* points to the end of the list */ Edouard@3399: static hmi_tree_item_t *incoming_tail = NULL; Edouard@3399: Edouard@3399: static hmi_tree_item_t hmi_tree_items[] = { Edouard@2764: %(variable_decl_array)s Edouard@2764: }; Edouard@2764: Edouard@2767: #define __Unpack_desc_type hmi_tree_item_t Edouard@2766: Edouard@2767: %(var_access_code)s Edouard@2765: Edouard@3399: static int write_iterator(hmi_tree_item_t *dsc) Edouard@2788: { edouard@3400: uint32_t session_index = 0; edouard@3400: int value_changed = 0; edouard@3400: void *dest_p = NULL; edouard@3400: void *value_p = NULL; edouard@3400: size_t sz = 0; edouard@3400: while(session_index < MAX_CONNECTIONS) { edouard@3400: if(dsc->wstate[session_index] == buf_set){ edouard@3400: /* if being subscribed */ edouard@3400: if(dsc->refresh_period_ms[session_index]){ edouard@3400: if(dsc->age_ms[session_index] + ticktime_ms < dsc->refresh_period_ms[session_index]){ edouard@3400: dsc->age_ms[session_index] += ticktime_ms; edouard@3400: }else{ edouard@3400: dsc->wstate[session_index] = buf_tosend; edouard@3400: global_write_dirty = 1; edouard@3400: } edouard@3400: } edouard@3400: } edouard@3400: edouard@3400: /* variable is sample only if just subscribed edouard@3400: or already subscribed and having value change */ edouard@3400: int do_sample = 0; edouard@3400: int just_subscribed = dsc->wstate[session_index] == buf_new; edouard@3400: if(!just_subscribed){ edouard@3400: int already_subscribed = dsc->refresh_period_ms[session_index] > 0; edouard@3400: if(already_subscribed){ edouard@3400: if(!value_changed){ edouard@3400: if(!value_p){ edouard@3400: UnpackVar(dsc, &value_p, NULL, &sz); edouard@3400: if(__Is_a_string(dsc)){ edouard@3400: sz = ((STRING*)value_p)->len + 1; Edouard@3374: } edouard@3400: dest_p = &wbuf[dsc->buf_index]; edouard@3400: } edouard@3400: value_changed = memcmp(dest_p, value_p, sz) != 0; edouard@3400: do_sample = value_changed; edouard@3400: }else{ edouard@3400: do_sample = 1; edouard@3400: } edouard@3400: } edouard@3400: } else { edouard@3400: do_sample = 1; edouard@3400: } edouard@3400: edouard@3400: edouard@3400: if(do_sample){ edouard@3400: if(dsc->wstate[session_index] != buf_set && dsc->wstate[session_index] != buf_tosend) { edouard@3400: if(dsc->wstate[session_index] == buf_new \ edouard@3400: || ticktime_ms > dsc->refresh_period_ms[session_index]){ edouard@3400: dsc->wstate[session_index] = buf_tosend; edouard@3400: global_write_dirty = 1; edouard@3400: } else { edouard@3400: dsc->wstate[session_index] = buf_set; edouard@3400: } edouard@3400: dsc->age_ms[session_index] = 0; edouard@3400: } edouard@3400: } edouard@3400: edouard@3400: session_index++; edouard@3400: } edouard@3400: /* copy value if changed (and subscribed) */ edouard@3400: if(value_changed) edouard@3400: memcpy(dest_p, value_p, sz); Edouard@2788: return 0; Edouard@2788: } Edouard@2788: Edouard@3399: static int send_iterator(uint32_t index, hmi_tree_item_t *dsc, uint32_t session_index) Edouard@3399: { Edouard@3399: if(dsc->wstate[session_index] == 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@3399: dsc->wstate[session_index] = 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@2862: return EOVERFLOW; Edouard@2788: } Edouard@2788: } Edouard@2788: Edouard@2862: return 0; Edouard@2788: } Edouard@2788: Edouard@3399: static int read_iterator(hmi_tree_item_t *dsc) Edouard@2788: { Edouard@3374: if(dsc->rstate == buf_set) Edouard@3374: { Edouard@3374: void *src_p = &rbuf[dsc->buf_index]; Edouard@3397: void *value_p = NULL; Edouard@3397: size_t sz = 0; Edouard@3397: UnpackVar(dsc, &value_p, NULL, &sz); Edouard@3397: memcpy(value_p, src_p, sz); Edouard@3374: dsc->rstate = buf_free; Edouard@3374: } Edouard@2788: return 0; Edouard@2788: } Edouard@2788: Edouard@3270: void update_refresh_period(hmi_tree_item_t *dsc, uint32_t session_index, uint16_t refresh_period_ms) Edouard@2768: { Edouard@3399: uint32_t other_session_index = 0; Edouard@3399: int previously_subscribed = 0; Edouard@3399: int session_only_subscriber = 0; edouard@3400: int session_already_subscriber = 0; Edouard@3399: int needs_subscription_for_session = (refresh_period_ms != 0); Edouard@3399: Edouard@3399: while(other_session_index < session_index) { Edouard@3399: previously_subscribed |= (dsc->refresh_period_ms[other_session_index++] != 0); Edouard@3399: } edouard@3400: session_already_subscriber = (dsc->refresh_period_ms[other_session_index++] != 0); Edouard@3399: while(other_session_index < MAX_CONNECTIONS) { Edouard@3399: previously_subscribed |= (dsc->refresh_period_ms[other_session_index++] != 0); Edouard@3399: } edouard@3400: session_only_subscriber = session_already_subscriber && !previously_subscribed; edouard@3400: previously_subscribed |= session_already_subscriber; Edouard@3399: Edouard@3399: if(needs_subscription_for_session) { edouard@3400: if(!session_already_subscriber) Edouard@3270: { Edouard@3270: dsc->wstate[session_index] = buf_new; Edouard@2863: } edouard@3400: /* item is appended to list only when no session was previously subscribed */ Edouard@3399: if(!previously_subscribed){ Edouard@3399: /* append subsciption to list */ edouard@3400: if(subscriptions_tail != NULL){ edouard@3400: /* if list wasn't empty, link with previous tail*/ edouard@3400: subscriptions_tail->subscriptions_next = dsc; edouard@3400: } Edouard@3399: dsc->subscriptions_prev = subscriptions_tail; Edouard@3399: subscriptions_tail = dsc; Edouard@3399: dsc->subscriptions_next = NULL; Edouard@3399: } Edouard@2805: } else { Edouard@3270: dsc->wstate[session_index] = buf_free; Edouard@3399: /* item is removed from list only when session was the only one remaining */ Edouard@3399: if (session_only_subscriber) { Edouard@3399: if(dsc->subscriptions_next == NULL){ /* remove tail */ Edouard@3399: /* re-link tail to previous */ Edouard@3399: subscriptions_tail = dsc->subscriptions_prev; edouard@3400: if(subscriptions_tail != NULL){ edouard@3400: subscriptions_tail->subscriptions_next = NULL; edouard@3400: } edouard@3400: } else if(dsc->subscriptions_prev == NULL){ /* remove head */ edouard@3400: dsc->subscriptions_next->subscriptions_prev = NULL; Edouard@3399: } else { /* remove entry in between other entries */ edouard@3400: /* re-link previous and next node */ Edouard@3399: dsc->subscriptions_next->subscriptions_prev = dsc->subscriptions_prev; Edouard@3399: dsc->subscriptions_prev->subscriptions_next = dsc->subscriptions_next; Edouard@3399: } Edouard@3399: /* unnecessary Edouard@3399: dsc->subscriptions_next = NULL; Edouard@3399: dsc->subscriptions_prev = NULL; Edouard@3399: */ Edouard@3399: } Edouard@3270: } Edouard@3270: dsc->refresh_period_ms[session_index] = refresh_period_ms; Edouard@2766: } Edouard@2766: edouard@3294: static void *svghmi_handle; edouard@3294: edouard@3294: void SVGHMI_SuspendFromPythonThread(void) edouard@3294: { edouard@3294: wait_RT_to_nRT_signal(svghmi_handle); edouard@3294: } edouard@3294: edouard@3294: void SVGHMI_WakeupFromRTThread(void) edouard@3294: { edouard@3294: unblock_RT_to_nRT_signal(svghmi_handle); edouard@3294: } Edouard@2775: Edouard@3281: int svghmi_continue_collect; Edouard@2776: Edouard@2764: int __init_svghmi() Edouard@2764: { edouard@3295: memset(rbuf,0,sizeof(rbuf)); edouard@3295: memset(wbuf,0,sizeof(wbuf)); Edouard@2819: Edouard@3281: svghmi_continue_collect = 1; Edouard@2765: edouard@3294: /* create svghmi_pipe */ edouard@3294: svghmi_handle = create_RT_to_nRT_signal("SVGHMI_pipe"); edouard@3294: Edouard@3399: if(!svghmi_handle) edouard@3294: return 1; edouard@3294: Edouard@2764: return 0; Edouard@2750: } Edouard@2764: Edouard@2764: void __cleanup_svghmi() Edouard@2764: { Edouard@3281: svghmi_continue_collect = 0; Edouard@2820: SVGHMI_WakeupFromRTThread(); edouard@3294: delete_RT_to_nRT_signal(svghmi_handle); Edouard@2764: } Edouard@2764: Edouard@2764: void __retrieve_svghmi() Edouard@2764: { Edouard@3374: if(AtomicCompareExchange(&hmitree_rlock, 0, 1) == 0) { Edouard@3399: hmi_tree_item_t *dsc = incoming_tail; Edouard@3399: /* iterate through read list (changes from HMI) */ Edouard@3399: while(dsc){ edouard@3400: hmi_tree_item_t *_dsc = dsc->incoming_prev; Edouard@3399: read_iterator(dsc); Edouard@3399: /* unnecessary Edouard@3399: dsc->incoming_prev = NULL; Edouard@3399: */ edouard@3400: dsc = _dsc; Edouard@3399: } Edouard@3399: /* flush read list */ Edouard@3399: incoming_tail = NULL; Edouard@3374: AtomicCompareExchange(&hmitree_rlock, 1, 0); Edouard@3374: } Edouard@2764: } Edouard@2764: Edouard@2764: void __publish_svghmi() Edouard@2764: { Edouard@2768: global_write_dirty = 0; Edouard@3399: Edouard@3374: if(AtomicCompareExchange(&hmitree_wlock, 0, 1) == 0) { Edouard@3399: hmi_tree_item_t *dsc = subscriptions_tail; Edouard@3399: while(dsc){ Edouard@3399: write_iterator(dsc); Edouard@3399: dsc = dsc->subscriptions_prev; Edouard@3399: } Edouard@3374: AtomicCompareExchange(&hmitree_wlock, 1, 0); Edouard@3374: } Edouard@3399: Edouard@2768: if(global_write_dirty) { Edouard@2820: SVGHMI_WakeupFromRTThread(); Edouard@2775: } Edouard@2775: } Edouard@2775: Edouard@2775: /* PYTHON CALLS */ Edouard@3271: int svghmi_wait(void){ Edouard@3271: Edouard@3271: SVGHMI_SuspendFromPythonThread(); Edouard@3271: } Edouard@3271: Edouard@3270: int svghmi_send_collect(uint32_t session_index, uint32_t *size, char **ptr){ Edouard@2775: edouard@3400: Edouard@3281: if(svghmi_continue_collect) { Edouard@2788: int res; Edouard@2788: sbufidx = HMI_HASH_SIZE; Edouard@3374: Edouard@3374: while(AtomicCompareExchange(&hmitree_wlock, 0, 1)){ Edouard@3374: nRT_reschedule(); Edouard@3374: } Edouard@3374: Edouard@3399: hmi_tree_item_t *dsc = subscriptions_tail; Edouard@3399: while(dsc){ Edouard@3399: uint32_t index = dsc - hmi_tree_items; Edouard@3399: res = send_iterator(index, dsc, session_index); Edouard@3399: if(res != 0){ Edouard@3399: break; Edouard@3399: } Edouard@3399: dsc = dsc->subscriptions_prev; Edouard@3399: } Edouard@3399: if(res == 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@3374: AtomicCompareExchange(&hmitree_wlock, 1, 0); Edouard@2799: return 0; Edouard@2799: } Edouard@3374: AtomicCompareExchange(&hmitree_wlock, 1, 0); Edouard@2799: return ENODATA; Edouard@2799: } Edouard@3374: AtomicCompareExchange(&hmitree_wlock, 1, 0); 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@3374: unset = -1, Edouard@2788: setval = 0, Edouard@2788: reset = 1, Edouard@2798: subscribe = 2 Edouard@2788: } cmd_from_JS; Edouard@2788: Edouard@3271: int svghmi_reset(uint32_t session_index){ Edouard@3399: hmi_tree_item_t *dsc = subscriptions_tail; edouard@3400: while(AtomicCompareExchange(&hmitree_wlock, 0, 1)){ edouard@3400: nRT_reschedule(); edouard@3400: } Edouard@3399: while(dsc){ edouard@3400: hmi_tree_item_t *_dsc = dsc->subscriptions_prev; Edouard@3399: update_refresh_period(dsc, session_index, 0); edouard@3400: dsc = _dsc; edouard@3400: } edouard@3400: AtomicCompareExchange(&hmitree_wlock, 1, 0); Edouard@3271: return 1; Edouard@3271: } Edouard@3271: Edouard@2822: // Returns : Edouard@2822: // 0 is OK, <0 is error, 1 is heartbeat Edouard@3270: int svghmi_recv_dispatch(uint32_t session_index, 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@3374: int ret; Edouard@3374: int got_wlock = 0; Edouard@3374: int got_rlock = 0; Edouard@3374: cmd_from_JS cmd_old = unset; Edouard@3374: cmd_from_JS cmd = unset; Edouard@3374: Edouard@2788: while(cursor < end) Edouard@2788: { Edouard@2788: uint32_t progress; Edouard@3374: Edouard@3374: cmd_old = cmd; Edouard@3374: cmd = *(cursor++); Edouard@3374: edouard@3400: Edouard@3374: if(cmd_old != cmd){ Edouard@3374: if(got_wlock){ Edouard@3374: AtomicCompareExchange(&hmitree_wlock, 1, 0); Edouard@3374: got_wlock = 0; Edouard@3374: } Edouard@3374: if(got_rlock){ Edouard@3374: AtomicCompareExchange(&hmitree_rlock, 1, 0); Edouard@3374: got_rlock = 0; Edouard@3374: } Edouard@3374: } 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@2862: edouard@3400: Edouard@2822: if(index == heartbeat_index) Edouard@2822: was_hearbeat = 1; Edouard@2788: Edouard@2788: if(index < HMI_ITEM_COUNT) Edouard@2788: { Edouard@3399: hmi_tree_item_t *dsc = &hmi_tree_items[index]; Edouard@3397: size_t sz = 0; Edouard@2788: void *dst_p = &rbuf[dsc->buf_index]; Edouard@2829: Edouard@2829: if(__Is_a_string(dsc)){ Edouard@2829: sz = ((STRING*)valptr)->len + 1; Edouard@3399: } else { Edouard@3399: UnpackVar(dsc, NULL, NULL, &sz); Edouard@2829: } Edouard@2788: Edouard@2802: if((valptr + sz) <= end) Edouard@2788: { Edouard@2788: // rescheduling spinlock until free Edouard@3374: if(!got_rlock){ Edouard@3374: while(AtomicCompareExchange(&hmitree_rlock, 0, 1)){ Edouard@3374: nRT_reschedule(); Edouard@3374: } Edouard@3374: got_rlock=1; Edouard@3374: } Edouard@2788: Edouard@2788: memcpy(dst_p, valptr, sz); Edouard@3399: Edouard@3399: /* check that rstate is not already buf_set */ Edouard@3399: if(dsc->rstate != buf_set){ Edouard@3399: dsc->rstate = buf_set; Edouard@3399: /* append entry to read list (changes from HMI) */ Edouard@3399: dsc->incoming_prev = incoming_tail; Edouard@3399: incoming_tail = dsc; Edouard@3399: } Edouard@2788: Edouard@2788: progress = sz + sizeof(uint32_t) /* index */; Edouard@2788: } Edouard@2862: else Edouard@2799: { Edouard@3374: ret = -EINVAL; Edouard@3374: goto exit_free; Edouard@2799: } Edouard@2799: } Edouard@2862: else Edouard@2799: { Edouard@3374: ret = -EINVAL; Edouard@3374: goto exit_free; Edouard@2799: } Edouard@2788: } Edouard@2788: break; Edouard@2788: Edouard@2788: case reset: Edouard@2788: { Edouard@2788: progress = 0; Edouard@3374: if(!got_wlock){ Edouard@3374: while(AtomicCompareExchange(&hmitree_wlock, 0, 1)){ Edouard@3374: nRT_reschedule(); Edouard@3374: } Edouard@3374: got_wlock = 1; Edouard@3374: } Edouard@3399: { Edouard@3399: hmi_tree_item_t *dsc = subscriptions_tail; Edouard@3399: while(dsc){ edouard@3400: hmi_tree_item_t *_dsc = dsc->subscriptions_prev; Edouard@3399: update_refresh_period(dsc, session_index, 0); edouard@3400: dsc = _dsc; Edouard@3399: } Edouard@3399: } 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@3374: if(!got_wlock){ Edouard@3374: while(AtomicCompareExchange(&hmitree_wlock, 0, 1)){ Edouard@3374: nRT_reschedule(); Edouard@3374: } Edouard@3374: got_wlock = 1; Edouard@3374: } Edouard@3399: hmi_tree_item_t *dsc = &hmi_tree_items[index]; Edouard@3270: update_refresh_period(dsc, session_index, refresh_period_ms); Edouard@2799: } Edouard@2862: else Edouard@2799: { Edouard@3374: ret = -EINVAL; Edouard@3374: goto exit_free; Edouard@2799: } Edouard@2788: Edouard@2862: 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@3374: ret = was_hearbeat; Edouard@3374: Edouard@3374: exit_free: Edouard@3374: if(got_wlock){ Edouard@3374: AtomicCompareExchange(&hmitree_wlock, 1, 0); Edouard@3374: got_wlock = 0; Edouard@3374: } Edouard@3374: if(got_rlock){ Edouard@3374: AtomicCompareExchange(&hmitree_rlock, 1, 0); Edouard@3374: got_rlock = 0; Edouard@3374: } Edouard@3374: return ret; Edouard@3374: } Edouard@3374: