# HG changeset patch # User Laurent Bessard # Date 1379976501 -7200 # Node ID a2385e535cf51e6d842312e08163fac63831db0c # Parent c8eee6be2da8d03274d0b42d189b5f6e939ede80 Fixed controllers for working with latest version of xmlclass diff -r c8eee6be2da8 -r a2385e535cf5 etherlab/EthercatMaster.py --- a/etherlab/EthercatMaster.py Tue Jun 25 00:55:38 2013 +0200 +++ b/etherlab/EthercatMaster.py Tue Sep 24 00:48:21 2013 +0200 @@ -1,6 +1,7 @@ import os import cPickle -from xml.dom import minidom +from lxml import etree +from copy import deepcopy import wx @@ -81,14 +82,14 @@ # Ethercat MASTER #-------------------------------------------------- -EtherCATConfigClasses = GenerateClassesFromXSD(os.path.join(os.path.dirname(__file__), "EtherCATConfig.xsd")) +EtherCATConfigParser = GenerateParserFromXSD(os.path.join(os.path.dirname(__file__), "EtherCATConfig.xsd")) def sort_commands(x, y): if x["Index"] == y["Index"]: return cmp(x["Subindex"], y["Subindex"]) return cmp(x["Index"], y["Index"]) -cls = EtherCATConfigClasses.get("Config_Slave", None) +cls = EtherCATConfigParser.GetElementClass("Slave", "Config") if cls: def getType(self): @@ -152,12 +153,12 @@ def appendStartupCommand(self, command_infos): InitCmds = self.getInitCmds(True) - command = EtherCATConfigClasses["InitCmds_InitCmd"]() + command = EtherCATConfigParser.CreateElement("InitCmd", "InitCmds", 1) + InitCmds.appendInitCmd(command) command.setIndex(command_infos["Index"]) command.setSubIndex(command_infos["Subindex"]) command.setData(command_infos["Value"]) command.setComment(command_infos["Description"]) - InitCmds.appendInitCmd(command) return len(InitCmds.getInitCmd()) - 1 setattr(cls, "appendStartupCommand", appendStartupCommand) @@ -206,7 +207,7 @@ """ -ProcessVariablesClasses = GenerateClassesFromXSDstring(ProcessVariablesXSD) +ProcessVariablesParser = GenerateParserFromXSDstring(ProcessVariablesXSD) class _EthercatCTN: @@ -218,29 +219,27 @@ def __init__(self): config_filepath = self.ConfigFileName() config_is_saved = False - self.Config = EtherCATConfigClasses["EtherCATConfig"]() if os.path.isfile(config_filepath): config_xmlfile = open(config_filepath, 'r') - config_tree = minidom.parse(config_xmlfile) + self.Config = etree.fromstring( + config_xmlfile.read(), EtherCATConfigParser) + config_is_saved = True config_xmlfile.close() - - for child in config_tree.childNodes: - if child.nodeType == config_tree.ELEMENT_NODE and child.nodeName == "EtherCATConfig": - self.Config.loadXMLTree(child) - config_is_saved = True + + else: + self.Config = EtherCATConfigParser.CreateElement("EtherCATConfig") process_filepath = self.ProcessVariablesFileName() process_is_saved = False - self.ProcessVariables = ProcessVariablesClasses["ProcessVariables"]() if os.path.isfile(process_filepath): process_xmlfile = open(process_filepath, 'r') - process_tree = minidom.parse(process_xmlfile) + self.ProcessVariables = etree.fromstring( + process_xmlfile.read(), ProcessVariablesParser) + process_is_saved = True process_xmlfile.close() - for child in process_tree.childNodes: - if child.nodeType == process_tree.ELEMENT_NODE and child.nodeName == "ProcessVariables": - self.ProcessVariables.loadXMLTree(child) - process_is_saved = True + else: + self.ProcessVariables = ProcessVariablesParser.CreateElement("ProcessVariables") if config_is_saved and process_is_saved: self.CreateBuffer(True) @@ -352,7 +351,7 @@ def SetProcessVariables(self, variables): vars = [] for var in variables: - variable = ProcessVariablesClasses["ProcessVariables_variable"]() + variable = ProcessVariablesParser.CreateElement("variable", "ProcessVariables") variable.setName(var["Name"]) variable.setComment(var["Description"]) if var["ReadFrom"] != "": @@ -456,12 +455,12 @@ slave = self.GetSlave(newConfNodeOpj.BaseParams.getIEC_Channel()) if slave is None: - slave = EtherCATConfigClasses["Config_Slave"]() + slave = EtherCATConfigParser.CreateElement("Slave", "Config") + self.Config.getConfig().appendSlave(slave) slave_infos = slave.getInfo() slave_infos.setName("undefined") slave_infos.setPhysAddr(newConfNodeOpj.BaseParams.getIEC_Channel()) slave_infos.setAutoIncAddr(0) - self.Config.getConfig().appendSlave(slave) self.BufferModel() self.OnCTNSave() @@ -542,6 +541,7 @@ type_infos = slave.getType() device, module_extra_params = self.GetModuleInfos(type_infos) if device is not None: + print "Get Entries List", limits entries = device.GetEntriesList(limits) #print entries entries_list = entries.items() @@ -647,23 +647,22 @@ def OnCTNSave(self, from_project_path=None): config_filepath = self.ConfigFileName() - config_text = "\n" - config_extras = {"xmlns:xsi":"http://www.w3.org/2001/XMLSchema-instance", - "xsi:noNamespaceSchemaLocation" : "EtherCATInfo.xsd"} - config_text += self.Config.generateXMLText("EtherCATConfig", 0, config_extras) - config_xmlfile = open(config_filepath,"w") - config_xmlfile.write(config_text.encode("utf-8")) + config_xmlfile.write(etree.tostring( + self.Config, + pretty_print=True, + xml_declaration=True, + encoding='utf-8')) config_xmlfile.close() process_filepath = self.ProcessVariablesFileName() - process_text = "\n" - process_extras = {"xmlns:xsi":"http://www.w3.org/2001/XMLSchema-instance"} - process_text += self.ProcessVariables.generateXMLText("ProcessVariables", 0, process_extras) - process_xmlfile = open(process_filepath,"w") - process_xmlfile.write(process_text.encode("utf-8")) + process_xmlfile.write(etree.tostring( + self.ProcessVariables, + pretty_print=True, + xml_declaration=True, + encoding='utf-8')) process_xmlfile.close() self.Buffer.CurrentSaved() @@ -751,13 +750,18 @@ Return a copy of the config """ def Copy(self, model): - return cPickle.loads(cPickle.dumps(model)) + return deepcopy(model) def CreateBuffer(self, saved): - self.Buffer = UndoBuffer(cPickle.dumps((self.Config, self.ProcessVariables)), saved) + self.Buffer = UndoBuffer( + (EtherCATConfigParser.Dumps(self.Config), + ProcessVariablesParser.Dumps(self.ProcessVariables)), + saved) def BufferModel(self): - self.Buffer.Buffering(cPickle.dumps((self.Config, self.ProcessVariables))) + self.Buffer.Buffering( + (EtherCATConfigParser.Dumps(self.Config), + ProcessVariablesParser.Dumps(self.ProcessVariables))) def ModelIsSaved(self): if self.Buffer is not None: @@ -766,10 +770,14 @@ return True def LoadPrevious(self): - self.Config, self.ProcessVariables = cPickle.loads(self.Buffer.Previous()) + config, process_variables = self.Buffer.Previous() + self.Config = EtherCATConfigParser.Loads(config) + self.ProcessVariables = ProcessVariablesParser.Loads(process_variables) def LoadNext(self): - self.Config, self.ProcessVariables = cPickle.loads(self.Buffer.Next()) + config, process_variables = self.Buffer.Next() + self.Config = EtherCATConfigParser.Loads(config) + self.ProcessVariables = ProcessVariablesParser.Loads(process_variables) def GetBufferState(self): first = self.Buffer.IsFirst() diff -r c8eee6be2da8 -r a2385e535cf5 etherlab/entries_list.xslt --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/etherlab/entries_list.xslt Tue Sep 24 00:48:21 2013 +0200 @@ -0,0 +1,123 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0 + + + + + + + + + + + + + + + + + + + + + + + + + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ro + T + + + wo + R + + + + + + + + + + + + + + \ No newline at end of file diff -r c8eee6be2da8 -r a2385e535cf5 etherlab/etherlab.py --- a/etherlab/etherlab.py Tue Jun 25 00:55:38 2013 +0200 +++ b/etherlab/etherlab.py Tue Sep 24 00:48:21 2013 +0200 @@ -1,5 +1,5 @@ import os, shutil -from xml.dom import minidom +from lxml import etree import wx import csv @@ -13,81 +13,79 @@ from EthercatMaster import _EthercatCTN from ConfigEditor import LibraryEditor, ETHERCAT_VENDOR, ETHERCAT_GROUP, ETHERCAT_DEVICE +ScriptDirectory = os.path.split(os.path.realpath(__file__))[0] + #-------------------------------------------------- # Ethercat ConfNode #-------------------------------------------------- -EtherCATInfoClasses = GenerateClassesFromXSD(os.path.join(os.path.dirname(__file__), "EtherCATInfo.xsd")) - -cls = EtherCATInfoClasses["EtherCATBase.xsd"].get("DictionaryType", None) +EtherCATInfoParser = GenerateParserFromXSD(os.path.join(os.path.dirname(__file__), "EtherCATInfo.xsd")) +EtherCATInfo_XPath = lambda xpath: etree.XPath(xpath) + +def extract_param(el): + if el.tag == "Index": + return "#x%4.4X" % int(el.text) + if el.tag == "PDOMapping": + if el.text is None: + return "" + return el.text.upper() + if el.text is None: + return "" + return el.text + +def extract_pdo_infos(pdo_infos): + return { + pdo_infos.tag + " " + el.tag: extract_param(el) + for el in pdo_infos} + +def HexDecValue(ctxt, values): + return str(ExtractHexDecValue(values[0])) + +def EntryName(ctxt, values): + default=None + names = [] + for element in values: + if element.tag == "Default": + default = element.text + else: + names.append(element) + return ExtractName(names, default) + +class AddEntry(etree.XSLTExtension): + + def __init__(self, entries): + etree.XSLTExtension.__init__(self) + self.Entries = entries + + def execute(self, context, self_node, input_node, output_parent): + infos = etree.Element('entry_infos') + self.process_children(context, infos) + index, subindex = map( + lambda x: int(infos.find(x).text), + ["Index", "SubIndex"]) + new_entry_infos = { + el.tag: extract_param(el) + for el in infos if el.tag != "PDO"} + if (index, subindex) != (0, 0): + entry_infos = self.Entries.get((index, subindex)) + if entry_infos is not None: + PDO_infos = infos.find("PDO") + if PDO_infos is not None: + entry_infos.update(extract_pdo_infos(PDO_infos)) + else: + self.Entries[(index, subindex)] = new_entry_infos + +entries_list_xslt = etree.parse( + os.path.join(ScriptDirectory, "entries_list.xslt")) + +cls = EtherCATInfoParser.GetElementClass("DeviceType") if cls: - cls.loadXMLTreeArgs = None - - setattr(cls, "_loadXMLTree", getattr(cls, "loadXMLTree")) - - def loadXMLTree(self, *args): - self.loadXMLTreeArgs = args - setattr(cls, "loadXMLTree", loadXMLTree) - - def load(self): - if self.loadXMLTreeArgs is not None: - self._loadXMLTree(*self.loadXMLTreeArgs) - self.loadXMLTreeArgs = None - setattr(cls, "load", load) - -cls = EtherCATInfoClasses["EtherCATInfo.xsd"].get("DeviceType", None) -if cls: - cls.DataTypes = None - + + profile_numbers_xpath = EtherCATInfo_XPath("Profile/ProfileNo") def GetProfileNumbers(self): - profiles = [] - - for profile in self.getProfile(): - profile_content = profile.getcontent() - if profile_content is None: - continue - - for content_element in profile_content["value"]: - if content_element["name"] == "ProfileNo": - profiles.append(content_element["value"]) - - return profiles + return [number.text for number in profile_numbers_xpath(self)] setattr(cls, "GetProfileNumbers", GetProfileNumbers) - def GetProfileDictionaries(self): - dictionaries = [] - - for profile in self.getProfile(): - - profile_content = profile.getcontent() - if profile_content is None: - continue - - for content_element in profile_content["value"]: - if content_element["name"] == "Dictionary": - dictionaries.append(content_element["value"]) - elif content_element["name"] == "DictionaryFile": - raise ValueError, "DictionaryFile for defining Device Profile is not yet supported!" - - return dictionaries - setattr(cls, "GetProfileDictionaries", GetProfileDictionaries) - - def ExtractDataTypes(self): - self.DataTypes = {} - - for dictionary in self.GetProfileDictionaries(): - dictionary.load() - - datatypes = dictionary.getDataTypes() - if datatypes is not None: - - for datatype in datatypes.getDataType(): - content = datatype.getcontent() - if content is not None and content["name"] == "SubItem": - self.DataTypes[datatype.getName()] = datatype - - setattr(cls, "ExtractDataTypes", ExtractDataTypes) - def getCoE(self): mailbox = self.getMailbox() if mailbox is not None: @@ -96,78 +94,22 @@ setattr(cls, "getCoE", getCoE) def GetEntriesList(self, limits=None): - if self.DataTypes is None: - self.ExtractDataTypes() - entries = {} - for dictionary in self.GetProfileDictionaries(): - dictionary.load() - - for object in dictionary.getObjects().getObject(): - entry_index = object.getIndex().getcontent() - index = ExtractHexDecValue(entry_index) - if limits is None or limits[0] <= index <= limits[1]: - entry_type = object.getType() - entry_name = ExtractName(object.getName()) - - entry_type_infos = self.DataTypes.get(entry_type, None) - if entry_type_infos is not None: - content = entry_type_infos.getcontent() - for subitem in content["value"]: - entry_subidx = subitem.getSubIdx() - if entry_subidx is None: - entry_subidx = "0" - subidx = ExtractHexDecValue(entry_subidx) - subitem_access = "" - subitem_pdomapping = "" - subitem_flags = subitem.getFlags() - if subitem_flags is not None: - access = subitem_flags.getAccess() - if access is not None: - subitem_access = access.getcontent() - pdomapping = subitem_flags.getPdoMapping() - if pdomapping is not None: - subitem_pdomapping = pdomapping.upper() - entries[(index, subidx)] = { - "Index": entry_index, - "SubIndex": entry_subidx, - "Name": "%s - %s" % - (entry_name.decode("utf-8"), - ExtractName(subitem.getDisplayName(), - subitem.getName()).decode("utf-8")), - "Type": subitem.getType(), - "BitSize": subitem.getBitSize(), - "Access": subitem_access, - "PDOMapping": subitem_pdomapping} - else: - entry_access = "" - entry_pdomapping = "" - entry_flags = object.getFlags() - if entry_flags is not None: - access = entry_flags.getAccess() - if access is not None: - entry_access = access.getcontent() - pdomapping = entry_flags.getPdoMapping() - if pdomapping is not None: - entry_pdomapping = pdomapping.upper() - entries[(index, 0)] = { - "Index": entry_index, - "SubIndex": "0", - "Name": entry_name, - "Type": entry_type, - "BitSize": object.getBitSize(), - "Access": entry_access, - "PDOMapping": entry_pdomapping} - - for TxPdo in self.getTxPdo(): - ExtractPdoInfos(TxPdo, "Transmit", entries, limits) - for RxPdo in self.getRxPdo(): - ExtractPdoInfos(RxPdo, "Receive", entries, limits) + entries_list_xslt_tree = etree.XSLT( + entries_list_xslt, extensions = { + ("entries_list_ns", "add_entry"): AddEntry(entries), + ("entries_list_ns", "HexDecValue"): HexDecValue, + ("entries_list_ns", "EntryName"): EntryName}) + entries_list_xslt_tree(self, **dict(zip( + ["min_index", "max_index"], + map(lambda x: etree.XSLT.strparam(str(x)), + limits if limits is not None else [0x0000, 0xFFFF]) + ))) return entries setattr(cls, "GetEntriesList", GetEntriesList) - + def GetSyncManagers(self): sync_managers = [] for sync_manager in self.getSm(): @@ -200,38 +142,6 @@ SortGroupItems(item) group["children"].sort(GroupItemCompare) -def ExtractPdoInfos(pdo, pdo_type, entries, limits=None): - pdo_index = pdo.getIndex().getcontent() - pdo_name = ExtractName(pdo.getName()) - for pdo_entry in pdo.getEntry(): - entry_index = pdo_entry.getIndex().getcontent() - entry_subindex = pdo_entry.getSubIndex() - index = ExtractHexDecValue(entry_index) - subindex = ExtractHexDecValue(entry_subindex) - - if limits is None or limits[0] <= index <= limits[1]: - entry = entries.get((index, subindex), None) - if entry is not None: - entry["PDO index"] = pdo_index - entry["PDO name"] = pdo_name - entry["PDO type"] = pdo_type - else: - entry_type = pdo_entry.getDataType() - if entry_type is not None: - if pdo_type == "Transmit": - access = "ro" - pdomapping = "T" - else: - access = "wo" - pdomapping = "R" - entries[(index, subindex)] = { - "Index": entry_index, - "SubIndex": entry_subindex, - "Name": ExtractName(pdo_entry.getName()), - "Type": entry_type.getcontent(), - "Access": access, - "PDOMapping": pdomapping} - class ModulesLibrary: MODULES_EXTRA_PARAMS = [ @@ -276,6 +186,8 @@ def GetModulesExtraParamsFilePath(self): return os.path.join(self.Path, "modules_extra_params.cfg") + groups_xpath = EtherCATInfo_XPath("Descriptions/Groups/Group") + devices_xpath = EtherCATInfo_XPath("Descriptions/Devices/Device") def LoadModules(self): self.Library = {} @@ -283,37 +195,40 @@ for file in files: filepath = os.path.join(self.Path, file) if os.path.isfile(filepath) and os.path.splitext(filepath)[-1] == ".xml": + self.modules_infos = None + xmlfile = open(filepath, 'r') - xml_tree = minidom.parse(xmlfile) + try: + self.modules_infos = etree.fromstring( + xmlfile.read(), EtherCATInfoParser) + except: + pass xmlfile.close() - self.modules_infos = None - for child in xml_tree.childNodes: - if child.nodeType == xml_tree.ELEMENT_NODE and child.nodeName == "EtherCATInfo": - self.modules_infos = EtherCATInfoClasses["EtherCATInfo.xsd"]["EtherCATInfo"]() - self.modules_infos.loadXMLTree(child) - if self.modules_infos is not None: vendor = self.modules_infos.getVendor() - vendor_category = self.Library.setdefault(ExtractHexDecValue(vendor.getId()), - {"name": ExtractName(vendor.getName(), _("Miscellaneous")), - "groups": {}}) + vendor_category = self.Library.setdefault( + ExtractHexDecValue(vendor.getId()), + {"name": ExtractName(vendor.getName(), _("Miscellaneous")), + "groups": {}}) - for group in self.modules_infos.getDescriptions().getGroups().getGroup(): + for group in self.groups_xpath(self.modules_infos): group_type = group.getType() - vendor_category["groups"].setdefault(group_type, {"name": ExtractName(group.getName(), group_type), - "parent": group.getParentGroup(), - "order": group.getSortOrder(), - "value": group.getcontent()["value"], - "devices": []}) + vendor_category["groups"].setdefault(group_type, + {"name": ExtractName(group.getName(), group_type), + "parent": group.getParentGroup(), + "order": group.getSortOrder(), + #"value": group.getcontent()["value"], + "devices": []}) - for device in self.modules_infos.getDescriptions().getDevices().getDevice(): + for device in self.devices_xpath(self.modules_infos): device_group = device.getGroupType() if not vendor_category["groups"].has_key(device_group): raise ValueError, "Not such group \"%\"" % device_group - vendor_category["groups"][device_group]["devices"].append((device.getType().getcontent(), device)) + vendor_category["groups"][device_group]["devices"].append( + (device.getType().getcontent(), device)) return self.Library @@ -464,8 +379,8 @@ CTNChildrenTypes = [("EthercatNode",_EthercatCTN,"Ethercat Master")] EditorType = LibraryEditor - - + + def __init__(self): self.ModulesLibrary = None self.LoadModulesLibrary()