42 # module scope for HMITree root |
42 # module scope for HMITree root |
43 # so that CTN can use HMITree deduced in Library |
43 # so that CTN can use HMITree deduced in Library |
44 # note: this only works because library's Generate_C is |
44 # note: this only works because library's Generate_C is |
45 # systematicaly invoked before CTN's CTNGenerate_C |
45 # systematicaly invoked before CTN's CTNGenerate_C |
46 |
46 |
47 hmi_tree_root = None |
|
48 |
|
49 on_hmitree_update = None |
|
50 |
|
51 maxConnectionsTotal = 0 |
|
52 |
47 |
53 class SVGHMILibrary(POULibrary): |
48 class SVGHMILibrary(POULibrary): |
|
49 |
|
50 hmi_tree_root = None |
|
51 |
|
52 maxConnectionsTotal = 0 |
|
53 |
54 def GetLibraryPath(self): |
54 def GetLibraryPath(self): |
55 return paths.AbsNeighbourFile(__file__, "pous.xml") |
55 return paths.AbsNeighbourFile(__file__, "pous.xml") |
56 |
56 |
57 def Generate_C(self, buildpath, varlist, IECCFLAGS): |
57 def Generate_C(self, buildpath, varlist, IECCFLAGS): |
58 global hmi_tree_root, on_hmitree_update, maxConnectionsTotal |
58 |
59 |
59 self.maxConnectionsTotal = 0 |
60 maxConnectionsTotal = 0 |
|
61 |
60 |
62 already_found_watchdog = False |
61 already_found_watchdog = False |
63 found_SVGHMI_instance = False |
62 found_SVGHMI_instance = False |
64 for CTNChild in self.GetCTR().IterChildren(): |
63 for CTNChild in self.GetCTR().IterChildren(): |
65 if isinstance(CTNChild, SVGHMI): |
64 if isinstance(CTNChild, SVGHMI): |
66 found_SVGHMI_instance = True |
65 found_SVGHMI_instance = True |
67 # collect maximum connection total for all svghmi nodes |
66 # collect maximum connection total for all svghmi nodes |
68 maxConnectionsTotal += CTNChild.GetParamsAttributes("SVGHMI.MaxConnections")["value"] |
67 self.maxConnectionsTotal += CTNChild.GetParamsAttributes("SVGHMI.MaxConnections")["value"] |
69 |
68 |
70 # spot watchdog abuse |
69 # spot watchdog abuse |
71 if CTNChild.GetParamsAttributes("SVGHMI.EnableWatchdog")["value"]: |
70 if CTNChild.GetParamsAttributes("SVGHMI.EnableWatchdog")["value"]: |
72 if already_found_watchdog: |
71 if already_found_watchdog: |
73 self.FatalError("SVGHMI: Only one watchdog enabled HMI allowed") |
72 self.FatalError("SVGHMI: Only one watchdog enabled HMI allowed") |
112 """ |
111 """ |
113 |
112 |
114 # Filter known HMI types |
113 # Filter known HMI types |
115 hmi_types_instances = [v for v in varlist if v["derived"] in HMI_TYPES] |
114 hmi_types_instances = [v for v in varlist if v["derived"] in HMI_TYPES] |
116 |
115 |
117 hmi_tree_root = None |
116 self.hmi_tree_root = None |
118 |
117 |
119 # take first HMI_NODE (placed as special node), make it root |
118 # take first HMI_NODE (placed as special node), make it root |
120 for i,v in enumerate(hmi_types_instances): |
119 for i,v in enumerate(hmi_types_instances): |
121 path = v["IEC_path"].split(".") |
120 path = v["IEC_path"].split(".") |
122 derived = v["derived"] |
121 derived = v["derived"] |
123 if derived == "HMI_NODE": |
122 if derived == "HMI_NODE": |
124 hmi_tree_root = HMITreeNode(path, "", derived, v["type"], v["vartype"], v["C_path"]) |
123 self.hmi_tree_root = HMITreeNode(path, "", derived, v["type"], v["vartype"], v["C_path"]) |
125 hmi_types_instances.pop(i) |
124 hmi_types_instances.pop(i) |
126 break |
125 break |
127 |
126 |
128 # deduce HMI tree from PLC HMI_* instances |
127 # deduce HMI tree from PLC HMI_* instances |
129 for v in hmi_types_instances: |
128 for v in hmi_types_instances: |
142 name = path[-2] |
141 name = path[-2] |
143 kwargs['hmiclass'] = path[-1] |
142 kwargs['hmiclass'] = path[-1] |
144 else: |
143 else: |
145 name = path[-1] |
144 name = path[-1] |
146 new_node = HMITreeNode(path, name, derived, v["type"], vartype, v["C_path"], **kwargs) |
145 new_node = HMITreeNode(path, name, derived, v["type"], vartype, v["C_path"], **kwargs) |
147 placement_result = hmi_tree_root.place_node(new_node) |
146 placement_result = self.hmi_tree_root.place_node(new_node) |
148 if placement_result is not None: |
147 if placement_result is not None: |
149 cause, problematic_node = placement_result |
148 cause, problematic_node = placement_result |
150 if cause == "Non_Unique": |
149 if cause == "Non_Unique": |
151 message = _("HMI tree nodes paths are not unique.\nConflicting variable: {} {}").format( |
150 message = _("HMI tree nodes paths are not unique.\nConflicting variable: {} {}").format( |
152 ".".join(problematic_node.path), |
151 ".".join(problematic_node.path), |
169 ".".join(problematic_node.path), |
168 ".".join(problematic_node.path), |
170 ".".join(new_node.path)) |
169 ".".join(new_node.path)) |
171 |
170 |
172 self.FatalError("SVGHMI : " + message) |
171 self.FatalError("SVGHMI : " + message) |
173 |
172 |
174 if on_hmitree_update is not None: |
173 self.on_hmitree_update() |
175 on_hmitree_update(hmi_tree_root) |
|
176 |
174 |
177 variable_decl_array = [] |
175 variable_decl_array = [] |
178 extern_variables_declarations = [] |
176 extern_variables_declarations = [] |
179 buf_index = 0 |
177 buf_index = 0 |
180 item_count = 0 |
178 item_count = 0 |
181 found_heartbeat = False |
179 found_heartbeat = False |
182 |
180 |
183 hearbeat_IEC_path = ['CONFIG', 'HEARTBEAT'] |
181 hearbeat_IEC_path = ['CONFIG', 'HEARTBEAT'] |
184 |
182 |
185 for node in hmi_tree_root.traverse(): |
183 for node in self.hmi_tree_root.traverse(): |
186 if not found_heartbeat and node.path == hearbeat_IEC_path: |
184 if not found_heartbeat and node.path == hearbeat_IEC_path: |
187 hmi_tree_hearbeat_index = item_count |
185 hmi_tree_hearbeat_index = item_count |
188 found_heartbeat = True |
186 found_heartbeat = True |
189 extern_variables_declarations += [ |
187 extern_variables_declarations += [ |
190 "#define heartbeat_index "+str(hmi_tree_hearbeat_index) |
188 "#define heartbeat_index "+str(hmi_tree_hearbeat_index) |
230 "extern_variables_declarations": "\n".join(extern_variables_declarations), |
228 "extern_variables_declarations": "\n".join(extern_variables_declarations), |
231 "buffer_size": buf_index, |
229 "buffer_size": buf_index, |
232 "item_count": item_count, |
230 "item_count": item_count, |
233 "var_access_code": targets.GetCode("var_access.c"), |
231 "var_access_code": targets.GetCode("var_access.c"), |
234 "PLC_ticktime": self.GetCTR().GetTicktime(), |
232 "PLC_ticktime": self.GetCTR().GetTicktime(), |
235 "hmi_hash_ints": ",".join(map(str,hmi_tree_root.hash())), |
233 "hmi_hash_ints": ",".join(map(str,self.hmi_tree_root.hash())), |
236 "max_connections": maxConnectionsTotal |
234 "max_connections": self.maxConnectionsTotal |
237 } |
235 } |
238 |
236 |
239 gen_svghmi_c_path = os.path.join(buildpath, "svghmi.c") |
237 gen_svghmi_c_path = os.path.join(buildpath, "svghmi.c") |
240 gen_svghmi_c = open(gen_svghmi_c_path, 'w') |
238 gen_svghmi_c = open(gen_svghmi_c_path, 'w') |
241 gen_svghmi_c.write(svghmi_c_code) |
239 gen_svghmi_c.write(svghmi_c_code) |
252 runtimefile.close() |
250 runtimefile.close() |
253 |
251 |
254 # Backup HMI Tree in XML form so that it can be loaded without building |
252 # Backup HMI Tree in XML form so that it can be loaded without building |
255 hmitree_backup_path = os.path.join(buildpath, "hmitree.xml") |
253 hmitree_backup_path = os.path.join(buildpath, "hmitree.xml") |
256 hmitree_backup_file = open(hmitree_backup_path, 'wb') |
254 hmitree_backup_file = open(hmitree_backup_path, 'wb') |
257 hmitree_backup_file.write(etree.tostring(hmi_tree_root.etree())) |
255 hmitree_backup_file.write(etree.tostring(self.hmi_tree_root.etree())) |
258 hmitree_backup_file.close() |
256 hmitree_backup_file.close() |
259 |
257 |
260 return ((["svghmi"], [(gen_svghmi_c_path, IECCFLAGS)], True), "", |
258 return ((["svghmi"], [(gen_svghmi_c_path, IECCFLAGS)], True), "", |
261 ("runtime_00_svghmi.py", open(runtimefile_path, "rb"))) |
259 ("runtime_00_svghmi.py", open(runtimefile_path, "rb"))) |
262 # ^ |
260 # ^ |
266 def GlobalInstances(self): |
264 def GlobalInstances(self): |
267 """ Adds HMI tree root and hearbeat to PLC Configuration's globals """ |
265 """ Adds HMI tree root and hearbeat to PLC Configuration's globals """ |
268 return [(name, iec_type, "") for name, iec_type in SPECIAL_NODES] |
266 return [(name, iec_type, "") for name, iec_type in SPECIAL_NODES] |
269 |
267 |
270 |
268 |
271 |
269 registered_uis = [] |
272 def Register_SVGHMI_UI_for_HMI_tree_updates(ref): |
270 def on_hmitree_update(self): |
273 global on_hmitree_update |
271 for uiref in self.registered_uis[:]: |
274 def HMITreeUpdate(_hmi_tree_root): |
272 obj = uiref() |
275 obj = ref() |
273 if obj is None: |
276 if obj is not None: |
274 self.registered_uis.remove(uiref) |
277 obj.HMITreeUpdate(_hmi_tree_root) |
275 else: |
278 |
276 obj.HMITreeUpdate(self.hmi_tree_root) |
279 on_hmitree_update = HMITreeUpdate |
277 |
|
278 |
|
279 def Register_SVGHMI_UI_for_HMI_tree_updates(self, uiref): |
|
280 self.registered_uis.append(uiref) |
280 |
281 |
281 |
282 |
282 class SVGHMIEditor(ConfTreeNodeEditor): |
283 class SVGHMIEditor(ConfTreeNodeEditor): |
283 CONFNODEEDITOR_TABS = [ |
284 CONFNODEEDITOR_TABS = [ |
284 (_("HMI Tree"), "CreateSVGHMI_UI")] |
285 (_("HMI Tree"), "CreateSVGHMI_UI")] |
286 def __init__(self, parent, controler, window): |
287 def __init__(self, parent, controler, window): |
287 ConfTreeNodeEditor.__init__(self, parent, controler, window) |
288 ConfTreeNodeEditor.__init__(self, parent, controler, window) |
288 self.Controler = controler |
289 self.Controler = controler |
289 |
290 |
290 def CreateSVGHMI_UI(self, parent): |
291 def CreateSVGHMI_UI(self, parent): |
291 global hmi_tree_root |
292 ctroot = self.Controler.GetCTRoot() |
292 |
293 svghmilib = ctroot.Libraries["SVGHMI"] |
293 if hmi_tree_root is None: |
294 |
294 buildpath = self.Controler.GetCTRoot()._getBuildPath() |
295 if svghmilib.hmi_tree_root is None: |
|
296 buildpath = ctroot._getBuildPath() |
295 hmitree_backup_path = os.path.join(buildpath, "hmitree.xml") |
297 hmitree_backup_path = os.path.join(buildpath, "hmitree.xml") |
296 if os.path.exists(hmitree_backup_path): |
298 if os.path.exists(hmitree_backup_path): |
297 hmitree_backup_file = open(hmitree_backup_path, 'rb') |
299 hmitree_backup_file = open(hmitree_backup_path, 'rb') |
298 hmi_tree_root = HMITreeNode.from_etree(etree.parse(hmitree_backup_file).getroot()) |
300 svghmilib.hmi_tree_root = HMITreeNode.from_etree(etree.parse(hmitree_backup_file).getroot()) |
299 |
301 |
300 ret = SVGHMI_UI(parent, self.Controler, Register_SVGHMI_UI_for_HMI_tree_updates) |
302 ret = SVGHMI_UI(parent, self.Controler, svghmilib.Register_SVGHMI_UI_for_HMI_tree_updates) |
301 |
303 |
302 on_hmitree_update(hmi_tree_root) |
304 svghmilib.on_hmitree_update() |
303 |
305 |
304 return ret |
306 return ret |
305 |
307 |
306 if sys.platform.startswith('win'): |
308 if sys.platform.startswith('win'): |
307 default_cmds={ |
309 default_cmds={ |
448 |
450 |
449 self.ProgressEnd("inkscape") |
451 self.ProgressEnd("inkscape") |
450 return res |
452 return res |
451 |
453 |
452 def GetHMITree(self): |
454 def GetHMITree(self): |
453 global hmi_tree_root |
455 ctroot = self.GetCTRoot() |
|
456 svghmilib = ctroot.Libraries["SVGHMI"] |
454 self.ProgressStart("hmitree", "getting HMI tree") |
457 self.ProgressStart("hmitree", "getting HMI tree") |
455 res = [hmi_tree_root.etree(add_hash=True)] |
458 res = [svghmilib.hmi_tree_root.etree(add_hash=True)] |
456 self.ProgressEnd("hmitree") |
459 self.ProgressEnd("hmitree") |
457 return res |
460 return res |
458 |
461 |
459 def GetTranslations(self, _context, msgs): |
462 def GetTranslations(self, _context, msgs): |
460 self.ProgressStart("i18n", "getting Translations") |
463 self.ProgressStart("i18n", "getting Translations") |
525 path=path, |
528 path=path, |
526 enable_watchdog=enable_watchdog, |
529 enable_watchdog=enable_watchdog, |
527 url=url) |
530 url=url) |
528 |
531 |
529 def CTNGenerate_C(self, buildpath, locations): |
532 def CTNGenerate_C(self, buildpath, locations): |
530 global hmi_tree_root |
533 ctroot = self.GetCTRoot() |
|
534 svghmilib = ctroot.Libraries["SVGHMI"] |
|
535 hmi_tree_root = svghmilib.hmi_tree_root |
|
536 |
531 |
537 |
532 if hmi_tree_root is None: |
538 if hmi_tree_root is None: |
533 self.FatalError("SVGHMI : Library is not selected. Please select it in project config.") |
539 self.FatalError("SVGHMI : Library is not selected. Please select it in project config.") |
534 |
540 |
535 location_str = "_".join(map(str, self.GetCurrentLocation())) |
541 location_str = "_".join(map(str, self.GetCurrentLocation())) |
543 |
549 |
544 build_path = self._getBuildPath() |
550 build_path = self._getBuildPath() |
545 target_path = os.path.join(build_path, target_fname) |
551 target_path = os.path.join(build_path, target_fname) |
546 hash_path = os.path.join(build_path, "svghmi_"+location_str+".md5") |
552 hash_path = os.path.join(build_path, "svghmi_"+location_str+".md5") |
547 |
553 |
548 self.GetCTRoot().logger.write("SVGHMI:\n") |
554 ctroot.logger.write("SVGHMI:\n") |
549 |
555 |
550 if os.path.exists(svgfile): |
556 if os.path.exists(svgfile): |
551 |
557 |
552 hasher = hashlib.md5() |
558 hasher = hashlib.md5() |
553 hmi_tree_root._hash(hasher) |
559 hmi_tree_root._hash(hasher) |
727 xhtml=target_fname, |
733 xhtml=target_fname, |
728 svghmi_cmds=svghmi_cmds, |
734 svghmi_cmds=svghmi_cmds, |
729 watchdog_initial = self.GetParamsAttributes("SVGHMI.WatchdogInitial")["value"], |
735 watchdog_initial = self.GetParamsAttributes("SVGHMI.WatchdogInitial")["value"], |
730 watchdog_interval = self.GetParamsAttributes("SVGHMI.WatchdogInterval")["value"], |
736 watchdog_interval = self.GetParamsAttributes("SVGHMI.WatchdogInterval")["value"], |
731 maxConnections = self.GetParamsAttributes("SVGHMI.MaxConnections")["value"], |
737 maxConnections = self.GetParamsAttributes("SVGHMI.MaxConnections")["value"], |
732 maxConnections_total = maxConnectionsTotal, |
738 maxConnections_total = svghmilib.maxConnectionsTotal, |
733 **svghmi_options |
739 **svghmi_options |
734 )) |
740 )) |
735 |
741 |
736 runtimefile.close() |
742 runtimefile.close() |
737 |
743 |