# HG changeset patch # User Edouard Tisserant <edouard.tisserant@gmail.com> # Date 1737890412 -3600 # Node ID 59f90953b1178709abdd22c5bc0a18295639f455 # Parent 5d86ede7384a05d960082c25a094c9ba53f71f76# Parent c6c80c08849718bd5e85f88df51ebd59bb580a9b merged diff -r 5d86ede7384a -r 59f90953b117 ConfigTreeNode.py --- a/ConfigTreeNode.py Fri Jan 24 15:53:11 2025 +0100 +++ b/ConfigTreeNode.py Sun Jan 26 12:20:12 2025 +0100 @@ -585,6 +585,8 @@ NewCTNName = self.FindNewName(CTNName) # If dir have already be made, and file exist if os.path.isdir(self.CTNPath(NewCTNName)): # and os.path.isfile(self.ConfNodeXmlFilePath(CTNName)): + # Make CTN aware that this configuration was loaded + self.new_config = False # Load the confnode.xml file into parameters members self.LoadXMLParams(NewCTNName) # Basic check. Better to fail immediately. @@ -605,6 +607,8 @@ # just loaded, nothing to saved self.ChangesToSave = False else: + # Make CTN aware that this is first time configuration + self.new_config = True # If confnode do not have corresponding file/dirs - they will be created on Save self.CTNMakeDir() # Find an IEC number diff -r 5d86ede7384a -r 59f90953b117 mqtt/library.py --- a/mqtt/library.py Fri Jan 24 15:53:11 2025 +0100 +++ b/mqtt/library.py Sun Jan 26 12:20:12 2025 +0100 @@ -22,11 +22,14 @@ res = c_function(topic, payload, len(payload), QoS, Retained) return res -# C per client CallBack type for __mqtt_python_subscribe_{name} -c_cb_type = ctypes.CFUNCTYPE(ctypes.c_int, # return - ctypes.c_char_p, # topic - ctypes.POINTER(ctypes.c_char), # data - ctypes.c_uint32) # data length +# C per client CallBack type for __mqtt_python_onmsg_{name} +mqtt_c_cb_onmsg_type = ctypes.CFUNCTYPE(ctypes.c_int, # return + ctypes.c_char_p, # topic + ctypes.POINTER(ctypes.c_char), # data + ctypes.c_uint32) # data length + +# C per client CallBack type for __mqtt_python_resub_{name} +mqtt_c_cb_resub_type = ctypes.CFUNCTYPE(ctypes.c_int) # return # CallBacks management # - each call to MQTT_subscribe registers a callback @@ -35,18 +38,21 @@ # - one callback registered to C side per client MQTT_client_cbs = {} -def per_client_cb_factory(client): - def per_client_cb(topic, dataptr, datalen): +def mqtt_per_client_cb_factory(clientname): + def per_client_onmsg_cb(topic, dataptr, datalen): payload = ctypes.string_at(dataptr, datalen) - subscriber = MQTT_subscribers_cbs[client].get(topic, None) + subscriber,_Qos = MQTT_subscribers_cbs[clientname].get(topic, None) if subscriber: subscriber(topic, payload) return 0 return 1 - return per_client_cb + def per_client_resub_cb(): + for topic,(_cb,QoS) in MQTT_subscribers_cbs[clientname].items(): + _MQTT_subscribe(clientname, topic, QoS) + return 1 + return per_client_onmsg_cb,per_client_resub_cb -def MQTT_subscribe(clientname, topic, cb, QoS = 1): - global MQTT_client_cbs, MQTT_subscribers_cbs +def _MQTT_subscribe(clientname, topic, QoS): c_function_name = "__mqtt_python_subscribe_" + clientname c_function = getattr(PLCBinary, c_function_name) c_function.restype = ctypes.c_int # error or 0 @@ -54,17 +60,24 @@ ctypes.c_char_p, # topic ctypes.c_uint8] # QoS - MQTT_subscribers_cbs.setdefault(clientname, {})[topic] = cb + return c_function(topic, QoS) - c_cb = MQTT_client_cbs.get(clientname, None) - if c_cb is None: - c_cb = c_cb_type(per_client_cb_factory(clientname)) - MQTT_client_cbs[clientname] = c_cb +def MQTT_subscribe(clientname, topic, cb, QoS = 1): + global MQTT_client_cbs, MQTT_subscribers_cbs + + MQTT_subscribers_cbs.setdefault(clientname, {})[topic] = (cb, QoS) + res = _MQTT_subscribe(clientname, topic, QoS) + + c_cbs = MQTT_client_cbs.get(clientname, None) + if c_cbs is None: + cb_onmsg, cb_resub = mqtt_per_client_cb_factory(clientname) + c_cbs = (mqtt_c_cb_onmsg_type(cb_onmsg), + mqtt_c_cb_resub_type(cb_resub)) + MQTT_client_cbs[clientname] = c_cbs register_c_function = getattr(PLCBinary, "__mqtt_python_callback_setter_"+clientname ) - register_c_function.argtypes = [c_cb_type] - register_c_function(c_cb) + register_c_function.argtypes = [mqtt_c_cb_onmsg_type, mqtt_c_cb_resub_type] + register_c_function(*c_cbs) - res = c_function(topic, QoS) return res """ diff -r 5d86ede7384a -r 59f90953b117 mqtt/mqtt_template.c --- a/mqtt/mqtt_template.c Fri Jan 24 15:53:11 2025 +0100 +++ b/mqtt/mqtt_template.c Sun Jan 26 12:20:12 2025 +0100 @@ -260,8 +260,11 @@ }} -typedef int(*callback_fptr_t)(char* topic, char* data, uint32_t datalen); -static callback_fptr_t __mqtt_python_callback_fptr_{name} = NULL; +typedef int(*cb_onmsg_fptr_t)(char* topic, char* data, uint32_t datalen); +static cb_onmsg_fptr_t __mqtt_python_cb_onmsg_fptr_{name} = NULL; + +typedef int(*cb_resub_fptr_t)(void); +static cb_resub_fptr_t __mqtt_python_cb_resub_fptr_{name} = NULL; static int messageArrived(void *context, char *topicName, int topicLen, MQTTClient_message *message) {{ @@ -309,8 +312,8 @@ high = mid - 1; }} // If we reach here, then the element was not present - if(__mqtt_python_callback_fptr_{name} && - (*__mqtt_python_callback_fptr_{name})(topicName, + if(__mqtt_python_cb_onmsg_fptr_{name} && + (*__mqtt_python_cb_onmsg_fptr_{name})(topicName, (char*)message->payload, message->payloadlen) == 0){{ // Topic was handled in python @@ -405,7 +408,7 @@ #define PUBLISH_JSON(Topic, QoS, C_type, c_loc_name, Retained) \ int res = json_gen_##c_loc_name(&MQTT_##c_loc_name##_buf); \ if(res == 0) {{ \ - _PUBLISH(Topic, QoS, json_out_len, json_out_buf, Retained) \ + _PUBLISH(#Topic, QoS, json_out_len, json_out_buf, Retained) \ }} #define INIT_PUBLICATION(encoding, Topic, QoS, C_type, c_loc_name, Retained) \ @@ -458,6 +461,10 @@ {init_pubsub} + if(__mqtt_python_cb_resub_fptr_{name}){{ + (*__mqtt_python_cb_resub_fptr_{name})(); + }} + return MQTTCLIENT_SUCCESS; }} @@ -630,8 +637,9 @@ return 0; }} -int __mqtt_python_callback_setter_{name}(callback_fptr_t cb) -{{ - __mqtt_python_callback_fptr_{name} = cb; +int __mqtt_python_callback_setter_{name}(cb_onmsg_fptr_t cb_onmsg, cb_resub_fptr_t cb_resub) +{{ + __mqtt_python_cb_onmsg_fptr_{name} = cb_onmsg; + __mqtt_python_cb_resub_fptr_{name} = cb_resub; return 0; }} diff -r 5d86ede7384a -r 59f90953b117 xmlclass/xmlclass.py --- a/xmlclass/xmlclass.py Fri Jan 24 15:53:11 2025 +0100 +++ b/xmlclass/xmlclass.py Sun Jan 26 12:20:12 2025 +0100 @@ -575,7 +575,7 @@ } -def GenerateTagInfos(infos): +def GenerateTagInfos(factory, infos): def ExtractTag(tree): if len(tree._attrs) > 0: raise ValueError("\"%s\" musn't have attributes!" % infos["name"]) @@ -593,15 +593,14 @@ else: return "" - def Initial(): - p = etree.Element(infos["name"]) - return p + def InitialTag(): + return factory.Parser.CreateElement(infos["name"]) return { "type": TAG, "extract": ExtractTag, "generate": GenerateTag, - "initial": Initial, + "initial": InitialTag, "check": lambda x: x is None or infos["minOccurs"] == 0 and x } @@ -659,7 +658,7 @@ if element_infos is not None: sequence_element["elmt_type"] = element_infos elif choice["elmt_type"] == "tag": - choice["elmt_type"] = GenerateTagInfos(choice) + choice["elmt_type"] = GenerateTagInfos(factory, choice) factory.AddToLookupClass(choice["name"], name, DefaultElementClass) else: choice_infos = factory.ExtractTypeInfos(choice["name"], name, choice["elmt_type"]) @@ -1147,7 +1146,7 @@ else: elmtname = element["name"] if element["elmt_type"] == "tag": - infos = GenerateTagInfos(element) + infos = GenerateTagInfos(self, element) self.AddToLookupClass(element["name"], name, DefaultElementClass) else: infos = self.ExtractTypeInfos(element["name"], name, element["elmt_type"]) @@ -1489,8 +1488,7 @@ value = "" else: value = self.content.getLocalTag() - if self.content is not None: - children.extend(self.content.getElementInfos(value)["children"]) + children.extend(self.content.getElementInfos(value)["children"]) elif element["elmt_type"]["type"] == SIMPLETYPE: children.append({ "name": element_name, @@ -1736,7 +1734,14 @@ return NAMESPACE_PATTERN.sub("", etree.tostring(self, encoding='unicode')) def getElementInfos(self, name, path=None, derived=False): - return {"name": name, "type": TAG, "value": None, "use": None, "children": []} + if path is not None: + raise ValueError("Simple element "+name+" accepts no path: "+path) + return { + "name": name, + "type": "element", + "value": None, + "use": "required", + "children": []} class XMLElementClassLookUp(etree.PythonElementClassLookup):