svghmi/svghmi.py
branchsvghmi
changeset 3197 0f41c1e2c121
parent 3193 8006bb60a4dd
child 3201 6dadc1690284
equal deleted inserted replaced
3196:f1049e4df866 3197:0f41c1e2c121
     1 #!/usr/bin/env python
     1 #!/usr/bin/env python
     2 # -*- coding: utf-8 -*-
     2 # -*- coding: utf-8 -*-
     3 
     3 
     4 # This file is part of Beremiz
     4 # This file is part of Beremiz
     5 # Copyright (C) 2019: Edouard TISSERANT
     5 # Copyright (C) 2021: Edouard TISSERANT
     6 #
     6 #
     7 # See COPYING file for copyrights details.
     7 # See COPYING file for copyrights details.
     8 
     8 
     9 from __future__ import absolute_import
     9 from __future__ import absolute_import
    10 import os
    10 import os
    11 import shutil
    11 import shutil
    12 from itertools import izip, imap
       
    13 from pprint import pformat
       
    14 import hashlib
    12 import hashlib
    15 import weakref
    13 import weakref
    16 import shlex
    14 import shlex
    17 import time
    15 import time
    18 
    16 
    30 from runtime.typemapping import DebugTypesSize
    28 from runtime.typemapping import DebugTypesSize
    31 import targets
    29 import targets
    32 from editors.ConfTreeNodeEditor import ConfTreeNodeEditor
    30 from editors.ConfTreeNodeEditor import ConfTreeNodeEditor
    33 from XSLTransform import XSLTransform
    31 from XSLTransform import XSLTransform
    34 from svghmi.i18n import EtreeToMessages, SaveCatalog, ReadTranslations, MatchTranslations, TranslationToEtree, open_pofile
    32 from svghmi.i18n import EtreeToMessages, SaveCatalog, ReadTranslations, MatchTranslations, TranslationToEtree, open_pofile
    35 
    33 from svghmi.hmi_tree import HMI_TYPES, HMITreeNode, SPECIAL_NODES 
    36 HMI_TYPES_DESC = {
       
    37     "HMI_NODE":{},
       
    38     "HMI_STRING":{},
       
    39     "HMI_INT":{},
       
    40     "HMI_BOOL":{},
       
    41     "HMI_REAL":{}
       
    42 }
       
    43 
       
    44 HMI_TYPES = HMI_TYPES_DESC.keys()
       
    45 
    34 
    46 
    35 
    47 ScriptDirectory = paths.AbsDir(__file__)
    36 ScriptDirectory = paths.AbsDir(__file__)
    48 
    37 
    49 class HMITreeNode(object):
       
    50     def __init__(self, path, name, nodetype, iectype = None, vartype = None, cpath = None, hmiclass = None):
       
    51         self.path = path
       
    52         self.name = name
       
    53         self.nodetype = nodetype
       
    54         self.hmiclass = hmiclass
       
    55 
       
    56         if iectype is not None:
       
    57             self.iectype = iectype
       
    58             self.vartype = vartype
       
    59             self.cpath = cpath
       
    60 
       
    61         if nodetype in ["HMI_NODE"]:
       
    62             self.children = []
       
    63 
       
    64     def pprint(self, indent = 0):
       
    65         res = ">"*indent + pformat(self.__dict__, indent = indent, depth = 1) + "\n"
       
    66         if hasattr(self, "children"):
       
    67             res += "\n".join([child.pprint(indent = indent + 1)
       
    68                               for child in self.children])
       
    69             res += "\n"
       
    70 
       
    71         return res
       
    72 
       
    73     def place_node(self, node):
       
    74         best_child = None
       
    75         known_best_match = 0
       
    76         potential_siblings = {}
       
    77         for child in self.children:
       
    78             if child.path is not None:
       
    79                 in_common = 0
       
    80                 for child_path_item, node_path_item in izip(child.path, node.path):
       
    81                     if child_path_item == node_path_item:
       
    82                         in_common +=1
       
    83                     else:
       
    84                         break
       
    85                 # Match can only be HMI_NODE, and the whole path of node
       
    86                 # must match candidate node (except for name part)
       
    87                 # since candidate would become child of that node
       
    88                 if in_common > known_best_match and \
       
    89                    child.nodetype == "HMI_NODE" and \
       
    90                    in_common == len(child.path) - 1:
       
    91                     known_best_match = in_common
       
    92                     best_child = child
       
    93                 else:
       
    94                     potential_siblings[child.path[
       
    95                         -2 if child.nodetype == "HMI_NODE" else -1]] = child
       
    96         if best_child is not None:
       
    97             if node.nodetype == "HMI_NODE" and best_child.path[:-1] == node.path[:-1]:
       
    98                 return "Duplicate_HMI_NODE", best_child
       
    99             return best_child.place_node(node)
       
   100         else:
       
   101             candidate_name = node.path[-2 if node.nodetype == "HMI_NODE" else -1]
       
   102             if candidate_name in potential_siblings:
       
   103                 return "Non_Unique", potential_siblings[candidate_name]
       
   104 
       
   105             if node.nodetype == "HMI_NODE" and len(self.children) > 0:
       
   106                 prev = self.children[-1]
       
   107                 if prev.path[:-1] == node.path[:-1]:
       
   108                     return "Late_HMI_NODE",prev
       
   109 
       
   110             self.children.append(node)
       
   111             return None
       
   112 
       
   113     def etree(self, add_hash=False):
       
   114 
       
   115         attribs = dict(name=self.name)
       
   116         if self.path is not None:
       
   117             attribs["path"] = ".".join(self.path)
       
   118 
       
   119         if self.hmiclass is not None:
       
   120             attribs["class"] = self.hmiclass
       
   121 
       
   122         if add_hash:
       
   123             attribs["hash"] = ",".join(map(str,self.hash()))
       
   124 
       
   125         res = etree.Element(self.nodetype, **attribs)
       
   126 
       
   127         if hasattr(self, "children"):
       
   128             for child_etree in imap(lambda c:c.etree(), self.children):
       
   129                 res.append(child_etree)
       
   130 
       
   131         return res
       
   132 
       
   133     @classmethod
       
   134     def from_etree(cls, enode):
       
   135         """
       
   136         alternative constructor, restoring HMI Tree from XML backup
       
   137         note: all C-related information is gone, 
       
   138               this restore is only for tree display and widget picking
       
   139         """
       
   140         nodetype = enode.tag
       
   141         attributes = enode.attrib
       
   142         name = attributes["name"]
       
   143         path = attributes["path"].split('.') if "path" in attributes else None 
       
   144         hmiclass = attributes.get("class", None)
       
   145         # hash is computed on demand
       
   146         node = cls(path, name, nodetype, hmiclass=hmiclass)
       
   147         for child in enode.iterchildren():
       
   148             node.children.append(cls.from_etree(child))
       
   149         return node
       
   150 
       
   151     def traverse(self):
       
   152         yield self
       
   153         if hasattr(self, "children"):
       
   154             for c in self.children:
       
   155                 for yoodl in c.traverse():
       
   156                     yield yoodl
       
   157 
       
   158 
       
   159     def hash(self):
       
   160         """ Produce a hash, any change in HMI tree structure change that hash """
       
   161         s = hashlib.new('md5')
       
   162         self._hash(s)
       
   163         # limit size to HMI_HASH_SIZE as in svghmi.c
       
   164         return map(ord,s.digest())[:8]
       
   165 
       
   166     def _hash(self, s):
       
   167         s.update(str((self.name,self.nodetype)))
       
   168         if hasattr(self, "children"):
       
   169             for c in self.children:
       
   170                 c._hash(s)
       
   171 
    38 
   172 # module scope for HMITree root
    39 # module scope for HMITree root
   173 # so that CTN can use HMITree deduced in Library
    40 # so that CTN can use HMITree deduced in Library
   174 # note: this only works because library's Generate_C is
    41 # note: this only works because library's Generate_C is
   175 #       systematicaly invoked before CTN's CTNGenerate_C
    42 #       systematicaly invoked before CTN's CTNGenerate_C
   176 
    43 
   177 hmi_tree_root = None
    44 hmi_tree_root = None
   178 
    45 
   179 on_hmitree_update = None
    46 on_hmitree_update = None
   180 
       
   181 SPECIAL_NODES = [("HMI_ROOT", "HMI_NODE"),
       
   182                  ("heartbeat", "HMI_INT")]
       
   183                  # ("current_page", "HMI_STRING")])
       
   184 
    47 
   185 class SVGHMILibrary(POULibrary):
    48 class SVGHMILibrary(POULibrary):
   186     def GetLibraryPath(self):
    49     def GetLibraryPath(self):
   187          return paths.AbsNeighbourFile(__file__, "pous.xml")
    50          return paths.AbsNeighbourFile(__file__, "pous.xml")
   188 
    51