# HG changeset patch # User Edouard Tisserant # Date 1616560486 -3600 # Node ID 0f41c1e2c1210fff830058480776d1dcc9701da9 # Parent f1049e4df86604dcca4d12fba656d728607a1a8d SVGHMI: split svghmi.py into hmi_tree.py + svghmi.py diff -r f1049e4df866 -r 0f41c1e2c121 svghmi/hmi_tree.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/svghmi/hmi_tree.py Wed Mar 24 05:34:46 2021 +0100 @@ -0,0 +1,152 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +# This file is part of Beremiz +# Copyright (C) 2021: Edouard TISSERANT +# +# See COPYING file for copyrights details. + +from __future__ import absolute_import +from itertools import izip, imap +from pprint import pformat +import hashlib + +from lxml import etree + +HMI_TYPES_DESC = { + "HMI_NODE":{}, + "HMI_STRING":{}, + "HMI_INT":{}, + "HMI_BOOL":{}, + "HMI_REAL":{} +} + +HMI_TYPES = HMI_TYPES_DESC.keys() + +class HMITreeNode(object): + def __init__(self, path, name, nodetype, iectype = None, vartype = None, cpath = None, hmiclass = None): + self.path = path + self.name = name + self.nodetype = nodetype + self.hmiclass = hmiclass + + if iectype is not None: + self.iectype = iectype + self.vartype = vartype + self.cpath = cpath + + if nodetype in ["HMI_NODE"]: + self.children = [] + + def pprint(self, indent = 0): + res = ">"*indent + pformat(self.__dict__, indent = indent, depth = 1) + "\n" + if hasattr(self, "children"): + res += "\n".join([child.pprint(indent = indent + 1) + for child in self.children]) + res += "\n" + + return res + + def place_node(self, node): + best_child = None + known_best_match = 0 + potential_siblings = {} + for child in self.children: + if child.path is not None: + in_common = 0 + for child_path_item, node_path_item in izip(child.path, node.path): + if child_path_item == node_path_item: + in_common +=1 + else: + break + # Match can only be HMI_NODE, and the whole path of node + # must match candidate node (except for name part) + # since candidate would become child of that node + if in_common > known_best_match and \ + child.nodetype == "HMI_NODE" and \ + in_common == len(child.path) - 1: + known_best_match = in_common + best_child = child + else: + potential_siblings[child.path[ + -2 if child.nodetype == "HMI_NODE" else -1]] = child + if best_child is not None: + if node.nodetype == "HMI_NODE" and best_child.path[:-1] == node.path[:-1]: + return "Duplicate_HMI_NODE", best_child + return best_child.place_node(node) + else: + candidate_name = node.path[-2 if node.nodetype == "HMI_NODE" else -1] + if candidate_name in potential_siblings: + return "Non_Unique", potential_siblings[candidate_name] + + if node.nodetype == "HMI_NODE" and len(self.children) > 0: + prev = self.children[-1] + if prev.path[:-1] == node.path[:-1]: + return "Late_HMI_NODE",prev + + self.children.append(node) + return None + + def etree(self, add_hash=False): + + attribs = dict(name=self.name) + if self.path is not None: + attribs["path"] = ".".join(self.path) + + if self.hmiclass is not None: + attribs["class"] = self.hmiclass + + if add_hash: + attribs["hash"] = ",".join(map(str,self.hash())) + + res = etree.Element(self.nodetype, **attribs) + + if hasattr(self, "children"): + for child_etree in imap(lambda c:c.etree(), self.children): + res.append(child_etree) + + return res + + @classmethod + def from_etree(cls, enode): + """ + alternative constructor, restoring HMI Tree from XML backup + note: all C-related information is gone, + this restore is only for tree display and widget picking + """ + nodetype = enode.tag + attributes = enode.attrib + name = attributes["name"] + path = attributes["path"].split('.') if "path" in attributes else None + hmiclass = attributes.get("class", None) + # hash is computed on demand + node = cls(path, name, nodetype, hmiclass=hmiclass) + for child in enode.iterchildren(): + node.children.append(cls.from_etree(child)) + return node + + def traverse(self): + yield self + if hasattr(self, "children"): + for c in self.children: + for yoodl in c.traverse(): + yield yoodl + + + def hash(self): + """ Produce a hash, any change in HMI tree structure change that hash """ + s = hashlib.new('md5') + self._hash(s) + # limit size to HMI_HASH_SIZE as in svghmi.c + return map(ord,s.digest())[:8] + + def _hash(self, s): + s.update(str((self.name,self.nodetype))) + if hasattr(self, "children"): + for c in self.children: + c._hash(s) + +SPECIAL_NODES = [("HMI_ROOT", "HMI_NODE"), + ("heartbeat", "HMI_INT")] + # ("current_page", "HMI_STRING")]) + diff -r f1049e4df866 -r 0f41c1e2c121 svghmi/svghmi.py --- a/svghmi/svghmi.py Tue Mar 23 05:15:19 2021 +0100 +++ b/svghmi/svghmi.py Wed Mar 24 05:34:46 2021 +0100 @@ -2,15 +2,13 @@ # -*- coding: utf-8 -*- # This file is part of Beremiz -# Copyright (C) 2019: Edouard TISSERANT +# Copyright (C) 2021: Edouard TISSERANT # # See COPYING file for copyrights details. from __future__ import absolute_import import os import shutil -from itertools import izip, imap -from pprint import pformat import hashlib import weakref import shlex @@ -32,142 +30,11 @@ from editors.ConfTreeNodeEditor import ConfTreeNodeEditor from XSLTransform import XSLTransform from svghmi.i18n import EtreeToMessages, SaveCatalog, ReadTranslations, MatchTranslations, TranslationToEtree, open_pofile - -HMI_TYPES_DESC = { - "HMI_NODE":{}, - "HMI_STRING":{}, - "HMI_INT":{}, - "HMI_BOOL":{}, - "HMI_REAL":{} -} - -HMI_TYPES = HMI_TYPES_DESC.keys() +from svghmi.hmi_tree import HMI_TYPES, HMITreeNode, SPECIAL_NODES ScriptDirectory = paths.AbsDir(__file__) -class HMITreeNode(object): - def __init__(self, path, name, nodetype, iectype = None, vartype = None, cpath = None, hmiclass = None): - self.path = path - self.name = name - self.nodetype = nodetype - self.hmiclass = hmiclass - - if iectype is not None: - self.iectype = iectype - self.vartype = vartype - self.cpath = cpath - - if nodetype in ["HMI_NODE"]: - self.children = [] - - def pprint(self, indent = 0): - res = ">"*indent + pformat(self.__dict__, indent = indent, depth = 1) + "\n" - if hasattr(self, "children"): - res += "\n".join([child.pprint(indent = indent + 1) - for child in self.children]) - res += "\n" - - return res - - def place_node(self, node): - best_child = None - known_best_match = 0 - potential_siblings = {} - for child in self.children: - if child.path is not None: - in_common = 0 - for child_path_item, node_path_item in izip(child.path, node.path): - if child_path_item == node_path_item: - in_common +=1 - else: - break - # Match can only be HMI_NODE, and the whole path of node - # must match candidate node (except for name part) - # since candidate would become child of that node - if in_common > known_best_match and \ - child.nodetype == "HMI_NODE" and \ - in_common == len(child.path) - 1: - known_best_match = in_common - best_child = child - else: - potential_siblings[child.path[ - -2 if child.nodetype == "HMI_NODE" else -1]] = child - if best_child is not None: - if node.nodetype == "HMI_NODE" and best_child.path[:-1] == node.path[:-1]: - return "Duplicate_HMI_NODE", best_child - return best_child.place_node(node) - else: - candidate_name = node.path[-2 if node.nodetype == "HMI_NODE" else -1] - if candidate_name in potential_siblings: - return "Non_Unique", potential_siblings[candidate_name] - - if node.nodetype == "HMI_NODE" and len(self.children) > 0: - prev = self.children[-1] - if prev.path[:-1] == node.path[:-1]: - return "Late_HMI_NODE",prev - - self.children.append(node) - return None - - def etree(self, add_hash=False): - - attribs = dict(name=self.name) - if self.path is not None: - attribs["path"] = ".".join(self.path) - - if self.hmiclass is not None: - attribs["class"] = self.hmiclass - - if add_hash: - attribs["hash"] = ",".join(map(str,self.hash())) - - res = etree.Element(self.nodetype, **attribs) - - if hasattr(self, "children"): - for child_etree in imap(lambda c:c.etree(), self.children): - res.append(child_etree) - - return res - - @classmethod - def from_etree(cls, enode): - """ - alternative constructor, restoring HMI Tree from XML backup - note: all C-related information is gone, - this restore is only for tree display and widget picking - """ - nodetype = enode.tag - attributes = enode.attrib - name = attributes["name"] - path = attributes["path"].split('.') if "path" in attributes else None - hmiclass = attributes.get("class", None) - # hash is computed on demand - node = cls(path, name, nodetype, hmiclass=hmiclass) - for child in enode.iterchildren(): - node.children.append(cls.from_etree(child)) - return node - - def traverse(self): - yield self - if hasattr(self, "children"): - for c in self.children: - for yoodl in c.traverse(): - yield yoodl - - - def hash(self): - """ Produce a hash, any change in HMI tree structure change that hash """ - s = hashlib.new('md5') - self._hash(s) - # limit size to HMI_HASH_SIZE as in svghmi.c - return map(ord,s.digest())[:8] - - def _hash(self, s): - s.update(str((self.name,self.nodetype))) - if hasattr(self, "children"): - for c in self.children: - c._hash(s) # module scope for HMITree root # so that CTN can use HMITree deduced in Library @@ -178,10 +45,6 @@ on_hmitree_update = None -SPECIAL_NODES = [("HMI_ROOT", "HMI_NODE"), - ("heartbeat", "HMI_INT")] - # ("current_page", "HMI_STRING")]) - class SVGHMILibrary(POULibrary): def GetLibraryPath(self): return paths.AbsNeighbourFile(__file__, "pous.xml")