#include "iec_types_all.h"
#define DEFAULT_REFRESH_PERIOD_MS 100
#define HMI_BUFFER_SIZE %(buffer_size)d
#define HMI_ITEM_COUNT %(item_count)d
static uint8_t hmi_hash[HMI_HASH_SIZE] = {%(hmi_hash_ints)s};
/* PLC reads from that buffer */
static char rbuf[HMI_BUFFER_SIZE];
/* PLC writes to that buffer */
static char wbuf[HMI_BUFFER_SIZE];
/* TODO change that in case of multiclient... */
/* worst biggest send buffer. FIXME : use dynamic alloc ? */
static char sbuf[HMI_HASH_SIZE + HMI_BUFFER_SIZE + (HMI_ITEM_COUNT * sizeof(uint32_t))];
static unsigned int sbufidx;
%(extern_variables_declarations)s
#define ticktime_ns %(PLC_ticktime)d
static uint16_t ticktime_ms = (ticktime_ns>1000000)?
static int global_write_dirty = 0;
/* zero means not subscribed */
uint16_t refresh_period_ms;
static hmi_tree_item_t hmi_tree_item[] = {
typedef int(*hmi_tree_iterator)(uint32_t, hmi_tree_item_t*);
static int traverse_hmi_tree(hmi_tree_iterator fp)
for(i = 0; i < sizeof(hmi_tree_item)/sizeof(hmi_tree_item_t); i++){
hmi_tree_item_t *dsc = &hmi_tree_item[i];
#define __Unpack_desc_type hmi_tree_item_t
static int write_iterator(uint32_t index, hmi_tree_item_t *dsc)
if(AtomicCompareExchange(&dsc->wlock, 0, 1) == 0)
if(dsc->wstate == buf_set){
/* if being subscribed */
if(dsc->refresh_period_ms){
if(dsc->age_ms + ticktime_ms < dsc->refresh_period_ms){
dsc->age_ms += ticktime_ms;
dsc->wstate = buf_tosend;
void *dest_p = &wbuf[dsc->buf_index];
void *real_value_p = NULL;
void *visible_value_p = UnpackVar(dsc, &real_value_p, &flags);
/* if new value differs from previous one */
USINT sz = __get_type_enum_size(dsc->type);
if(dsc->wstate == buf_new || memcmp(dest_p, visible_value_p, sz) != 0){
/* copy and flag as set */
memcpy(dest_p, visible_value_p, sz);
if(dsc->wstate == buf_new || dsc->wstate == buf_free) {
if(dsc->wstate == buf_new || ticktime_ms > dsc->refresh_period_ms){
dsc->wstate = buf_tosend;
AtomicCompareExchange(&dsc->wlock, 1, 0);
// else ... : PLC can't wait, variable will be updated next turn
static int send_iterator(uint32_t index, hmi_tree_item_t *dsc)
while(AtomicCompareExchange(&dsc->wlock, 0, 1)) sched_yield();
if(dsc->wstate == buf_tosend)
uint32_t sz = __get_type_enum_size(dsc->type);
if(sbufidx + sizeof(uint32_t) + sz <= sizeof(sbuf))
void *src_p = &wbuf[dsc->buf_index];
void *dst_p = &sbuf[sbufidx];
/* TODO : force into little endian */
memcpy(dst_p, &index, sizeof(uint32_t));
memcpy(dst_p + sizeof(uint32_t), src_p, sz);
sbufidx += sizeof(uint32_t) /* index */ + sz;
printf("BUG!!! %%d + %%ld + %%d > %%ld \n", sbufidx, sizeof(uint32_t), sz, sizeof(sbuf));
AtomicCompareExchange(&dsc->wlock, 1, 0);
AtomicCompareExchange(&dsc->wlock, 1, 0);
static int read_iterator(uint32_t index, hmi_tree_item_t *dsc)
if(AtomicCompareExchange(&dsc->rlock, 0, 1) == 0)
if(dsc->rstate == buf_set)
void *src_p = &rbuf[dsc->buf_index];
void *real_value_p = NULL;
void *visible_value_p = UnpackVar(dsc, &real_value_p, &flags);
memcpy(real_value_p, src_p, __get_type_enum_size(dsc->type));
AtomicCompareExchange(&dsc->rlock, 1, 0);
// else ... : PLC can't wait, variable will be updated next turn
void update_refresh_period(hmi_tree_item_t *dsc, uint16_t refresh_period_ms)
while(AtomicCompareExchange(&dsc->wlock, 0, 1)) sched_yield();
dsc->refresh_period_ms = refresh_period_ms;
/* TODO : maybe only if was null before for optimization */
AtomicCompareExchange(&dsc->wlock, 1, 0);
static int reset_iterator(uint32_t index, hmi_tree_item_t *dsc)
update_refresh_period(dsc, 0);
static pthread_cond_t svghmi_send_WakeCond = PTHREAD_COND_INITIALIZER;
static pthread_mutex_t svghmi_send_WakeCondLock = PTHREAD_MUTEX_INITIALIZER;
static int continue_collect;
bzero(rbuf,sizeof(rbuf));
bzero(wbuf,sizeof(wbuf));
pthread_mutex_lock(&svghmi_send_WakeCondLock);
pthread_cond_signal(&svghmi_send_WakeCond);
pthread_mutex_unlock(&svghmi_send_WakeCondLock);
traverse_hmi_tree(read_iterator);
traverse_hmi_tree(write_iterator);
pthread_cond_signal(&svghmi_send_WakeCond);
int svghmi_send_collect(uint32_t *size, char **ptr){
pthread_mutex_lock(&svghmi_send_WakeCondLock);
do_collect = continue_collect;
pthread_cond_wait(&svghmi_send_WakeCond, &svghmi_send_WakeCondLock);
do_collect = continue_collect;
pthread_mutex_unlock(&svghmi_send_WakeCondLock);
if((res = traverse_hmi_tree(send_iterator)) == 0)
if(sbufidx > HMI_HASH_SIZE){
memcpy(&sbuf[0], &hmi_hash[0], HMI_HASH_SIZE);
// printf("collected BAD result %%d\n", res);
int svghmi_recv_dispatch(uint32_t size, const uint8_t *ptr){
const uint8_t* cursor = ptr + HMI_HASH_SIZE;
const uint8_t* end = ptr + size;
/* match hmitree fingerprint */
if(size <= HMI_HASH_SIZE || memcmp(ptr, hmi_hash, HMI_HASH_SIZE) != 0)
printf("svghmi_recv_dispatch MISMATCH !!\n");
cmd_from_JS cmd = *(cursor++);
uint32_t index = *(uint32_t*)(cursor);
uint8_t const *valptr = cursor + sizeof(uint32_t);
if(index < HMI_ITEM_COUNT)
hmi_tree_item_t *dsc = &hmi_tree_item[index];
void *real_value_p = NULL;
void *visible_value_p = UnpackVar(dsc, &real_value_p, &flags);
void *dst_p = &rbuf[dsc->buf_index];
uint32_t sz = __get_type_enum_size(dsc->type);
// rescheduling spinlock until free
while(AtomicCompareExchange(&dsc->rlock, 0, 1)) sched_yield();
memcpy(dst_p, valptr, sz);
AtomicCompareExchange(&dsc->rlock, 1, 0);
progress = sz + sizeof(uint32_t) /* index */;
traverse_hmi_tree(reset_iterator);
uint32_t index = *(uint32_t*)(cursor);
uint16_t refresh_period_ms = *(uint32_t*)(cursor + sizeof(uint32_t));
if(index < HMI_ITEM_COUNT)
hmi_tree_item_t *dsc = &hmi_tree_item[index];
update_refresh_period(dsc, refresh_period_ms);
progress = sizeof(uint32_t) /* index */ +
sizeof(uint16_t) /* refresh period */;
printf("svghmi_recv_dispatch unknown %%d\n",cmd);