SVGHMI: split svghmi.py into hmi_tree.py + svghmi.py svghmi
authorEdouard Tisserant <edouard.tisserant@gmail.com>
Wed, 24 Mar 2021 05:34:46 +0100
branchsvghmi
changeset 3197 0f41c1e2c121
parent 3196 f1049e4df866
child 3201 6dadc1690284
SVGHMI: split svghmi.py into hmi_tree.py + svghmi.py
svghmi/hmi_tree.py
svghmi/svghmi.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")])
+
--- 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")