--- /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")