etherlab/etherlab.py
changeset 2157 a2385e535cf5
parent 2152 e6946c298a42
child 2159 93797d4303a3
equal deleted inserted replaced
2156:c8eee6be2da8 2157:a2385e535cf5
     1 import os, shutil
     1 import os, shutil
     2 from xml.dom import minidom
     2 from lxml import etree
     3 
     3 
     4 import wx
     4 import wx
     5 import csv
     5 import csv
     6 
     6 
     7 from xmlclass import *
     7 from xmlclass import *
    11 
    11 
    12 from EthercatSlave import ExtractHexDecValue, ExtractName
    12 from EthercatSlave import ExtractHexDecValue, ExtractName
    13 from EthercatMaster import _EthercatCTN
    13 from EthercatMaster import _EthercatCTN
    14 from ConfigEditor import LibraryEditor, ETHERCAT_VENDOR, ETHERCAT_GROUP, ETHERCAT_DEVICE
    14 from ConfigEditor import LibraryEditor, ETHERCAT_VENDOR, ETHERCAT_GROUP, ETHERCAT_DEVICE
    15 
    15 
       
    16 ScriptDirectory = os.path.split(os.path.realpath(__file__))[0]
       
    17 
    16 #--------------------------------------------------
    18 #--------------------------------------------------
    17 #                 Ethercat ConfNode
    19 #                 Ethercat ConfNode
    18 #--------------------------------------------------
    20 #--------------------------------------------------
    19 
    21 
    20 EtherCATInfoClasses = GenerateClassesFromXSD(os.path.join(os.path.dirname(__file__), "EtherCATInfo.xsd")) 
    22 EtherCATInfoParser = GenerateParserFromXSD(os.path.join(os.path.dirname(__file__), "EtherCATInfo.xsd")) 
    21 
    23 EtherCATInfo_XPath = lambda xpath: etree.XPath(xpath)
    22 cls = EtherCATInfoClasses["EtherCATBase.xsd"].get("DictionaryType", None)
    24 
       
    25 def extract_param(el):
       
    26     if el.tag == "Index":
       
    27         return "#x%4.4X" % int(el.text)
       
    28     if el.tag == "PDOMapping":
       
    29         if el.text is None:
       
    30             return ""
       
    31         return el.text.upper()
       
    32     if el.text is None:
       
    33         return ""
       
    34     return el.text
       
    35 
       
    36 def extract_pdo_infos(pdo_infos):
       
    37     return {
       
    38         pdo_infos.tag + " " + el.tag: extract_param(el)
       
    39         for el in pdo_infos}
       
    40 
       
    41 def HexDecValue(ctxt, values):
       
    42     return str(ExtractHexDecValue(values[0]))
       
    43 
       
    44 def EntryName(ctxt, values):
       
    45     default=None
       
    46     names = []
       
    47     for element in values:
       
    48         if element.tag == "Default":
       
    49             default = element.text
       
    50         else:
       
    51             names.append(element)
       
    52     return ExtractName(names, default)
       
    53 
       
    54 class AddEntry(etree.XSLTExtension):
       
    55 
       
    56     def __init__(self, entries):
       
    57         etree.XSLTExtension.__init__(self)
       
    58         self.Entries = entries
       
    59     
       
    60     def execute(self, context, self_node, input_node, output_parent):
       
    61         infos = etree.Element('entry_infos')
       
    62         self.process_children(context, infos)
       
    63         index, subindex = map(
       
    64             lambda x: int(infos.find(x).text),
       
    65             ["Index", "SubIndex"])
       
    66         new_entry_infos = {
       
    67             el.tag: extract_param(el)
       
    68             for el in infos if el.tag != "PDO"}
       
    69         if (index, subindex) != (0, 0):
       
    70             entry_infos = self.Entries.get((index, subindex))
       
    71             if entry_infos is not None:
       
    72                 PDO_infos = infos.find("PDO")
       
    73                 if PDO_infos is not None:
       
    74                     entry_infos.update(extract_pdo_infos(PDO_infos))
       
    75             else:
       
    76                 self.Entries[(index, subindex)] = new_entry_infos
       
    77 
       
    78 entries_list_xslt = etree.parse(
       
    79     os.path.join(ScriptDirectory, "entries_list.xslt"))
       
    80 
       
    81 cls = EtherCATInfoParser.GetElementClass("DeviceType")
    23 if cls:
    82 if cls:
    24     cls.loadXMLTreeArgs = None
    83     
    25     
    84     profile_numbers_xpath = EtherCATInfo_XPath("Profile/ProfileNo")
    26     setattr(cls, "_loadXMLTree", getattr(cls, "loadXMLTree"))
       
    27     
       
    28     def loadXMLTree(self, *args):
       
    29         self.loadXMLTreeArgs = args
       
    30     setattr(cls, "loadXMLTree", loadXMLTree)
       
    31 
       
    32     def load(self):
       
    33         if self.loadXMLTreeArgs is not None:
       
    34             self._loadXMLTree(*self.loadXMLTreeArgs)
       
    35             self.loadXMLTreeArgs = None
       
    36     setattr(cls, "load", load)
       
    37 
       
    38 cls = EtherCATInfoClasses["EtherCATInfo.xsd"].get("DeviceType", None)
       
    39 if cls:
       
    40     cls.DataTypes = None
       
    41     
       
    42     def GetProfileNumbers(self):
    85     def GetProfileNumbers(self):
    43         profiles = []
    86         return [number.text for number in profile_numbers_xpath(self)]
    44         
       
    45         for profile in self.getProfile():
       
    46             profile_content = profile.getcontent()
       
    47             if profile_content is None:
       
    48                 continue
       
    49             
       
    50             for content_element in profile_content["value"]:
       
    51                 if content_element["name"] == "ProfileNo":
       
    52                     profiles.append(content_element["value"])
       
    53         
       
    54         return profiles
       
    55     setattr(cls, "GetProfileNumbers", GetProfileNumbers)
    87     setattr(cls, "GetProfileNumbers", GetProfileNumbers)
    56     
       
    57     def GetProfileDictionaries(self):
       
    58         dictionaries = []
       
    59         
       
    60         for profile in self.getProfile():
       
    61         
       
    62             profile_content = profile.getcontent()
       
    63             if profile_content is None:
       
    64                 continue
       
    65             
       
    66             for content_element in profile_content["value"]:
       
    67                 if content_element["name"] == "Dictionary":
       
    68                     dictionaries.append(content_element["value"])
       
    69                 elif content_element["name"] == "DictionaryFile":
       
    70                     raise ValueError, "DictionaryFile for defining Device Profile is not yet supported!"
       
    71                 
       
    72         return dictionaries
       
    73     setattr(cls, "GetProfileDictionaries", GetProfileDictionaries)
       
    74     
       
    75     def ExtractDataTypes(self):
       
    76         self.DataTypes = {}
       
    77         
       
    78         for dictionary in self.GetProfileDictionaries():
       
    79             dictionary.load()
       
    80             
       
    81             datatypes = dictionary.getDataTypes()
       
    82             if datatypes is not None:
       
    83                 
       
    84                 for datatype in datatypes.getDataType():
       
    85                     content = datatype.getcontent()
       
    86                     if content is not None and content["name"] == "SubItem":
       
    87                         self.DataTypes[datatype.getName()] = datatype
       
    88     
       
    89     setattr(cls, "ExtractDataTypes", ExtractDataTypes)
       
    90     
    88     
    91     def getCoE(self):
    89     def getCoE(self):
    92         mailbox = self.getMailbox()
    90         mailbox = self.getMailbox()
    93         if mailbox is not None:
    91         if mailbox is not None:
    94             return mailbox.getCoE()
    92             return mailbox.getCoE()
    95         return None
    93         return None
    96     setattr(cls, "getCoE", getCoE)
    94     setattr(cls, "getCoE", getCoE)
    97 
    95 
    98     def GetEntriesList(self, limits=None):
    96     def GetEntriesList(self, limits=None):
    99         if self.DataTypes is None:
       
   100             self.ExtractDataTypes()
       
   101         
       
   102         entries = {}
    97         entries = {}
   103         
    98         
   104         for dictionary in self.GetProfileDictionaries():
    99         entries_list_xslt_tree = etree.XSLT(
   105             dictionary.load()
   100             entries_list_xslt, extensions = {
   106             
   101                 ("entries_list_ns", "add_entry"): AddEntry(entries),
   107             for object in dictionary.getObjects().getObject():
   102                 ("entries_list_ns", "HexDecValue"): HexDecValue,
   108                 entry_index = object.getIndex().getcontent()
   103                 ("entries_list_ns", "EntryName"): EntryName})
   109                 index = ExtractHexDecValue(entry_index)
   104         entries_list_xslt_tree(self, **dict(zip(
   110                 if limits is None or limits[0] <= index <= limits[1]:
   105             ["min_index", "max_index"], 
   111                     entry_type = object.getType()
   106             map(lambda x: etree.XSLT.strparam(str(x)),
   112                     entry_name = ExtractName(object.getName())
   107                 limits if limits is not None else [0x0000, 0xFFFF])
   113                     
   108             )))
   114                     entry_type_infos = self.DataTypes.get(entry_type, None)
       
   115                     if entry_type_infos is not None:
       
   116                         content = entry_type_infos.getcontent()
       
   117                         for subitem in content["value"]:
       
   118                             entry_subidx = subitem.getSubIdx()
       
   119                             if entry_subidx is None:
       
   120                                 entry_subidx = "0"
       
   121                             subidx = ExtractHexDecValue(entry_subidx)
       
   122                             subitem_access = ""
       
   123                             subitem_pdomapping = ""
       
   124                             subitem_flags = subitem.getFlags()
       
   125                             if subitem_flags is not None:
       
   126                                 access = subitem_flags.getAccess()
       
   127                                 if access is not None:
       
   128                                     subitem_access = access.getcontent()
       
   129                                 pdomapping = subitem_flags.getPdoMapping()
       
   130                                 if pdomapping is not None:
       
   131                                     subitem_pdomapping = pdomapping.upper()
       
   132                             entries[(index, subidx)] = {
       
   133                                 "Index": entry_index,
       
   134                                 "SubIndex": entry_subidx,
       
   135                                 "Name": "%s - %s" % 
       
   136                                         (entry_name.decode("utf-8"),
       
   137                                          ExtractName(subitem.getDisplayName(), 
       
   138                                                      subitem.getName()).decode("utf-8")),
       
   139                                 "Type": subitem.getType(),
       
   140                                 "BitSize": subitem.getBitSize(),
       
   141                                 "Access": subitem_access, 
       
   142                                 "PDOMapping": subitem_pdomapping}
       
   143                     else:
       
   144                         entry_access = ""
       
   145                         entry_pdomapping = ""
       
   146                         entry_flags = object.getFlags()
       
   147                         if entry_flags is not None:
       
   148                             access = entry_flags.getAccess()
       
   149                             if access is not None:
       
   150                                 entry_access = access.getcontent()
       
   151                             pdomapping = entry_flags.getPdoMapping()
       
   152                             if pdomapping is not None:
       
   153                                 entry_pdomapping = pdomapping.upper()
       
   154                         entries[(index, 0)] = {
       
   155                              "Index": entry_index,
       
   156                              "SubIndex": "0",
       
   157                              "Name": entry_name,
       
   158                              "Type": entry_type,
       
   159                              "BitSize": object.getBitSize(),
       
   160                              "Access": entry_access,
       
   161                              "PDOMapping": entry_pdomapping}
       
   162 
       
   163         for TxPdo in self.getTxPdo():
       
   164             ExtractPdoInfos(TxPdo, "Transmit", entries, limits)
       
   165         for RxPdo in self.getRxPdo():
       
   166             ExtractPdoInfos(RxPdo, "Receive", entries, limits)
       
   167         
   109         
   168         return entries
   110         return entries
   169     setattr(cls, "GetEntriesList", GetEntriesList)
   111     setattr(cls, "GetEntriesList", GetEntriesList)
   170     
   112 
   171     def GetSyncManagers(self):
   113     def GetSyncManagers(self):
   172         sync_managers = []
   114         sync_managers = []
   173         for sync_manager in self.getSm():
   115         for sync_manager in self.getSm():
   174             sync_manager_infos = {}
   116             sync_manager_infos = {}
   175             for name, value in [("Name", sync_manager.getcontent()),
   117             for name, value in [("Name", sync_manager.getcontent()),
   197 def SortGroupItems(group):
   139 def SortGroupItems(group):
   198     for item in group["children"]:
   140     for item in group["children"]:
   199         if item["type"] == ETHERCAT_GROUP:
   141         if item["type"] == ETHERCAT_GROUP:
   200             SortGroupItems(item)
   142             SortGroupItems(item)
   201     group["children"].sort(GroupItemCompare)
   143     group["children"].sort(GroupItemCompare)
   202 
       
   203 def ExtractPdoInfos(pdo, pdo_type, entries, limits=None):
       
   204     pdo_index = pdo.getIndex().getcontent()
       
   205     pdo_name = ExtractName(pdo.getName())
       
   206     for pdo_entry in pdo.getEntry():
       
   207         entry_index = pdo_entry.getIndex().getcontent()
       
   208         entry_subindex = pdo_entry.getSubIndex()
       
   209         index = ExtractHexDecValue(entry_index)
       
   210         subindex = ExtractHexDecValue(entry_subindex)
       
   211         
       
   212         if limits is None or limits[0] <= index <= limits[1]:
       
   213             entry = entries.get((index, subindex), None)
       
   214             if entry is not None:
       
   215                 entry["PDO index"] = pdo_index
       
   216                 entry["PDO name"] = pdo_name
       
   217                 entry["PDO type"] = pdo_type
       
   218             else:
       
   219                 entry_type = pdo_entry.getDataType()
       
   220                 if entry_type is not None:
       
   221                     if pdo_type == "Transmit":
       
   222                         access = "ro"
       
   223                         pdomapping = "T"
       
   224                     else:
       
   225                         access = "wo"
       
   226                         pdomapping = "R"
       
   227                     entries[(index, subindex)] = {
       
   228                         "Index": entry_index,
       
   229                         "SubIndex": entry_subindex,
       
   230                         "Name": ExtractName(pdo_entry.getName()),
       
   231                         "Type": entry_type.getcontent(),
       
   232                         "Access": access,
       
   233                         "PDOMapping": pdomapping}
       
   234 
   144 
   235 class ModulesLibrary:
   145 class ModulesLibrary:
   236 
   146 
   237     MODULES_EXTRA_PARAMS = [
   147     MODULES_EXTRA_PARAMS = [
   238         ("pdo_alignment", {
   148         ("pdo_alignment", {
   274         return self.Path
   184         return self.Path
   275     
   185     
   276     def GetModulesExtraParamsFilePath(self):
   186     def GetModulesExtraParamsFilePath(self):
   277         return os.path.join(self.Path, "modules_extra_params.cfg")
   187         return os.path.join(self.Path, "modules_extra_params.cfg")
   278     
   188     
       
   189     groups_xpath = EtherCATInfo_XPath("Descriptions/Groups/Group")
       
   190     devices_xpath = EtherCATInfo_XPath("Descriptions/Devices/Device")
   279     def LoadModules(self):
   191     def LoadModules(self):
   280         self.Library = {}
   192         self.Library = {}
   281         
   193         
   282         files = os.listdir(self.Path)
   194         files = os.listdir(self.Path)
   283         for file in files:
   195         for file in files:
   284             filepath = os.path.join(self.Path, file)
   196             filepath = os.path.join(self.Path, file)
   285             if os.path.isfile(filepath) and os.path.splitext(filepath)[-1] == ".xml":
   197             if os.path.isfile(filepath) and os.path.splitext(filepath)[-1] == ".xml":
       
   198                 self.modules_infos = None
       
   199                 
   286                 xmlfile = open(filepath, 'r')
   200                 xmlfile = open(filepath, 'r')
   287                 xml_tree = minidom.parse(xmlfile)
   201                 try:
       
   202                     self.modules_infos = etree.fromstring(
       
   203                         xmlfile.read(), EtherCATInfoParser)
       
   204                 except:
       
   205                     pass
   288                 xmlfile.close()
   206                 xmlfile.close()
   289                 
       
   290                 self.modules_infos = None
       
   291                 for child in xml_tree.childNodes:
       
   292                     if child.nodeType == xml_tree.ELEMENT_NODE and child.nodeName == "EtherCATInfo":
       
   293                         self.modules_infos = EtherCATInfoClasses["EtherCATInfo.xsd"]["EtherCATInfo"]()
       
   294                         self.modules_infos.loadXMLTree(child)
       
   295                 
   207                 
   296                 if self.modules_infos is not None:
   208                 if self.modules_infos is not None:
   297                     vendor = self.modules_infos.getVendor()
   209                     vendor = self.modules_infos.getVendor()
   298                     
   210                     
   299                     vendor_category = self.Library.setdefault(ExtractHexDecValue(vendor.getId()), 
   211                     vendor_category = self.Library.setdefault(
   300                                                               {"name": ExtractName(vendor.getName(), _("Miscellaneous")), 
   212                         ExtractHexDecValue(vendor.getId()), 
   301                                                                "groups": {}})
   213                         {"name": ExtractName(vendor.getName(), _("Miscellaneous")), 
       
   214                          "groups": {}})
   302                     
   215                     
   303                     for group in self.modules_infos.getDescriptions().getGroups().getGroup():
   216                     for group in self.groups_xpath(self.modules_infos):
   304                         group_type = group.getType()
   217                         group_type = group.getType()
   305                         
   218                         
   306                         vendor_category["groups"].setdefault(group_type, {"name": ExtractName(group.getName(), group_type), 
   219                         vendor_category["groups"].setdefault(group_type, 
   307                                                                           "parent": group.getParentGroup(),
   220                             {"name": ExtractName(group.getName(), group_type), 
   308                                                                           "order": group.getSortOrder(), 
   221                              "parent": group.getParentGroup(),
   309                                                                           "value": group.getcontent()["value"],
   222                              "order": group.getSortOrder(), 
   310                                                                           "devices": []})
   223                              #"value": group.getcontent()["value"],
       
   224                              "devices": []})
   311                     
   225                     
   312                     for device in self.modules_infos.getDescriptions().getDevices().getDevice():
   226                     for device in self.devices_xpath(self.modules_infos):
   313                         device_group = device.getGroupType()
   227                         device_group = device.getGroupType()
   314                         if not vendor_category["groups"].has_key(device_group):
   228                         if not vendor_category["groups"].has_key(device_group):
   315                             raise ValueError, "Not such group \"%\"" % device_group
   229                             raise ValueError, "Not such group \"%\"" % device_group
   316                         vendor_category["groups"][device_group]["devices"].append((device.getType().getcontent(), device))
   230                         vendor_category["groups"][device_group]["devices"].append(
       
   231                             (device.getType().getcontent(), device))
   317 
   232 
   318         return self.Library 
   233         return self.Library 
   319 
   234 
   320     def GetModulesLibrary(self, profile_filter=None):
   235     def GetModulesLibrary(self, profile_filter=None):
   321         if self.Library is None:
   236         if self.Library is None: