# HG changeset patch # User Edouard Tisserant # Date 1642348676 -3600 # Node ID 3f54a680fe456d956342939e8f676f0315da423a # Parent a65bcbb6af205dcbc5eb4e4241e01b209a6b051b# Parent 526785cdc97ae657a5c002966127e8823428cb37 Merged RuntimeLists branch diff -r a65bcbb6af20 -r 3f54a680fe45 ProjectController.py --- a/ProjectController.py Tue Nov 30 09:52:42 2021 +0100 +++ b/ProjectController.py Sun Jan 16 16:57:56 2022 +0100 @@ -276,6 +276,7 @@ # copy StatusMethods so that it can be later customized self.StatusMethods = [dic.copy() for dic in self.StatusMethods] self.DebugToken = None + self.LastComplainDebugToken = None self.debug_status = PlcStatus.Stopped self.IECcodeDigest = None @@ -971,7 +972,7 @@ # describes CSV columns ProgramsListAttributeName = ["num", "C_path", "type"] VariablesListAttributeName = [ - "num", "vartype", "IEC_path", "C_path", "type", "derived"] + "num", "vartype", "IEC_path", "C_path", "type", "derived", "retain"] self._ProgramList = [] self._VariablesList = [] self._DbgVariablesList = [] @@ -1052,10 +1053,9 @@ # prepare debug code variable_decl_array = [] - bofs = 0 - for v in self._DbgVariablesList: - sz = DebugTypesSize.get(v["type"], 0) - variable_decl_array += [ + retain_indexes = [] + for i, v in enumerate(self._DbgVariablesList): + variable_decl_array.append( "{&(%(C_path)s), " % v + { "EXT": "%(type)s_P_ENUM", @@ -1064,10 +1064,12 @@ "OUT": "%(type)s_O_ENUM", "VAR": "%(type)s_ENUM" }[v["vartype"]] % v + - "}"] - bofs += sz + "}") + + if v["retain"] == "1": + retain_indexes.append("/* "+v["C_path"]+" */ "+str(i)) + debug_code = targets.GetCode("plc_debug.c") % { - "buffer_size": bofs, "programs_declarations": "\n".join(["extern %(type)s %(C_path)s;" % p for p in self._ProgramList]), "extern_variables_declarations": "\n".join([ @@ -1081,6 +1083,7 @@ }[v["vartype"]] % v for v in self._VariablesList if v["C_path"].find('.') < 0]), "variable_decl_array": ",\n".join(variable_decl_array), + "retain_vardsc_index_array": ",\n".join(retain_indexes), "var_access_code": targets.GetCode("var_access.c") } @@ -1555,6 +1558,14 @@ else: values_buffer.append((value, forced)) self.DebugTicks.append(debug_tick) + else: + # complain if trace is incomplete, but only once per debug session + if self.LastComplainDebugToken != self.DebugToken : + self.logger.write_warning( + _("Debug: target couldn't trace all requested variables.\n")) + self.LastComplainDebugToken = self.DebugToken + + buffers, self.DebugValuesBuffers = (self.DebugValuesBuffers, [list() for dummy in xrange(len(self.TracedIECPath))]) @@ -1563,6 +1574,15 @@ return debug_status, ticks, buffers + RegisterDebugVariableErrorCodes = { + # TRACE_LIST_OVERFLOW + 1 : _("Debug: Too many variables traced. Max 1024.\n"), + # FORCE_LIST_OVERFLOW + 2 : _("Debug: Too many variables forced. Max 256.\n"), + # FORCE_BUFFER_OVERFLOW + 3 : _("Debug: Cumulated forced variables size too large. Max 1KB.\n") + } + def RegisterDebugVarToConnector(self): Idxs = [] self.TracedIECPath = [] @@ -1596,7 +1616,14 @@ IdxsT = zip(*Idxs) self.TracedIECPath = IdxsT[3] self.TracedIECTypes = IdxsT[1] - self.DebugToken = self._connector.SetTraceVariablesList(zip(*IdxsT[0:3])) + res = self._connector.SetTraceVariablesList(zip(*IdxsT[0:3])) + if res is not None and res > 0: + self.DebugToken = res + else: + self.DebugToken = None + self.logger.write_warning( + self.RegisterDebugVariableErrorCodes.get( + -res, _("Debug: Unknown error"))) else: self.TracedIECPath = [] self._connector.SetTraceVariablesList([]) diff -r a65bcbb6af20 -r 3f54a680fe45 runtime/PLCObject.py --- a/runtime/PLCObject.py Tue Nov 30 09:52:42 2021 +0100 +++ b/runtime/PLCObject.py Sun Jan 16 16:57:56 2022 +0100 @@ -223,7 +223,7 @@ self._ResetDebugVariables.restype = None self._RegisterDebugVariable = self.PLClibraryHandle.RegisterDebugVariable - self._RegisterDebugVariable.restype = None + self._RegisterDebugVariable.restype = ctypes.c_int self._RegisterDebugVariable.argtypes = [ctypes.c_int, ctypes.c_void_p] self._FreeDebugData = self.PLClibraryHandle.FreeDebugData @@ -294,7 +294,7 @@ self._startPLC = lambda x, y: None self._stopPLC = lambda: None self._ResetDebugVariables = lambda: None - self._RegisterDebugVariable = lambda x, y: None + self._RegisterDebugVariable = lambda x, y: 0 self._IterDebugData = lambda x, y: None self._FreeDebugData = lambda: None self._GetDebugData = lambda: -1 @@ -720,7 +720,11 @@ TypeTranslator.get(iectype, (None, None, None)) force = ctypes.byref(pack_func(c_type, force)) - self._RegisterDebugVariable(idx, force) + res = self._RegisterDebugVariable(idx, force) + if res != 0: + self._resumeDebug() + self._suspendDebug(True) + return -res self._TracesSwap() self._resumeDebug() return self.DebugToken diff -r a65bcbb6af20 -r 3f54a680fe45 runtime/typemapping.py --- a/runtime/typemapping.py Tue Nov 30 09:52:42 2021 +0100 +++ b/runtime/typemapping.py Sun Jan 16 16:57:56 2022 +0100 @@ -85,11 +85,22 @@ for iectype in indexes: c_type, unpack_func, _pack_func = \ TypeTranslator.get(iectype, (None, None, None)) - if c_type is not None and buffoffset < buffsize: - cursor = c_void_p(buffptr + buffoffset) + + cursor = c_void_p(buffptr + buffoffset) + if iectype == "STRING": + # strlen is stored in c_uint8 and sizeof(c_uint8) is 1 + # first check we can read size + if (buffoffset + 1) <= buffsize: + size = 1 + cast(cursor,POINTER(c_type)).contents.len + else: + return None + else: + size = sizeof(c_type) + + if c_type is not None and (buffoffset + size) <= buffsize: value = unpack_func(cast(cursor, POINTER(c_type)).contents) - buffoffset += sizeof(c_type) if iectype != "STRING" else len(value)+1 + buffoffset += size res.append(value) else: return None diff -r a65bcbb6af20 -r 3f54a680fe45 svghmi/svghmi.c --- a/svghmi/svghmi.c Tue Nov 30 09:52:42 2021 +0100 +++ b/svghmi/svghmi.c Sun Jan 16 16:57:56 2022 +0100 @@ -10,6 +10,7 @@ #define HMI_ITEM_COUNT %(item_count)d #define HMI_HASH_SIZE 8 #define MAX_CONNECTIONS %(max_connections)d +#define MAX_CON_INDEX MAX_CONNECTIONS - 1 static uint8_t hmi_hash[HMI_HASH_SIZE] = {%(hmi_hash_ints)s}; @@ -19,7 +20,6 @@ /* 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; @@ -42,11 +42,15 @@ static long hmitree_rlock = 0; static long hmitree_wlock = 0; -typedef struct { +typedef struct hmi_tree_item_s hmi_tree_item_t; +struct hmi_tree_item_s{ void *ptr; __IEC_types_enum type; uint32_t buf_index; + /* retrieve/read/recv */ + buf_state_t rstate; + /* publish/write/send */ buf_state_t wstate[MAX_CONNECTIONS]; @@ -54,111 +58,114 @@ uint16_t refresh_period_ms[MAX_CONNECTIONS]; uint16_t age_ms[MAX_CONNECTIONS]; - /* retrieve/read/recv */ - buf_state_t rstate; - -} hmi_tree_item_t; - -static hmi_tree_item_t hmi_tree_item[] = { + /* dual linked list for subscriptions */ + hmi_tree_item_t *subscriptions_next; + hmi_tree_item_t *subscriptions_prev; + + /* single linked list for changes from HMI */ + hmi_tree_item_t *incoming_prev; + +}; + +#define HMITREE_ITEM_INITIALIZER(cpath,type,buf_index) { \ + &(cpath), /*ptr*/ \ + type, /*type*/ \ + buf_index, /*buf_index*/ \ + buf_free, /*rstate*/ \ + {[0 ... MAX_CON_INDEX] = buf_free}, /*wstate*/ \ + {[0 ... MAX_CON_INDEX] = 0}, /*refresh_period_ms*/ \ + {[0 ... MAX_CON_INDEX] = 0}, /*age_ms*/ \ + NULL, /*subscriptions_next*/\ + NULL, /*subscriptions_prev*/\ + NULL} /*incoming_next*/ + + +/* entry for dual linked list for HMI subscriptions */ +/* points to the end of the list */ +static hmi_tree_item_t *subscriptions_tail = NULL; + +/* entry for single linked list for changes from HMI */ +/* points to the end of the list */ +static hmi_tree_item_t *incoming_tail = NULL; + +static hmi_tree_item_t hmi_tree_items[] = { %(variable_decl_array)s }; -typedef int(*hmi_tree_iterator)(uint32_t, hmi_tree_item_t*); -static int traverse_hmi_tree(hmi_tree_iterator fp) -{ - unsigned int i; - for(i = 0; i < sizeof(hmi_tree_item)/sizeof(hmi_tree_item_t); i++){ - hmi_tree_item_t *dsc = &hmi_tree_item[i]; - int res = (*fp)(i, dsc); - if(res != 0){ - return res; - } - } +#define __Unpack_desc_type hmi_tree_item_t + +%(var_access_code)s + +static int write_iterator(hmi_tree_item_t *dsc) +{ + uint32_t session_index = 0; + int value_changed = 0; + void *dest_p = NULL; + void *value_p = NULL; + size_t sz = 0; + while(session_index < MAX_CONNECTIONS) { + if(dsc->wstate[session_index] == buf_set){ + /* if being subscribed */ + if(dsc->refresh_period_ms[session_index]){ + if(dsc->age_ms[session_index] + ticktime_ms < dsc->refresh_period_ms[session_index]){ + dsc->age_ms[session_index] += ticktime_ms; + }else{ + dsc->wstate[session_index] = buf_tosend; + global_write_dirty = 1; + } + } + } + + /* variable is sample only if just subscribed + or already subscribed and having value change */ + int do_sample = 0; + int just_subscribed = dsc->wstate[session_index] == buf_new; + if(!just_subscribed){ + int already_subscribed = dsc->refresh_period_ms[session_index] > 0; + if(already_subscribed){ + if(!value_changed){ + if(!value_p){ + UnpackVar(dsc, &value_p, NULL, &sz); + if(__Is_a_string(dsc)){ + sz = ((STRING*)value_p)->len + 1; + } + dest_p = &wbuf[dsc->buf_index]; + } + value_changed = memcmp(dest_p, value_p, sz) != 0; + do_sample = value_changed; + }else{ + do_sample = 1; + } + } + } else { + do_sample = 1; + } + + + if(do_sample){ + if(dsc->wstate[session_index] != buf_set && dsc->wstate[session_index] != buf_tosend) { + if(dsc->wstate[session_index] == buf_new \ + || ticktime_ms > dsc->refresh_period_ms[session_index]){ + dsc->wstate[session_index] = buf_tosend; + global_write_dirty = 1; + } else { + dsc->wstate[session_index] = buf_set; + } + dsc->age_ms[session_index] = 0; + } + } + + session_index++; + } + /* copy value if changed (and subscribed) */ + if(value_changed) + memcpy(dest_p, value_p, sz); return 0; } -#define __Unpack_desc_type hmi_tree_item_t - -%(var_access_code)s - -static int write_iterator(uint32_t index, hmi_tree_item_t *dsc) -{ - { - uint32_t session_index = 0; - int value_changed = 0; - void *dest_p = NULL; - void *real_value_p = NULL; - void *visible_value_p = NULL; - USINT sz = 0; - while(session_index < MAX_CONNECTIONS) { - if(dsc->wstate[session_index] == buf_set){ - /* if being subscribed */ - if(dsc->refresh_period_ms[session_index]){ - if(dsc->age_ms[session_index] + ticktime_ms < dsc->refresh_period_ms[session_index]){ - dsc->age_ms[session_index] += ticktime_ms; - }else{ - dsc->wstate[session_index] = buf_tosend; - global_write_dirty = 1; - } - } - } - - /* variable is sample only if just subscribed - or already subscribed and having value change */ - int do_sample = 0; - int just_subscribed = dsc->wstate[session_index] == buf_new; - if(!just_subscribed){ - int already_subscribed = dsc->refresh_period_ms[session_index] > 0; - if(already_subscribed){ - if(!value_changed){ - if(!visible_value_p){ - char flags = 0; - visible_value_p = UnpackVar(dsc, &real_value_p, &flags); - if(__Is_a_string(dsc)){ - sz = ((STRING*)visible_value_p)->len + 1; - } else { - sz = __get_type_enum_size(dsc->type); - } - dest_p = &wbuf[dsc->buf_index]; - } - value_changed = memcmp(dest_p, visible_value_p, sz) != 0; - do_sample = value_changed; - }else{ - do_sample = 1; - } - } - } else { - do_sample = 1; - } - - - if(do_sample){ - if(dsc->wstate[session_index] != buf_set && dsc->wstate[session_index] != buf_tosend) { - if(dsc->wstate[session_index] == buf_new \ - || ticktime_ms > dsc->refresh_period_ms[session_index]){ - dsc->wstate[session_index] = buf_tosend; - global_write_dirty = 1; - } else { - dsc->wstate[session_index] = buf_set; - } - dsc->age_ms[session_index] = 0; - } - } - - session_index++; - } - /* copy value if changed (and subscribed) */ - if(value_changed) - memcpy(dest_p, visible_value_p, sz); - } - // else ... : PLC can't wait, variable will be updated next turn - return 0; -} - -static uint32_t send_session_index; -static int send_iterator(uint32_t index, hmi_tree_item_t *dsc) -{ - if(dsc->wstate[send_session_index] == buf_tosend) +static int send_iterator(uint32_t index, hmi_tree_item_t *dsc, uint32_t session_index) +{ + if(dsc->wstate[session_index] == buf_tosend) { uint32_t sz = __get_type_enum_size(dsc->type); if(sbufidx + sizeof(uint32_t) + sz <= sizeof(sbuf)) @@ -171,7 +178,7 @@ /* TODO : force into little endian */ memcpy(dst_p, &index, sizeof(uint32_t)); memcpy(dst_p + sizeof(uint32_t), src_p, sz); - dsc->wstate[send_session_index] = buf_free; + dsc->wstate[session_index] = buf_free; sbufidx += sizeof(uint32_t) /* index */ + sz; } else @@ -184,15 +191,15 @@ return 0; } -static int read_iterator(uint32_t index, hmi_tree_item_t *dsc) +static int read_iterator(hmi_tree_item_t *dsc) { if(dsc->rstate == buf_set) { void *src_p = &rbuf[dsc->buf_index]; - void *real_value_p = NULL; - char flags = 0; - void *visible_value_p = UnpackVar(dsc, &real_value_p, &flags); - memcpy(real_value_p, src_p, __get_type_enum_size(dsc->type)); + void *value_p = NULL; + size_t sz = 0; + UnpackVar(dsc, &value_p, NULL, &sz); + memcpy(value_p, src_p, sz); dsc->rstate = buf_free; } return 0; @@ -200,24 +207,64 @@ void update_refresh_period(hmi_tree_item_t *dsc, uint32_t session_index, uint16_t refresh_period_ms) { - if(refresh_period_ms) { - if(!dsc->refresh_period_ms[session_index]) + uint32_t other_session_index = 0; + int previously_subscribed = 0; + int session_only_subscriber = 0; + int session_already_subscriber = 0; + int needs_subscription_for_session = (refresh_period_ms != 0); + + while(other_session_index < session_index) { + previously_subscribed |= (dsc->refresh_period_ms[other_session_index++] != 0); + } + session_already_subscriber = (dsc->refresh_period_ms[other_session_index++] != 0); + while(other_session_index < MAX_CONNECTIONS) { + previously_subscribed |= (dsc->refresh_period_ms[other_session_index++] != 0); + } + session_only_subscriber = session_already_subscriber && !previously_subscribed; + previously_subscribed |= session_already_subscriber; + + if(needs_subscription_for_session) { + if(!session_already_subscriber) { dsc->wstate[session_index] = buf_new; } + /* item is appended to list only when no session was previously subscribed */ + if(!previously_subscribed){ + /* append subsciption to list */ + if(subscriptions_tail != NULL){ + /* if list wasn't empty, link with previous tail*/ + subscriptions_tail->subscriptions_next = dsc; + } + dsc->subscriptions_prev = subscriptions_tail; + subscriptions_tail = dsc; + dsc->subscriptions_next = NULL; + } } else { dsc->wstate[session_index] = buf_free; + /* item is removed from list only when session was the only one remaining */ + if (session_only_subscriber) { + if(dsc->subscriptions_next == NULL){ /* remove tail */ + /* re-link tail to previous */ + subscriptions_tail = dsc->subscriptions_prev; + if(subscriptions_tail != NULL){ + subscriptions_tail->subscriptions_next = NULL; + } + } else if(dsc->subscriptions_prev == NULL){ /* remove head */ + dsc->subscriptions_next->subscriptions_prev = NULL; + } else { /* remove entry in between other entries */ + /* re-link previous and next node */ + dsc->subscriptions_next->subscriptions_prev = dsc->subscriptions_prev; + dsc->subscriptions_prev->subscriptions_next = dsc->subscriptions_next; + } + /* unnecessary + dsc->subscriptions_next = NULL; + dsc->subscriptions_prev = NULL; + */ + } } dsc->refresh_period_ms[session_index] = refresh_period_ms; } -static uint32_t reset_session_index; -static int reset_iterator(uint32_t index, hmi_tree_item_t *dsc) -{ - update_refresh_period(dsc, reset_session_index, 0); - return 0; -} - static void *svghmi_handle; void SVGHMI_SuspendFromPythonThread(void) @@ -242,7 +289,7 @@ /* create svghmi_pipe */ svghmi_handle = create_RT_to_nRT_signal("SVGHMI_pipe"); - if(!svghmi_handle) + if(!svghmi_handle) return 1; return 0; @@ -258,7 +305,18 @@ void __retrieve_svghmi() { if(AtomicCompareExchange(&hmitree_rlock, 0, 1) == 0) { - traverse_hmi_tree(read_iterator); + hmi_tree_item_t *dsc = incoming_tail; + /* iterate through read list (changes from HMI) */ + while(dsc){ + hmi_tree_item_t *_dsc = dsc->incoming_prev; + read_iterator(dsc); + /* unnecessary + dsc->incoming_prev = NULL; + */ + dsc = _dsc; + } + /* flush read list */ + incoming_tail = NULL; AtomicCompareExchange(&hmitree_rlock, 1, 0); } } @@ -266,10 +324,16 @@ void __publish_svghmi() { global_write_dirty = 0; + if(AtomicCompareExchange(&hmitree_wlock, 0, 1) == 0) { - traverse_hmi_tree(write_iterator); + hmi_tree_item_t *dsc = subscriptions_tail; + while(dsc){ + write_iterator(dsc); + dsc = dsc->subscriptions_prev; + } AtomicCompareExchange(&hmitree_wlock, 1, 0); } + if(global_write_dirty) { SVGHMI_WakeupFromRTThread(); } @@ -283,16 +347,25 @@ int svghmi_send_collect(uint32_t session_index, uint32_t *size, char **ptr){ + if(svghmi_continue_collect) { int res; sbufidx = HMI_HASH_SIZE; - send_session_index = session_index; while(AtomicCompareExchange(&hmitree_wlock, 0, 1)){ nRT_reschedule(); } - if((res = traverse_hmi_tree(send_iterator)) == 0) + hmi_tree_item_t *dsc = subscriptions_tail; + while(dsc){ + uint32_t index = dsc - hmi_tree_items; + res = send_iterator(index, dsc, session_index); + if(res != 0){ + break; + } + dsc = dsc->subscriptions_prev; + } + if(res == 0) { if(sbufidx > HMI_HASH_SIZE){ memcpy(&sbuf[0], &hmi_hash[0], HMI_HASH_SIZE); @@ -304,7 +377,6 @@ AtomicCompareExchange(&hmitree_wlock, 1, 0); return ENODATA; } - // printf("collected BAD result %%d\n", res); AtomicCompareExchange(&hmitree_wlock, 1, 0); return res; } @@ -322,8 +394,16 @@ } cmd_from_JS; int svghmi_reset(uint32_t session_index){ - reset_session_index = session_index; - traverse_hmi_tree(reset_iterator); + hmi_tree_item_t *dsc = subscriptions_tail; + while(AtomicCompareExchange(&hmitree_wlock, 0, 1)){ + nRT_reschedule(); + } + while(dsc){ + hmi_tree_item_t *_dsc = dsc->subscriptions_prev; + update_refresh_period(dsc, session_index, 0); + dsc = _dsc; + } + AtomicCompareExchange(&hmitree_wlock, 1, 0); return 1; } @@ -355,6 +435,7 @@ cmd_old = cmd; cmd = *(cursor++); + if(cmd_old != cmd){ if(got_wlock){ AtomicCompareExchange(&hmitree_wlock, 1, 0); @@ -372,20 +453,20 @@ uint32_t index = *(uint32_t*)(cursor); uint8_t const *valptr = cursor + sizeof(uint32_t); + if(index == heartbeat_index) was_hearbeat = 1; if(index < HMI_ITEM_COUNT) { - hmi_tree_item_t *dsc = &hmi_tree_item[index]; - void *real_value_p = NULL; - char flags = 0; - void *visible_value_p = UnpackVar(dsc, &real_value_p, &flags); + hmi_tree_item_t *dsc = &hmi_tree_items[index]; + size_t sz = 0; void *dst_p = &rbuf[dsc->buf_index]; - uint32_t sz = __get_type_enum_size(dsc->type); if(__Is_a_string(dsc)){ sz = ((STRING*)valptr)->len + 1; + } else { + UnpackVar(dsc, NULL, NULL, &sz); } if((valptr + sz) <= end) @@ -399,7 +480,14 @@ } memcpy(dst_p, valptr, sz); - dsc->rstate = buf_set; + + /* check that rstate is not already buf_set */ + if(dsc->rstate != buf_set){ + dsc->rstate = buf_set; + /* append entry to read list (changes from HMI) */ + dsc->incoming_prev = incoming_tail; + incoming_tail = dsc; + } progress = sz + sizeof(uint32_t) /* index */; } @@ -420,14 +508,20 @@ case reset: { progress = 0; - reset_session_index = session_index; if(!got_wlock){ while(AtomicCompareExchange(&hmitree_wlock, 0, 1)){ nRT_reschedule(); } got_wlock = 1; } - traverse_hmi_tree(reset_iterator); + { + hmi_tree_item_t *dsc = subscriptions_tail; + while(dsc){ + hmi_tree_item_t *_dsc = dsc->subscriptions_prev; + update_refresh_period(dsc, session_index, 0); + dsc = _dsc; + } + } } break; @@ -444,7 +538,7 @@ } got_wlock = 1; } - hmi_tree_item_t *dsc = &hmi_tree_item[index]; + hmi_tree_item_t *dsc = &hmi_tree_items[index]; update_refresh_period(dsc, session_index, refresh_period_ms); } else diff -r a65bcbb6af20 -r 3f54a680fe45 svghmi/svghmi.py --- a/svghmi/svghmi.py Tue Nov 30 09:52:42 2021 +0100 +++ b/svghmi/svghmi.py Sun Jan 16 16:57:56 2022 +0100 @@ -191,14 +191,14 @@ if hasattr(node, "iectype"): sz = DebugTypesSize.get(node.iectype, 0) variable_decl_array += [ - "{&(" + node.cpath + "), " + node.iectype + { + "HMITREE_ITEM_INITIALIZER(" + node.cpath + ", " + node.iectype + { "EXT": "_P_ENUM", "IN": "_P_ENUM", "MEM": "_O_ENUM", "OUT": "_O_ENUM", "VAR": "_ENUM" }[node.vartype] + ", " + - str(buf_index) + ", 0, }"] + str(buf_index) + ")"] buf_index += sz item_count += 1 if len(node.path) == 1: diff -r a65bcbb6af20 -r 3f54a680fe45 targets/Xenomai/plc_Xenomai_main.c --- a/targets/Xenomai/plc_Xenomai_main.c Tue Nov 30 09:52:42 2021 +0100 +++ b/targets/Xenomai/plc_Xenomai_main.c Sun Jan 16 16:57:56 2022 +0100 @@ -384,6 +384,7 @@ void resumeDebug(void) { + __DEBUG = 1; AtomicCompareExchange( &debug_state, DEBUG_BUSY, DEBUG_FREE); } diff -r a65bcbb6af20 -r 3f54a680fe45 targets/plc_debug.c --- a/targets/plc_debug.c Tue Nov 30 09:52:42 2021 +0100 +++ b/targets/plc_debug.c Sun Jan 16 16:57:56 2022 +0100 @@ -25,22 +25,59 @@ #include #include +typedef unsigned int dbgvardsc_index_t; +typedef unsigned short trace_buf_offset_t; + +#define BUFFER_EMPTY 0 +#define BUFFER_FULL 1 + #ifndef TARGET_ONLINE_DEBUG_DISABLE -#define BUFFER_SIZE %(buffer_size)d + +#define TRACE_BUFFER_SIZE 4096 +#define TRACE_LIST_SIZE 1024 /* Atomically accessed variable for buffer state */ -#define BUFFER_FREE 0 -#define BUFFER_BUSY 1 -static long buffer_state = BUFFER_FREE; - -/* The buffer itself */ -char debug_buffer[BUFFER_SIZE]; - -/* Buffer's cursor*/ -static char* buffer_cursor = debug_buffer; +static long trace_buffer_state = BUFFER_EMPTY; + +typedef struct trace_item_s { + dbgvardsc_index_t dbgvardsc_index; +} trace_item_t; + +trace_item_t trace_list[TRACE_LIST_SIZE]; +char trace_buffer[TRACE_BUFFER_SIZE]; + +/* Trace's cursor*/ +static trace_item_t *trace_list_collect_cursor = trace_list; +static trace_item_t *trace_list_addvar_cursor = trace_list; +static const trace_item_t *trace_list_end = + &trace_list[TRACE_LIST_SIZE-1]; +static char *trace_buffer_cursor = trace_buffer; +static const char *trace_buffer_end = trace_buffer + TRACE_BUFFER_SIZE; + + + +#define FORCE_BUFFER_SIZE 1024 +#define FORCE_LIST_SIZE 256 + +typedef struct force_item_s { + dbgvardsc_index_t dbgvardsc_index; + void *value_pointer_backup; +} force_item_t; + +force_item_t force_list[FORCE_LIST_SIZE]; +char force_buffer[FORCE_BUFFER_SIZE]; + +/* Force's cursor*/ +static force_item_t *force_list_apply_cursor = force_list; +static force_item_t *force_list_addvar_cursor = force_list; +static const force_item_t *force_list_end = + &force_list[FORCE_LIST_SIZE-1]; +static char *force_buffer_cursor = force_buffer; +static const char *force_buffer_end = force_buffer + FORCE_BUFFER_SIZE; + + #endif -static unsigned int retain_offset = 0; /*** * Declare programs **/ @@ -56,10 +93,16 @@ __IEC_types_enum type; } dbgvardsc_t; -static dbgvardsc_t dbgvardsc[] = { +static const dbgvardsc_t dbgvardsc[] = { %(variable_decl_array)s }; +static const dbgvardsc_index_t retain_list[] = { +%(retain_vardsc_index_array)s +}; +static unsigned int retain_list_collect_cursor = 0; +static const unsigned int retain_list_size = sizeof(retain_list)/sizeof(dbgvardsc_index_t); + typedef void(*__for_each_variable_do_fp)(dbgvardsc_t*); void __for_each_variable_do(__for_each_variable_do_fp fp) { @@ -77,23 +120,6 @@ void Remind(unsigned int offset, unsigned int count, void * p); -void RemindIterator(dbgvardsc_t *dsc) -{ - void *real_value_p = NULL; - char flags = 0; - UnpackVar(dsc, &real_value_p, &flags); - - if(flags & __IEC_RETAIN_FLAG){ - USINT size = __get_type_enum_size(dsc->type); - /* compute next cursor positon*/ - unsigned int next_retain_offset = retain_offset + size; - /* if buffer not full */ - Remind(retain_offset, size, real_value_p); - /* increment cursor according size*/ - retain_offset = next_retain_offset; - } -} - extern int CheckRetainBuffer(void); extern void InitRetain(void); @@ -101,20 +127,46 @@ { /* init local static vars */ #ifndef TARGET_ONLINE_DEBUG_DISABLE - buffer_cursor = debug_buffer; - buffer_state = BUFFER_FREE; + trace_buffer_cursor = trace_buffer; + trace_list_addvar_cursor = trace_list; + trace_list_collect_cursor = trace_list; + trace_buffer_state = BUFFER_EMPTY; + + force_buffer_cursor = force_buffer; + force_list_addvar_cursor = force_list; + force_list_apply_cursor = force_list; #endif - retain_offset = 0; InitRetain(); /* Iterate over all variables to fill debug buffer */ if(CheckRetainBuffer()){ - __for_each_variable_do(RemindIterator); + static unsigned int retain_offset = 0; + retain_list_collect_cursor = 0; + + /* iterate over retain list */ + while(retain_list_collect_cursor < retain_list_size){ + void *value_p = NULL; + size_t size; + char* next_cursor; + + dbgvardsc_t *dsc = &dbgvardsc[ + retain_list[retain_list_collect_cursor]]; + + UnpackVar(dsc, &value_p, NULL, &size); + + printf("Reminding %%d %%ld \n", retain_list_collect_cursor, size); + + /* if buffer not full */ + Remind(retain_offset, size, value_p); + /* increment cursor according size*/ + retain_offset += size; + + retain_list_collect_cursor++; + } }else{ char mstr[] = "RETAIN memory invalid - defaults used"; LogMessage(LOG_WARNING, mstr, sizeof(mstr)); } - retain_offset = 0; } extern void InitiateDebugTransfer(void); @@ -125,7 +177,7 @@ void __cleanup_debug(void) { #ifndef TARGET_ONLINE_DEBUG_DISABLE - buffer_cursor = debug_buffer; + trace_buffer_cursor = trace_buffer; InitiateDebugTransfer(); #endif @@ -136,84 +188,31 @@ { } - void Retain(unsigned int offset, unsigned int count, void * p); -static inline void BufferIterator(dbgvardsc_t *dsc, int do_debug) -{ - void *real_value_p = NULL; - void *visible_value_p = NULL; - char flags = 0; - - visible_value_p = UnpackVar(dsc, &real_value_p, &flags); - - if(flags & ( __IEC_DEBUG_FLAG | __IEC_RETAIN_FLAG)){ - USINT size = __get_type_enum_size(dsc->type); - -#ifndef TARGET_ONLINE_DEBUG_DISABLE - if(flags & __IEC_DEBUG_FLAG){ - /* copy visible variable to buffer */; - if(do_debug){ - /* compute next cursor positon. - No need to check overflow, as BUFFER_SIZE - is computed large enough */ - if(__Is_a_string(dsc)){ - /* optimization for strings */ - size = ((STRING*)visible_value_p)->len + 1; - } - char* next_cursor = buffer_cursor + size; - /* copy data to the buffer */ - memcpy(buffer_cursor, visible_value_p, size); - /* increment cursor according size*/ - buffer_cursor = next_cursor; - } - /* re-force real value of outputs (M and Q)*/ - if((flags & __IEC_FORCE_FLAG) && (flags & __IEC_OUTPUT_FLAG)){ - memcpy(real_value_p, visible_value_p, size); - } - } -#endif - - if(flags & __IEC_RETAIN_FLAG){ - /* compute next cursor positon*/ - unsigned int next_retain_offset = retain_offset + size; - /* if buffer not full */ - Retain(retain_offset, size, real_value_p); - /* increment cursor according size*/ - retain_offset = next_retain_offset; - } - } -} - -void DebugIterator(dbgvardsc_t *dsc){ - BufferIterator(dsc, 1); -} - -void RetainIterator(dbgvardsc_t *dsc){ - BufferIterator(dsc, 0); -} - - -unsigned int retain_size = 0; - -/* GetRetainSizeIterator */ -void GetRetainSizeIterator(dbgvardsc_t *dsc) -{ - void *real_value_p = NULL; - char flags = 0; - UnpackVar(dsc, &real_value_p, &flags); - - if(flags & __IEC_RETAIN_FLAG){ - USINT size = __get_type_enum_size(dsc->type); - /* Calc retain buffer size */ - retain_size += size; - } -} - /* Return size of all retain variables */ unsigned int GetRetainSize(void) { - __for_each_variable_do(GetRetainSizeIterator); + unsigned int retain_size = 0; + retain_list_collect_cursor = 0; + + /* iterate over retain list */ + while(retain_list_collect_cursor < retain_list_size){ + void *value_p = NULL; + size_t size; + char* next_cursor; + + dbgvardsc_t *dsc = &dbgvardsc[ + retain_list[retain_list_collect_cursor]]; + + UnpackVar(dsc, &value_p, NULL, &size); + + retain_size += size; + retain_list_collect_cursor++; + } + + printf("Retain size %%d \n", retain_size); + return retain_size; } @@ -226,9 +225,26 @@ extern void ValidateRetainBuffer(void); extern void InValidateRetainBuffer(void); +#define __ReForceOutput_case_p(TYPENAME) \ + case TYPENAME##_P_ENUM : \ + case TYPENAME##_O_ENUM : \ + { \ + char *next_cursor = force_buffer_cursor + sizeof(TYPENAME); \ + if(next_cursor <= force_buffer_end ){ \ + /* outputs real value must be systematically forced */ \ + if(vartype == TYPENAME##_O_ENUM) \ + /* overwrite value pointed by backup */ \ + *((TYPENAME *)force_list_apply_cursor->value_pointer_backup) = \ + *((TYPENAME *)force_buffer_cursor); \ + /* inc force_buffer cursor */ \ + force_buffer_cursor = next_cursor; \ + }else{ \ + stop = 1; \ + } \ + } \ + break; void __publish_debug(void) { - retain_offset = 0; InValidateRetainBuffer(); #ifndef TARGET_ONLINE_DEBUG_DISABLE @@ -236,116 +252,248 @@ if(TryEnterDebugSection()){ /* Lock buffer */ long latest_state = AtomicCompareExchange( - &buffer_state, - BUFFER_FREE, - BUFFER_BUSY); + &trace_buffer_state, + BUFFER_EMPTY, + BUFFER_FULL); /* If buffer was free */ - if(latest_state == BUFFER_FREE) + if(latest_state == BUFFER_EMPTY) { + int stop = 0; + /* Reset force list cursor */ + force_list_apply_cursor = force_list; + + /* iterate over force list */ + while(!stop && force_list_apply_cursor < force_list_addvar_cursor){ + dbgvardsc_t *dsc = &dbgvardsc[ + force_list_apply_cursor->dbgvardsc_index]; + void *varp = dsc->ptr; + __IEC_types_enum vartype = dsc->type; + switch(vartype){ + __ANY(__ReForceOutput_case_p) + default: + break; + } + force_list_apply_cursor++; \ + } + /* Reset buffer cursor */ - buffer_cursor = debug_buffer; - /* Iterate over all variables to fill debug buffer */ - __for_each_variable_do(DebugIterator); + trace_buffer_cursor = trace_buffer; + /* Reset trace list cursor */ + trace_list_collect_cursor = trace_list; + + /* iterate over trace list */ + while(trace_list_collect_cursor < trace_list_addvar_cursor){ + void *value_p = NULL; + size_t size; + char* next_cursor; + + dbgvardsc_t *dsc = &dbgvardsc[ + trace_list_collect_cursor->dbgvardsc_index]; + + UnpackVar(dsc, &value_p, NULL, &size); + + /* copy visible variable to buffer */; + if(__Is_a_string(dsc)){ + /* optimization for strings */ + /* assume NULL terminated strings */ + size = ((STRING*)value_p)->len + 1; + } + + /* compute next cursor positon.*/ + next_cursor = trace_buffer_cursor + size; + /* check for buffer overflow */ + if(next_cursor < trace_buffer_end) + /* copy data to the buffer */ + memcpy(trace_buffer_cursor, value_p, size); + else + /* stop looping in case of overflow */ + break; + /* increment cursor according size*/ + trace_buffer_cursor = next_cursor; + trace_list_collect_cursor++; + } /* Leave debug section, * Trigger asynchronous transmission * (returns immediately) */ InitiateDebugTransfer(); /* size */ - }else{ - /* when not debugging, do only retain */ - __for_each_variable_do(RetainIterator); } LeaveDebugSection(); - }else + } #endif - { - /* when not debugging, do only retain */ - __for_each_variable_do(RetainIterator); + static unsigned int retain_offset = 0; + /* when not debugging, do only retain */ + retain_list_collect_cursor = 0; + + /* iterate over retain list */ + while(retain_list_collect_cursor < retain_list_size){ + void *value_p = NULL; + size_t size; + char* next_cursor; + + dbgvardsc_t *dsc = &dbgvardsc[ + retain_list[retain_list_collect_cursor]]; + + UnpackVar(dsc, &value_p, NULL, &size); + + printf("Retaining %%d %%ld \n", retain_list_collect_cursor, size); + + /* if buffer not full */ + Retain(retain_offset, size, value_p); + /* increment cursor according size*/ + retain_offset += size; + + retain_list_collect_cursor++; } ValidateRetainBuffer(); } #ifndef TARGET_ONLINE_DEBUG_DISABLE -#define __RegisterDebugVariable_case_t(TYPENAME) \ - case TYPENAME##_ENUM :\ - ((__IEC_##TYPENAME##_t *)varp)->flags |= flags;\ - if(force)\ - ((__IEC_##TYPENAME##_t *)varp)->value = *((TYPENAME *)force);\ + +#define TRACE_LIST_OVERFLOW 1 +#define FORCE_LIST_OVERFLOW 2 +#define FORCE_BUFFER_OVERFLOW 3 + +#define __ForceVariable_case_t(TYPENAME) \ + case TYPENAME##_ENUM : \ + /* add to force_list*/ \ + force_list_addvar_cursor->dbgvardsc_index = idx; \ + ((__IEC_##TYPENAME##_t *)varp)->flags |= __IEC_FORCE_FLAG; \ + ((__IEC_##TYPENAME##_t *)varp)->value = *((TYPENAME *)force); \ break; -#define __RegisterDebugVariable_case_p(TYPENAME)\ - case TYPENAME##_P_ENUM :\ - ((__IEC_##TYPENAME##_p *)varp)->flags |= flags;\ - if(force)\ - ((__IEC_##TYPENAME##_p *)varp)->fvalue = *((TYPENAME *)force);\ - break;\ - case TYPENAME##_O_ENUM :\ - ((__IEC_##TYPENAME##_p *)varp)->flags |= flags;\ - if(force){\ - ((__IEC_##TYPENAME##_p *)varp)->fvalue = *((TYPENAME *)force);\ - *(((__IEC_##TYPENAME##_p *)varp)->value) = *((TYPENAME *)force);\ - }\ +#define __ForceVariable_case_p(TYPENAME) \ + case TYPENAME##_P_ENUM : \ + case TYPENAME##_O_ENUM : \ + { \ + char *next_cursor = force_buffer_cursor + sizeof(TYPENAME); \ + if(next_cursor <= force_buffer_end ){ \ + /* add to force_list*/ \ + force_list_addvar_cursor->dbgvardsc_index = idx; \ + /* save pointer to backup */ \ + force_list_addvar_cursor->value_pointer_backup = \ + ((__IEC_##TYPENAME##_p *)varp)->value; \ + /* store forced value in force_buffer */ \ + *((TYPENAME *)force_buffer_cursor) = *((TYPENAME *)force); \ + /* replace pointer with pointer to force_buffer */ \ + ((__IEC_##TYPENAME##_p *)varp)->value = \ + (TYPENAME *)force_buffer_cursor; \ + /* mark variable as forced */ \ + ((__IEC_##TYPENAME##_p *)varp)->flags |= __IEC_FORCE_FLAG; \ + /* inc force_buffer cursor */ \ + force_buffer_cursor = next_cursor; \ + /* outputs real value must be systematically forced */ \ + if(vartype == TYPENAME##_O_ENUM) \ + *(((__IEC_##TYPENAME##_p *)varp)->value) = *((TYPENAME *)force);\ + } else { \ + error_code = FORCE_BUFFER_OVERFLOW; \ + goto error_cleanup; \ + } \ + } \ break; -void RegisterDebugVariable(unsigned int idx, void* force) -{ - if(idx < sizeof(dbgvardsc)/sizeof(dbgvardsc_t)){ - unsigned char flags = force ? - __IEC_DEBUG_FLAG | __IEC_FORCE_FLAG : - __IEC_DEBUG_FLAG; - dbgvardsc_t *dsc = &dbgvardsc[idx]; + + +void ResetDebugVariables(void); + +int RegisterDebugVariable(dbgvardsc_index_t idx, void* force) +{ + int error_code = 0; + if(idx < sizeof(dbgvardsc)/sizeof(dbgvardsc_t)){ + /* add to trace_list, inc trace_list_addvar_cursor*/ + if(trace_list_addvar_cursor <= trace_list_end){ + trace_list_addvar_cursor->dbgvardsc_index = idx; + trace_list_addvar_cursor++; + } else { + error_code = TRACE_LIST_OVERFLOW; + goto error_cleanup; + } + if(force){ + if(force_list_addvar_cursor <= force_list_end){ + dbgvardsc_t *dsc = &dbgvardsc[idx]; + void *varp = dsc->ptr; + __IEC_types_enum vartype = dsc->type; + + switch(vartype){ + __ANY(__ForceVariable_case_t) + __ANY(__ForceVariable_case_p) + default: + break; + } + /* inc force_list cursor */ + force_list_addvar_cursor++; + } else { + error_code = FORCE_LIST_OVERFLOW; + goto error_cleanup; + } + } + } + return 0; + +error_cleanup: + ResetDebugVariables(); + trace_buffer_state = BUFFER_EMPTY; + return error_code; + +} + +#define ResetForcedVariable_case_t(TYPENAME) \ + case TYPENAME##_ENUM : \ + ((__IEC_##TYPENAME##_t *)varp)->flags &= ~__IEC_FORCE_FLAG; \ + /* for local variable we don't restore original value */ \ + /* that can be added if needed, but it was like that since ever */ \ + break; + +#define ResetForcedVariable_case_p(TYPENAME) \ + case TYPENAME##_P_ENUM : \ + case TYPENAME##_O_ENUM : \ + ((__IEC_##TYPENAME##_p *)varp)->flags &= ~__IEC_FORCE_FLAG; \ + /* restore backup to pointer */ \ + ((__IEC_##TYPENAME##_p *)varp)->value = \ + force_list_apply_cursor->value_pointer_backup; \ + break; + +void ResetDebugVariables(void) +{ + /* Reset trace list */ + trace_list_addvar_cursor = trace_list; + + force_list_apply_cursor = force_list; + /* Restore forced variables */ + while(force_list_apply_cursor < force_list_addvar_cursor){ + dbgvardsc_t *dsc = &dbgvardsc[ + force_list_apply_cursor->dbgvardsc_index]; void *varp = dsc->ptr; switch(dsc->type){ - __ANY(__RegisterDebugVariable_case_t) - __ANY(__RegisterDebugVariable_case_p) + __ANY(ResetForcedVariable_case_t) + __ANY(ResetForcedVariable_case_p) default: break; } - } -} - -#define __ResetDebugVariablesIterator_case_t(TYPENAME) \ - case TYPENAME##_ENUM :\ - ((__IEC_##TYPENAME##_t *)varp)->flags &= ~(__IEC_DEBUG_FLAG|__IEC_FORCE_FLAG);\ - break; - -#define __ResetDebugVariablesIterator_case_p(TYPENAME)\ - case TYPENAME##_P_ENUM :\ - case TYPENAME##_O_ENUM :\ - ((__IEC_##TYPENAME##_p *)varp)->flags &= ~(__IEC_DEBUG_FLAG|__IEC_FORCE_FLAG);\ - break; - -void ResetDebugVariablesIterator(dbgvardsc_t *dsc) -{ - /* force debug flag to 0*/ - void *varp = dsc->ptr; - switch(dsc->type){ - __ANY(__ResetDebugVariablesIterator_case_t) - __ANY(__ResetDebugVariablesIterator_case_p) - default: - break; - } -} - -void ResetDebugVariables(void) -{ - __for_each_variable_do(ResetDebugVariablesIterator); + /* inc force_list cursor */ + force_list_apply_cursor++; + } /* else TODO: warn user about failure to force */ + + /* Reset force list */ + force_list_addvar_cursor = force_list; + /* Reset force buffer */ + force_buffer_cursor = force_buffer; } void FreeDebugData(void) { /* atomically mark buffer as free */ AtomicCompareExchange( - &buffer_state, - BUFFER_BUSY, - BUFFER_FREE); + &trace_buffer_state, + BUFFER_FULL, + BUFFER_EMPTY); } int WaitDebugData(unsigned long *tick); /* Wait until debug data ready and return pointer to it */ int GetDebugData(unsigned long *tick, unsigned long *size, void **buffer){ int wait_error = WaitDebugData(tick); if(!wait_error){ - *size = buffer_cursor - debug_buffer; - *buffer = debug_buffer; + *size = trace_buffer_cursor - trace_buffer; + *buffer = trace_buffer; } return wait_error; } diff -r a65bcbb6af20 -r 3f54a680fe45 targets/var_access.c --- a/targets/var_access.c Tue Nov 30 09:52:42 2021 +0100 +++ b/targets/var_access.c Sun Jan 16 16:57:56 2022 +0100 @@ -1,37 +1,33 @@ -#define __Unpack_case_t(TYPENAME) \ - case TYPENAME##_ENUM :\ - *flags = ((__IEC_##TYPENAME##_t *)varp)->flags;\ - forced_value_p = *real_value_p = &((__IEC_##TYPENAME##_t *)varp)->value;\ +#define __Unpack_case_t(TYPENAME) \ + case TYPENAME##_ENUM : \ + if(flags) *flags = ((__IEC_##TYPENAME##_t *)varp)->flags; \ + if(value_p) *value_p = &((__IEC_##TYPENAME##_t *)varp)->value; \ + if(size) *size = sizeof(TYPENAME); \ break; -#define __Unpack_case_p(TYPENAME)\ - case TYPENAME##_O_ENUM :\ - *flags = __IEC_OUTPUT_FLAG;\ - case TYPENAME##_P_ENUM :\ - *flags |= ((__IEC_##TYPENAME##_p *)varp)->flags;\ - *real_value_p = ((__IEC_##TYPENAME##_p *)varp)->value;\ - forced_value_p = &((__IEC_##TYPENAME##_p *)varp)->fvalue;\ +#define __Unpack_case_p(TYPENAME) \ + case TYPENAME##_O_ENUM : \ + case TYPENAME##_P_ENUM : \ + if(flags) *flags = ((__IEC_##TYPENAME##_p *)varp)->flags; \ + if(value_p) *value_p = ((__IEC_##TYPENAME##_p *)varp)->value; \ + if(size) *size = sizeof(TYPENAME); \ break; #define __Is_a_string(dsc) (dsc->type == STRING_ENUM) ||\ (dsc->type == STRING_P_ENUM) ||\ (dsc->type == STRING_O_ENUM) -static void* UnpackVar(__Unpack_desc_type *dsc, void **real_value_p, char *flags) +static int UnpackVar(__Unpack_desc_type *dsc, void **value_p, char *flags, size_t *size) { void *varp = dsc->ptr; - void *forced_value_p = NULL; - *flags = 0; /* find data to copy*/ switch(dsc->type){ __ANY(__Unpack_case_t) __ANY(__Unpack_case_p) default: - break; + return 0; /* should never happen */ } - if (*flags & __IEC_FORCE_FLAG) - return forced_value_p; - return *real_value_p; + return 1; }