Merge remote-tracking branch 'hggit/python3' into python3
authorEdouard Tisserant <edouard.tisserant@gmail.com>
Sun, 26 Jan 2025 14:58:13 +0100 (6 weeks ago)
changeset 4103 63c002e87c57
parent 4102 32e0ffdf2f44 (current diff)
parent 4101 59f90953b117 (diff)
child 4109 2fb97bc2158a
Merge remote-tracking branch 'hggit/python3' into python3
--- a/ConfigTreeNode.py	Wed Jan 22 22:05:08 2025 +0100
+++ b/ConfigTreeNode.py	Sun Jan 26 14:58:13 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
Binary file exemples/svghmi_csv_json_img_table/project_files/beremiz.png has changed
Binary file exemples/svghmi_csv_json_img_table/project_files/file.png has changed
Binary file exemples/svghmi_csv_json_img_table/project_files/folder.png has changed
--- a/mqtt/library.py	Wed Jan 22 22:05:08 2025 +0100
+++ b/mqtt/library.py	Sun Jan 26 14:58:13 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
 
 """
--- a/mqtt/mqtt_template.c	Wed Jan 22 22:05:08 2025 +0100
+++ b/mqtt/mqtt_template.c	Sun Jan 26 14:58:13 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;
 }}
--- a/runtime/WampClient.py	Wed Jan 22 22:05:08 2025 +0100
+++ b/runtime/WampClient.py	Sun Jan 26 14:58:13 2025 +0100
@@ -241,7 +241,7 @@
 
 
 def SetWampSecret(wampSecret):
-    with open(os.path.realpath(_WampSecret), 'w') as f:
+    with open(os.path.realpath(_WampSecret), 'wb') as f:
         f.write(wampSecret)
 
 
--- a/xmlclass/xmlclass.py	Wed Jan 22 22:05:08 2025 +0100
+++ b/xmlclass/xmlclass.py	Sun Jan 26 14:58:13 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):