# HG changeset patch # User Edouard Tisserant # Date 1700867885 -3600 # Node ID 89549813a6c19c8e72706fbd303f980620faa9fa # Parent 17d0d164109009e016e2f814216c42e34b38f0d9 SVGHMI: Fixes UI lifecycle problems Was sometime showing HMI tree of previously closed project : no more module globals to store hmi tree. C++ dead object exceptions happened when re-opening SVGHMI UI or building after close of UI. diff -r 17d0d1641090 -r 89549813a6c1 svghmi/svghmi.py --- a/svghmi/svghmi.py Fri Nov 24 23:56:38 2023 +0100 +++ b/svghmi/svghmi.py Sat Nov 25 00:18:05 2023 +0100 @@ -44,20 +44,19 @@ # note: this only works because library's Generate_C is # systematicaly invoked before CTN's CTNGenerate_C -hmi_tree_root = None - -on_hmitree_update = None - -maxConnectionsTotal = 0 class SVGHMILibrary(POULibrary): + + hmi_tree_root = None + + maxConnectionsTotal = 0 + def GetLibraryPath(self): return paths.AbsNeighbourFile(__file__, "pous.xml") def Generate_C(self, buildpath, varlist, IECCFLAGS): - global hmi_tree_root, on_hmitree_update, maxConnectionsTotal - - maxConnectionsTotal = 0 + + self.maxConnectionsTotal = 0 already_found_watchdog = False found_SVGHMI_instance = False @@ -65,7 +64,7 @@ if isinstance(CTNChild, SVGHMI): found_SVGHMI_instance = True # collect maximum connection total for all svghmi nodes - maxConnectionsTotal += CTNChild.GetParamsAttributes("SVGHMI.MaxConnections")["value"] + self.maxConnectionsTotal += CTNChild.GetParamsAttributes("SVGHMI.MaxConnections")["value"] # spot watchdog abuse if CTNChild.GetParamsAttributes("SVGHMI.EnableWatchdog")["value"]: @@ -114,14 +113,14 @@ # Filter known HMI types hmi_types_instances = [v for v in varlist if v["derived"] in HMI_TYPES] - hmi_tree_root = None + self.hmi_tree_root = None # take first HMI_NODE (placed as special node), make it root for i,v in enumerate(hmi_types_instances): path = v["IEC_path"].split(".") derived = v["derived"] if derived == "HMI_NODE": - hmi_tree_root = HMITreeNode(path, "", derived, v["type"], v["vartype"], v["C_path"]) + self.hmi_tree_root = HMITreeNode(path, "", derived, v["type"], v["vartype"], v["C_path"]) hmi_types_instances.pop(i) break @@ -144,7 +143,7 @@ else: name = path[-1] new_node = HMITreeNode(path, name, derived, v["type"], vartype, v["C_path"], **kwargs) - placement_result = hmi_tree_root.place_node(new_node) + placement_result = self.hmi_tree_root.place_node(new_node) if placement_result is not None: cause, problematic_node = placement_result if cause == "Non_Unique": @@ -171,8 +170,7 @@ self.FatalError("SVGHMI : " + message) - if on_hmitree_update is not None: - on_hmitree_update(hmi_tree_root) + self.on_hmitree_update() variable_decl_array = [] extern_variables_declarations = [] @@ -182,7 +180,7 @@ hearbeat_IEC_path = ['CONFIG', 'HEARTBEAT'] - for node in hmi_tree_root.traverse(): + for node in self.hmi_tree_root.traverse(): if not found_heartbeat and node.path == hearbeat_IEC_path: hmi_tree_hearbeat_index = item_count found_heartbeat = True @@ -232,8 +230,8 @@ "item_count": item_count, "var_access_code": targets.GetCode("var_access.c"), "PLC_ticktime": self.GetCTR().GetTicktime(), - "hmi_hash_ints": ",".join(map(str,hmi_tree_root.hash())), - "max_connections": maxConnectionsTotal + "hmi_hash_ints": ",".join(map(str,self.hmi_tree_root.hash())), + "max_connections": self.maxConnectionsTotal } gen_svghmi_c_path = os.path.join(buildpath, "svghmi.c") @@ -254,7 +252,7 @@ # Backup HMI Tree in XML form so that it can be loaded without building hmitree_backup_path = os.path.join(buildpath, "hmitree.xml") hmitree_backup_file = open(hmitree_backup_path, 'wb') - hmitree_backup_file.write(etree.tostring(hmi_tree_root.etree())) + hmitree_backup_file.write(etree.tostring(self.hmi_tree_root.etree())) hmitree_backup_file.close() return ((["svghmi"], [(gen_svghmi_c_path, IECCFLAGS)], True), "", @@ -268,15 +266,18 @@ return [(name, iec_type, "") for name, iec_type in SPECIAL_NODES] - -def Register_SVGHMI_UI_for_HMI_tree_updates(ref): - global on_hmitree_update - def HMITreeUpdate(_hmi_tree_root): - obj = ref() - if obj is not None: - obj.HMITreeUpdate(_hmi_tree_root) - - on_hmitree_update = HMITreeUpdate + registered_uis = [] + def on_hmitree_update(self): + for uiref in self.registered_uis[:]: + obj = uiref() + if obj is None: + self.registered_uis.remove(uiref) + else: + obj.HMITreeUpdate(self.hmi_tree_root) + + + def Register_SVGHMI_UI_for_HMI_tree_updates(self, uiref): + self.registered_uis.append(uiref) class SVGHMIEditor(ConfTreeNodeEditor): @@ -288,18 +289,19 @@ self.Controler = controler def CreateSVGHMI_UI(self, parent): - global hmi_tree_root - - if hmi_tree_root is None: - buildpath = self.Controler.GetCTRoot()._getBuildPath() + ctroot = self.Controler.GetCTRoot() + svghmilib = ctroot.Libraries["SVGHMI"] + + if svghmilib.hmi_tree_root is None: + buildpath = ctroot._getBuildPath() hmitree_backup_path = os.path.join(buildpath, "hmitree.xml") if os.path.exists(hmitree_backup_path): hmitree_backup_file = open(hmitree_backup_path, 'rb') - hmi_tree_root = HMITreeNode.from_etree(etree.parse(hmitree_backup_file).getroot()) - - ret = SVGHMI_UI(parent, self.Controler, Register_SVGHMI_UI_for_HMI_tree_updates) - - on_hmitree_update(hmi_tree_root) + svghmilib.hmi_tree_root = HMITreeNode.from_etree(etree.parse(hmitree_backup_file).getroot()) + + ret = SVGHMI_UI(parent, self.Controler, svghmilib.Register_SVGHMI_UI_for_HMI_tree_updates) + + svghmilib.on_hmitree_update() return ret @@ -450,9 +452,10 @@ return res def GetHMITree(self): - global hmi_tree_root + ctroot = self.GetCTRoot() + svghmilib = ctroot.Libraries["SVGHMI"] self.ProgressStart("hmitree", "getting HMI tree") - res = [hmi_tree_root.etree(add_hash=True)] + res = [svghmilib.hmi_tree_root.etree(add_hash=True)] self.ProgressEnd("hmitree") return res @@ -527,7 +530,10 @@ url=url) def CTNGenerate_C(self, buildpath, locations): - global hmi_tree_root + ctroot = self.GetCTRoot() + svghmilib = ctroot.Libraries["SVGHMI"] + hmi_tree_root = svghmilib.hmi_tree_root + if hmi_tree_root is None: self.FatalError("SVGHMI : Library is not selected. Please select it in project config.") @@ -545,7 +551,7 @@ target_path = os.path.join(build_path, target_fname) hash_path = os.path.join(build_path, "svghmi_"+location_str+".md5") - self.GetCTRoot().logger.write("SVGHMI:\n") + ctroot.logger.write("SVGHMI:\n") if os.path.exists(svgfile): @@ -729,7 +735,7 @@ watchdog_initial = self.GetParamsAttributes("SVGHMI.WatchdogInitial")["value"], watchdog_interval = self.GetParamsAttributes("SVGHMI.WatchdogInterval")["value"], maxConnections = self.GetParamsAttributes("SVGHMI.MaxConnections")["value"], - maxConnections_total = maxConnectionsTotal, + maxConnections_total = svghmilib.maxConnectionsTotal, **svghmi_options )) diff -r 17d0d1641090 -r 89549813a6c1 svghmi/ui.py --- a/svghmi/ui.py Fri Nov 24 23:56:38 2023 +0100 +++ b/svghmi/ui.py Sat Nov 25 00:18:05 2023 +0100 @@ -717,7 +717,8 @@ register_for_HMI_tree_updates(weakref.ref(self)) def HMITreeUpdate(self, hmi_tree_root): - self.SelectionTree.MakeTree(hmi_tree_root) + if self: + self.SelectionTree.MakeTree(hmi_tree_root) def OnHMITreeNodeSelection(self, hmitree_nodes): self.Staging.OnHMITreeNodeSelection(hmitree_nodes)