55 if nodetype in ["HMI_NODE", "HMI_ROOT"]: |
55 if nodetype in ["HMI_NODE", "HMI_ROOT"]: |
56 self.children = [] |
56 self.children = [] |
57 |
57 |
58 def pprint(self, indent = 0): |
58 def pprint(self, indent = 0): |
59 res = ">"*indent + pformat(self.__dict__, indent = indent, depth = 1) + "\n" |
59 res = ">"*indent + pformat(self.__dict__, indent = indent, depth = 1) + "\n" |
60 if hasattr(self, "children"): |
60 if hasattr(self, "children"): |
61 res += "\n".join([child.pprint(indent = indent + 1) |
61 res += "\n".join([child.pprint(indent = indent + 1) |
62 for child in self.children]) |
62 for child in self.children]) |
63 res += "\n" |
63 res += "\n" |
64 |
64 |
65 return res |
65 return res |
66 |
66 |
67 def place_node(self, node): |
67 def place_node(self, node): |
68 best_child = None |
68 best_child = None |
69 known_best_match = 0 |
69 known_best_match = 0 |
70 for child in self.children : |
70 for child in self.children: |
71 if child.path is not None: |
71 if child.path is not None: |
72 in_common = 0 |
72 in_common = 0 |
73 for child_path_item, node_path_item in izip(child.path, node.path): |
73 for child_path_item, node_path_item in izip(child.path, node.path): |
74 if child_path_item == node_path_item: |
74 if child_path_item == node_path_item: |
75 in_common +=1 |
75 in_common +=1 |
80 best_child = child |
80 best_child = child |
81 if best_child is not None and best_child.nodetype == "HMI_NODE": |
81 if best_child is not None and best_child.nodetype == "HMI_NODE": |
82 best_child.place_node(node) |
82 best_child.place_node(node) |
83 else: |
83 else: |
84 self.children.append(node) |
84 self.children.append(node) |
85 |
85 |
86 def etree(self, add_hash=False): |
86 def etree(self, add_hash=False): |
87 |
87 |
88 attribs = dict(name=self.name) |
88 attribs = dict(name=self.name) |
89 if self.path is not None: |
89 if self.path is not None: |
90 attribs["path"] = ".".join(self.path) |
90 attribs["path"] = ".".join(self.path) |
95 if add_hash: |
95 if add_hash: |
96 attribs["hash"] = ",".join(map(str,self.hash())) |
96 attribs["hash"] = ",".join(map(str,self.hash())) |
97 |
97 |
98 res = etree.Element(self.nodetype, **attribs) |
98 res = etree.Element(self.nodetype, **attribs) |
99 |
99 |
100 if hasattr(self, "children"): |
100 if hasattr(self, "children"): |
101 for child_etree in imap(lambda c:c.etree(), self.children): |
101 for child_etree in imap(lambda c:c.etree(), self.children): |
102 res.append(child_etree) |
102 res.append(child_etree) |
103 |
103 |
104 return res |
104 return res |
105 |
105 |
106 def traverse(self): |
106 def traverse(self): |
107 yield self |
107 yield self |
108 if hasattr(self, "children"): |
108 if hasattr(self, "children"): |
109 for c in self.children: |
109 for c in self.children: |
110 for yoodl in c.traverse(): |
110 for yoodl in c.traverse(): |
111 yield yoodl |
111 yield yoodl |
112 |
112 |
113 |
113 |
114 def hash(self): |
114 def hash(self): |
115 """ Produce a hash, any change in HMI tree structure change that hash """ |
115 """ Produce a hash, any change in HMI tree structure change that hash """ |
116 s = hashlib.new('md5') |
116 s = hashlib.new('md5') |
117 self._hash(s) |
117 self._hash(s) |
118 # limit size to HMI_HASH_SIZE as in svghmi.c |
118 # limit size to HMI_HASH_SIZE as in svghmi.c |
119 return map(ord,s.digest())[:8] |
119 return map(ord,s.digest())[:8] |
120 |
120 |
121 def _hash(self, s): |
121 def _hash(self, s): |
122 s.update(str((self.name,self.nodetype))) |
122 s.update(str((self.name,self.nodetype))) |
123 if hasattr(self, "children"): |
123 if hasattr(self, "children"): |
124 for c in self.children: |
124 for c in self.children: |
125 c._hash(s) |
125 c._hash(s) |
126 |
126 |
127 # module scope for HMITree root |
127 # module scope for HMITree root |
128 # so that CTN can use HMITree deduced in Library |
128 # so that CTN can use HMITree deduced in Library |
129 # note: this only works because library's Generate_C is |
129 # note: this only works because library's Generate_C is |
130 # systematicaly invoked before CTN's CTNGenerate_C |
130 # systematicaly invoked before CTN's CTNGenerate_C |
131 |
131 |
132 hmi_tree_root = None |
132 hmi_tree_root = None |
133 |
133 |
134 hmi_tree_updated = None |
134 on_hmitree_update = None |
135 |
135 |
136 class SVGHMILibrary(POULibrary): |
136 class SVGHMILibrary(POULibrary): |
137 def GetLibraryPath(self): |
137 def GetLibraryPath(self): |
138 return paths.AbsNeighbourFile(__file__, "pous.xml") |
138 return paths.AbsNeighbourFile(__file__, "pous.xml") |
139 |
139 |
140 def Generate_C(self, buildpath, varlist, IECCFLAGS): |
140 def Generate_C(self, buildpath, varlist, IECCFLAGS): |
141 global hmi_tree_root, hmi_tree_updated, hmi_tree_unique_id |
141 global hmi_tree_root, on_hmitree_update, hmi_tree_unique_id |
142 |
142 |
143 """ |
143 """ |
144 PLC Instance Tree: |
144 PLC Instance Tree: |
145 prog0 |
145 prog0 |
146 +->v1 HMI_INT |
146 +->v1 HMI_INT |
177 # Filter known HMI types |
177 # Filter known HMI types |
178 hmi_types_instances = [v for v in varlist if v["derived"] in HMI_TYPES] |
178 hmi_types_instances = [v for v in varlist if v["derived"] in HMI_TYPES] |
179 |
179 |
180 hmi_tree_root = HMITreeNode(None, "/", "HMI_ROOT") |
180 hmi_tree_root = HMITreeNode(None, "/", "HMI_ROOT") |
181 |
181 |
182 # add special nodes |
182 # add special nodes |
183 map(lambda (n,t): hmi_tree_root.children.append(HMITreeNode(None,n,t)), [ |
183 map(lambda (n,t): hmi_tree_root.children.append(HMITreeNode(None,n,t)), [ |
184 ("plc_status", "HMI_PLC_STATUS"), |
184 ("plc_status", "HMI_PLC_STATUS"), |
185 ("current_page", "HMI_CURRENT_PAGE")]) |
185 ("current_page", "HMI_CURRENT_PAGE")]) |
186 |
186 |
187 # deduce HMI tree from PLC HMI_* instances |
187 # deduce HMI tree from PLC HMI_* instances |
198 else: |
198 else: |
199 name = path[-1] |
199 name = path[-1] |
200 new_node = HMITreeNode(path, name, derived, v["type"], v["vartype"], **kwargs) |
200 new_node = HMITreeNode(path, name, derived, v["type"], v["vartype"], **kwargs) |
201 hmi_tree_root.place_node(new_node) |
201 hmi_tree_root.place_node(new_node) |
202 |
202 |
203 if hmi_tree_updated is not None: |
203 if on_hmitree_update is not None: |
204 hmi_tree_updated() |
204 on_hmitree_update() |
205 |
205 |
206 variable_decl_array = [] |
206 variable_decl_array = [] |
207 extern_variables_declarations = [] |
207 extern_variables_declarations = [] |
208 buf_index = 0 |
208 buf_index = 0 |
209 item_count = 0 |
209 item_count = 0 |
241 # C code to observe/access HMI tree variables |
241 # C code to observe/access HMI tree variables |
242 svghmi_c_filepath = paths.AbsNeighbourFile(__file__, "svghmi.c") |
242 svghmi_c_filepath = paths.AbsNeighbourFile(__file__, "svghmi.c") |
243 svghmi_c_file = open(svghmi_c_filepath, 'r') |
243 svghmi_c_file = open(svghmi_c_filepath, 'r') |
244 svghmi_c_code = svghmi_c_file.read() |
244 svghmi_c_code = svghmi_c_file.read() |
245 svghmi_c_file.close() |
245 svghmi_c_file.close() |
246 svghmi_c_code = svghmi_c_code % { |
246 svghmi_c_code = svghmi_c_code % { |
247 "variable_decl_array": ",\n".join(variable_decl_array), |
247 "variable_decl_array": ",\n".join(variable_decl_array), |
248 "extern_variables_declarations": "\n".join(extern_variables_declarations), |
248 "extern_variables_declarations": "\n".join(extern_variables_declarations), |
249 "buffer_size": buf_index, |
249 "buffer_size": buf_index, |
250 "item_count": item_count, |
250 "item_count": item_count, |
251 "var_access_code": targets.GetCode("var_access.c"), |
251 "var_access_code": targets.GetCode("var_access.c"), |
443 # call xslt transform on Inkscape's SVG to generate XHTML |
443 # call xslt transform on Inkscape's SVG to generate XHTML |
444 try: |
444 try: |
445 result = transform.transform(svgdom) |
445 result = transform.transform(svgdom) |
446 except XSLTApplyError as e: |
446 except XSLTApplyError as e: |
447 self.FatalError("SVGHMI " + view_name + ": " + e.message) |
447 self.FatalError("SVGHMI " + view_name + ": " + e.message) |
448 |
448 |
449 result.write(target_file, encoding="utf-8") |
449 result.write(target_file, encoding="utf-8") |
450 # print(str(result)) |
450 # print(str(result)) |
451 # print(transform.xslt.error_log) |
451 # print(transform.xslt.error_log) |
452 |
452 |
453 # TODO |
453 # TODO |
454 # - Errors on HMI semantics |
454 # - Errors on HMI semantics |
455 # - ... maybe something to have a global view of what is declared in SVG. |
455 # - ... maybe something to have a global view of what is declared in SVG. |
456 |
456 |
457 else: |
457 else: |
458 # TODO : use default svg that expose the HMI tree as-is |
458 # TODO : use default svg that expose the HMI tree as-is |
459 target_file.write("""<!DOCTYPE html> |
459 target_file.write("""<!DOCTYPE html> |
460 <html> |
460 <html> |
461 <body> |
461 <body> |
462 <h1> No SVG file provided </h1> |
462 <h1> No SVG file provided </h1> |
463 </body> |
463 </body> |