svghmi/svghmi.py
changeset 3880 89549813a6c1
parent 3878 58bce98b9ac6
child 3900 4df9cfd14a3c
equal deleted inserted replaced
3879:17d0d1641090 3880:89549813a6c1
    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