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 |