lbessard@182: #!/usr/bin/env python lbessard@182: # -*- coding: utf-8 -*- lbessard@182: lbessard@182: #This file is part of CanFestival, a library implementing CanOpen Stack. lbessard@182: # lbessard@182: #Copyright (C): Edouard TISSERANT, Francis DUPIN and Laurent BESSARD lbessard@182: # lbessard@182: #See COPYING file for copyrights details. lbessard@182: # lbessard@182: #This library is free software; you can redistribute it and/or lbessard@182: #modify it under the terms of the GNU Lesser General Public lbessard@182: #License as published by the Free Software Foundation; either lbessard@182: #version 2.1 of the License, or (at your option) any later version. lbessard@182: # lbessard@182: #This library is distributed in the hope that it will be useful, lbessard@182: #but WITHOUT ANY WARRANTY; without even the implied warranty of lbessard@182: #MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU lbessard@182: #Lesser General Public License for more details. lbessard@182: # lbessard@182: #You should have received a copy of the GNU Lesser General Public lbessard@182: #License along with this library; if not, write to the Free Software lbessard@182: #Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA lbessard@182: lbessard@182: lbessard@182: import node lbessard@205: from node import nosub, var, array, rec, plurivar, pluriarray, plurirec greg@550: try: greg@550: set greg@550: except NameError: greg@550: from sets import Set as set lbessard@182: from types import * lbessard@182: from time import * lbessard@182: import os,re lbessard@182: lbessard@182: # Regular expression for finding index section names lbessard@332: index_model = re.compile('([0-9A-F]{1,4}$)') lbessard@182: # Regular expression for finding subindex section names lbessard@332: subindex_model = re.compile('([0-9A-F]{1,4})SUB([0-9A-F]{1,2}$)') lbessard@332: # Regular expression for finding index section names lbessard@332: index_objectlinks_model = re.compile('([0-9A-F]{1,4}OBJECTLINKS$)') lbessard@205: lbessard@205: # Regular expression for finding NodeXPresent keynames lbessard@332: nodepresent_model = re.compile('NODE([0-9]{1,3})PRESENT$') lbessard@205: # Regular expression for finding NodeXName keynames lbessard@332: nodename_model = re.compile('NODE([0-9]{1,3})NAME$') lbessard@205: # Regular expression for finding NodeXDCFName keynames lbessard@332: nodedcfname_model = re.compile('NODE([0-9]{1,3})DCFNAME$') lbessard@182: lbessard@182: # Dictionary for quickly translate boolean into integer value lbessard@182: BOOL_TRANSLATE = {True : "1", False : "0"} lbessard@182: lbessard@205: # Dictionary for quickly translate eds access value into canfestival access value lbessard@309: ACCESS_TRANSLATE = {"RO" : "ro", "WO" : "wo", "RW" : "rw", "RWR" : "rw", "RWW" : "rw", "CONST" : "ro"} lbessard@205: lbessard@182: # Function for verifying data values greg@206: is_integer = lambda x: type(x) in (IntType, LongType) greg@206: is_string = lambda x: type(x) in (StringType, UnicodeType) lbessard@205: is_boolean = lambda x: x in (0, 1) lbessard@182: lbessard@182: # Define checking of value for each attribute lbessard@509: ENTRY_ATTRIBUTES = {"SUBNUMBER" : is_integer, lbessard@509: "PARAMETERNAME" : is_string, lbessard@509: "OBJECTTYPE" : lambda x: x in (2, 7, 8, 9), lbessard@509: "DATATYPE" : is_integer, lbessard@509: "LOWLIMIT" : is_integer, lbessard@509: "HIGHLIMIT" : is_integer, lbessard@309: "ACCESSTYPE" : lambda x: x.upper() in ACCESS_TRANSLATE.keys(), lbessard@509: "DEFAULTVALUE" : lambda x: True, lbessard@509: "PDOMAPPING" : is_boolean, lbessard@509: "OBJFLAGS" : is_integer, lbessard@509: "PARAMETERVALUE" : lambda x: True, lbessard@509: "UPLOADFILE" : is_string, lbessard@509: "DOWNLOADFILE" : is_string} lbessard@182: lbessard@182: # Define entry parameters by entry ObjectType number lbessard@509: ENTRY_TYPES = {2 : {"name" : " DOMAIN", lbessard@509: "require" : ["PARAMETERNAME", "OBJECTTYPE"], lbessard@509: "optional" : ["DATATYPE", "ACCESSTYPE", "DEFAULTVALUE", "OBJFLAGS"]}, lbessard@509: 7 : {"name" : " VAR", lbessard@226: "require" : ["PARAMETERNAME", "DATATYPE", "ACCESSTYPE"], lbessard@323: "optional" : ["OBJECTTYPE", "DEFAULTVALUE", "PDOMAPPING", "LOWLIMIT", "HIGHLIMIT", "OBJFLAGS", "PARAMETERVALUE"]}, lbessard@182: 8 : {"name" : "n ARRAY", lbessard@226: "require" : ["PARAMETERNAME", "OBJECTTYPE", "SUBNUMBER"], lbessard@182: "optional" : ["OBJFLAGS"]}, lbessard@182: 9 : {"name" : " RECORD", lbessard@226: "require" : ["PARAMETERNAME", "OBJECTTYPE", "SUBNUMBER"], lbessard@182: "optional" : ["OBJFLAGS"]}} lbessard@182: lbessard@182: lbessard@182: # Function that search into Node Mappings the informations about an index or a subindex lbessard@182: # and return the default value lbessard@489: def GetDefaultValue(Node, index, subIndex = None): lbessard@205: infos = Node.GetEntryInfos(index) lbessard@182: if infos["struct"] & node.OD_MultipleSubindexes: Laurent@776: # First case entry is a array lbessard@182: if infos["struct"] & node.OD_IdenticalSubindexes: lbessard@205: subentry_infos = Node.GetSubentryInfos(index, 1) Laurent@776: # Second case entry is an record lbessard@182: else: lbessard@205: subentry_infos = Node.GetSubentryInfos(index, subIndex) lbessard@182: # If a default value is defined for this subindex, returns it lbessard@182: if "default" in subentry_infos: lbessard@182: return subentry_infos["default"] lbessard@182: # If not, returns the default value for the subindex type lbessard@182: else: lbessard@205: return Node.GetTypeDefaultValue(subentry_infos["type"]) lbessard@182: # Third case entry is a var lbessard@182: else: lbessard@205: subentry_infos = Node.GetSubentryInfos(index, 0) lbessard@182: # If a default value is defined for this subindex, returns it lbessard@182: if "default" in subentry_infos: lbessard@182: return subentry_infos["default"] lbessard@182: # If not, returns the default value for the subindex type lbessard@182: else: lbessard@205: return Node.GetTypeDefaultValue(subentry_infos["type"]) lbessard@182: return None lbessard@182: lbessard@182: lbessard@182: #------------------------------------------------------------------------------- lbessard@182: # Parse file lbessard@182: #------------------------------------------------------------------------------- lbessard@182: lbessard@182: lbessard@182: # List of section names that are not index and subindex and that we can meet in lbessard@182: # an EDS file lbessard@182: SECTION_KEYNAMES = ["FILEINFO", "DEVICEINFO", "DUMMYUSAGE", "COMMENTS", lbessard@305: "MANDATORYOBJECTS", "OPTIONALOBJECTS", "MANUFACTUREROBJECTS", lbessard@332: "STANDARDDATATYPES", "SUPPORTEDMODULES"] lbessard@182: lbessard@182: lbessard@205: # Function that extract sections from a file and returns a dictionary of the informations lbessard@205: def ExtractSections(file): lbessard@205: return [(blocktuple[0], # EntryName : Assignements dict lbessard@205: blocktuple[-1].splitlines()) # all the lines lbessard@205: for blocktuple in [ # Split the eds files into lbessard@226: block.split("]", 1) # (EntryName,Assignements) tuple lbessard@205: for block in # for each blocks staring with '[' lbessard@226: ("\n"+file).split("\n[")] lbessard@205: if blocktuple[0].isalnum()] # if EntryName exists lbessard@205: lbessard@205: lbessard@205: # Function that parse an CPJ file and returns a dictionary of the informations lbessard@205: def ParseCPJFile(filepath): lbessard@205: networks = [] lbessard@205: # Read file text lbessard@205: cpj_file = open(filepath,'r').read() lbessard@205: sections = ExtractSections(cpj_file) lbessard@205: # Parse assignments for each section lbessard@205: for section_name, assignments in sections: lbessard@205: lbessard@205: # Verify that section name is TOPOLOGY lbessard@205: if section_name.upper() in "TOPOLOGY": lbessard@205: lbessard@205: # Reset values for topology lbessard@205: topology = {"Name" : "", "Nodes" : {}} lbessard@205: lbessard@205: for assignment in assignments: lbessard@205: # Escape any comment lbessard@205: if assignment.startswith(";"): lbessard@205: pass lbessard@205: # Verify that line is a valid assignment lbessard@205: elif assignment.find('=') > 0: lbessard@205: # Split assignment into the two values keyname and value lbessard@226: keyname, value = assignment.split("=", 1) lbessard@205: lbessard@205: # keyname must be immediately followed by the "=" sign, so we lbessard@205: # verify that there is no whitespace into keyname lbessard@205: if keyname.isalnum(): lbessard@205: # value can be preceded and followed by whitespaces, so we escape them lbessard@205: value = value.strip() lbessard@205: lbessard@309: # First case, value starts with "0x" or "-0x", then it's an hexadecimal value lbessard@309: if value.startswith("0x") or value.startswith("-0x"): lbessard@205: try: lbessard@205: computed_value = int(value, 16) lbessard@205: except: laurent@580: raise SyntaxError, _("\"%s\" is not a valid value for attribute \"%s\" of section \"[%s]\"")%(value, keyname, section_name) lbessard@309: elif value.isdigit() or value.startswith("-") and value[1:].isdigit(): lbessard@309: # Second case, value is a number and starts with "0" or "-0", then it's an octal value lbessard@309: if value.startswith("0") or value.startswith("-0"): lbessard@205: computed_value = int(value, 8) lbessard@205: # Third case, value is a number and don't start with "0", then it's a decimal value lbessard@205: else: lbessard@205: computed_value = int(value) lbessard@205: # In any other case, we keep string value lbessard@205: else: lbessard@205: computed_value = value lbessard@205: lbessard@205: # Search if the section name match any cpj expression lbessard@205: nodepresent_result = nodepresent_model.match(keyname.upper()) lbessard@205: nodename_result = nodename_model.match(keyname.upper()) lbessard@205: nodedcfname_result = nodedcfname_model.match(keyname.upper()) lbessard@205: lbessard@205: if keyname.upper() == "NETNAME": lbessard@205: if not is_string(computed_value): laurent@580: raise SyntaxError, _("Invalid value \"%s\" for keyname \"%s\" of section \"[%s]\"")%(value, keyname, section_name) lbessard@205: topology["Name"] = computed_value lbessard@205: elif keyname.upper() == "NODES": lbessard@205: if not is_integer(computed_value): laurent@580: raise SyntaxError, _("Invalid value \"%s\" for keyname \"%s\" of section \"[%s]\"")%(value, keyname, section_name) lbessard@205: topology["Number"] = computed_value lbessard@205: elif keyname.upper() == "EDSBASENAME": lbessard@205: if not is_string(computed_value): laurent@580: raise SyntaxError, _("Invalid value \"%s\" for keyname \"%s\" of section \"[%s]\"")%(value, keyname, section_name) lbessard@205: topology["Path"] = computed_value lbessard@205: elif nodepresent_result: lbessard@205: if not is_boolean(computed_value): laurent@580: raise SyntaxError, _("Invalid value \"%s\" for keyname \"%s\" of section \"[%s]\"")%(value, keyname, section_name) lbessard@205: nodeid = int(nodepresent_result.groups()[0]) lbessard@205: if nodeid not in topology["Nodes"].keys(): lbessard@205: topology["Nodes"][nodeid] = {} lbessard@205: topology["Nodes"][nodeid]["Present"] = computed_value lbessard@205: elif nodename_result: lbessard@205: if not is_string(value): laurent@580: raise SyntaxError, _("Invalid value \"%s\" for keyname \"%s\" of section \"[%s]\"")%(value, keyname, section_name) lbessard@205: nodeid = int(nodename_result.groups()[0]) lbessard@205: if nodeid not in topology["Nodes"].keys(): lbessard@205: topology["Nodes"][nodeid] = {} lbessard@205: topology["Nodes"][nodeid]["Name"] = computed_value lbessard@205: elif nodedcfname_result: lbessard@205: if not is_string(computed_value): laurent@580: raise SyntaxError, _("Invalid value \"%s\" for keyname \"%s\" of section \"[%s]\"")%(value, keyname, section_name) lbessard@205: nodeid = int(nodedcfname_result.groups()[0]) lbessard@205: if nodeid not in topology["Nodes"].keys(): lbessard@205: topology["Nodes"][nodeid] = {} lbessard@205: topology["Nodes"][nodeid]["DCFName"] = computed_value lbessard@205: else: laurent@580: raise SyntaxError, _("Keyname \"%s\" not recognised for section \"[%s]\"")%(keyname, section_name) lbessard@205: lbessard@205: # All lines that are not empty and are neither a comment neither not a valid assignment lbessard@205: elif assignment.strip() != "": laurent@580: raise SyntaxError, _("\"%s\" is not a valid CPJ line")%assignment.strip() lbessard@205: lbessard@205: if "Number" not in topology.keys(): laurent@580: raise SyntaxError, _("\"Nodes\" keyname in \"[%s]\" section is missing")%section_name lbessard@205: lbessard@205: if topology["Number"] != len(topology["Nodes"]): laurent@580: raise SyntaxError, _("\"Nodes\" value not corresponding to number of nodes defined") lbessard@205: lbessard@205: for nodeid, node in topology["Nodes"].items(): lbessard@205: if "Present" not in node.keys(): laurent@580: raise SyntaxError, _("\"Node%dPresent\" keyname in \"[%s]\" section is missing")%(nodeid, section_name) lbessard@205: lbessard@205: networks.append(topology) lbessard@205: lbessard@205: # In other case, there is a syntax problem into CPJ file lbessard@205: else: laurent@580: raise SyntaxError, _("Section \"[%s]\" is unrecognized")%section_name lbessard@205: lbessard@205: return networks lbessard@205: lbessard@182: # Function that parse an EDS file and returns a dictionary of the informations lbessard@205: def ParseEDSFile(filepath): lbessard@182: eds_dict = {} lbessard@182: # Read file text lbessard@182: eds_file = open(filepath,'r').read() lbessard@205: sections = ExtractSections(eds_file) lbessard@182: lbessard@182: # Parse assignments for each section lbessard@182: for section_name, assignments in sections: lbessard@182: # Reset values of entry lbessard@182: values = {} lbessard@182: lbessard@182: # Search if the section name match an index or subindex expression lbessard@205: index_result = index_model.match(section_name.upper()) lbessard@205: subindex_result = subindex_model.match(section_name.upper()) lbessard@332: index_objectlinks_result = index_objectlinks_model.match(section_name.upper()) lbessard@182: lbessard@182: # Compilation of the EDS information dictionary lbessard@182: lbessard@182: is_entry = False lbessard@182: # First case, section name is in SECTION_KEYNAMES lbessard@182: if section_name.upper() in SECTION_KEYNAMES: lbessard@182: # Verify that entry is not already defined lbessard@182: if section_name.upper() not in eds_dict: lbessard@182: eds_dict[section_name.upper()] = values lbessard@182: else: laurent@580: raise SyntaxError, _("\"[%s]\" section is defined two times")%section_name lbessard@332: # Second case, section name is an index name lbessard@182: elif index_result: lbessard@182: # Extract index number lbessard@182: index = int(index_result.groups()[0], 16) lbessard@182: # If index hasn't been referenced before, we add an entry into the dictionary lbessard@182: if index not in eds_dict: lbessard@182: eds_dict[index] = values lbessard@182: eds_dict[index]["subindexes"] = {} lbessard@182: elif eds_dict[index].keys() == ["subindexes"]: lbessard@182: values["subindexes"] = eds_dict[index]["subindexes"] lbessard@182: eds_dict[index] = values lbessard@182: else: laurent@580: raise SyntaxError, _("\"[%s]\" section is defined two times")%section_name lbessard@182: is_entry = True lbessard@332: # Third case, section name is a subindex name lbessard@332: elif subindex_result: lbessard@332: # Extract index and subindex number lbessard@332: index, subindex = [int(value, 16) for value in subindex_result.groups()] lbessard@332: # If index hasn't been referenced before, we add an entry into the dictionary lbessard@332: # that will be updated later lbessard@332: if index not in eds_dict: lbessard@332: eds_dict[index] = {"subindexes" : {}} lbessard@332: if subindex not in eds_dict[index]["subindexes"]: lbessard@332: eds_dict[index]["subindexes"][subindex] = values lbessard@332: else: laurent@580: raise SyntaxError, _("\"[%s]\" section is defined two times")%section_name lbessard@332: is_entry = True lbessard@332: # Third case, section name is a subindex name lbessard@332: elif index_objectlinks_result: lbessard@332: pass lbessard@182: # In any other case, there is a syntax problem into EDS file lbessard@182: else: laurent@580: raise SyntaxError, _("Section \"[%s]\" is unrecognized")%section_name lbessard@182: lbessard@182: for assignment in assignments: lbessard@182: # Escape any comment lbessard@182: if assignment.startswith(";"): lbessard@182: pass lbessard@182: # Verify that line is a valid assignment lbessard@182: elif assignment.find('=') > 0: lbessard@182: # Split assignment into the two values keyname and value lbessard@226: keyname, value = assignment.split("=", 1) lbessard@226: lbessard@182: # keyname must be immediately followed by the "=" sign, so we lbessard@182: # verify that there is no whitespace into keyname lbessard@182: if keyname.isalnum(): lbessard@182: # value can be preceded and followed by whitespaces, so we escape them Laurent@776: value = value.strip() lbessard@182: # First case, value starts with "$NODEID", then it's a formula greg@542: if value.upper().startswith("$NODEID"): lbessard@182: try: greg@542: test = int(value.upper().replace("$NODEID+", ""), 16) lbessard@299: computed_value = "\"%s\""%value lbessard@182: except: laurent@580: raise SyntaxError, _("\"%s\" is not a valid formula for attribute \"%s\" of section \"[%s]\"")%(value, keyname, section_name) lbessard@182: # Second case, value starts with "0x", then it's an hexadecimal value lbessard@309: elif value.startswith("0x") or value.startswith("-0x"): lbessard@182: try: lbessard@182: computed_value = int(value, 16) lbessard@182: except: laurent@580: raise SyntaxError, _("\"%s\" is not a valid value for attribute \"%s\" of section \"[%s]\"")%(value, keyname, section_name) lbessard@309: elif value.isdigit() or value.startswith("-") and value[1:].isdigit(): lbessard@182: # Third case, value is a number and starts with "0", then it's an octal value lbessard@309: if value.startswith("0") or value.startswith("-0"): lbessard@182: computed_value = int(value, 8) lbessard@182: # Forth case, value is a number and don't start with "0", then it's a decimal value lbessard@182: else: lbessard@182: computed_value = int(value) lbessard@182: # In any other case, we keep string value lbessard@182: else: lbessard@182: computed_value = value lbessard@182: lbessard@182: # Add value to values dictionary lbessard@182: if computed_value != "": lbessard@182: # If entry is an index or a subindex lbessard@182: if is_entry: lbessard@182: # Verify that keyname is a possible attribute lbessard@182: if keyname.upper() not in ENTRY_ATTRIBUTES: laurent@580: raise SyntaxError, _("Keyname \"%s\" not recognised for section \"[%s]\"")%(keyname, section_name) lbessard@182: # Verify that value is valid lbessard@182: elif not ENTRY_ATTRIBUTES[keyname.upper()](computed_value): laurent@580: raise SyntaxError, _("Invalid value \"%s\" for keyname \"%s\" of section \"[%s]\"")%(value, keyname, section_name) lbessard@182: else: lbessard@182: values[keyname.upper()] = computed_value lbessard@182: else: lbessard@182: values[keyname.upper()] = computed_value lbessard@182: # All lines that are not empty and are neither a comment neither not a valid assignment lbessard@182: elif assignment.strip() != "": laurent@580: raise SyntaxError, _("\"%s\" is not a valid EDS line")%assignment.strip() lbessard@182: lbessard@182: # If entry is an index or a subindex lbessard@182: if is_entry: lbessard@182: # Verify that entry has an ObjectType lbessard@509: values["OBJECTTYPE"] = values.get("OBJECTTYPE", 7) lbessard@226: # Extract parameters defined greg@550: keys = set(values.keys()) lbessard@226: keys.discard("subindexes") lbessard@226: # Extract possible parameters and parameters required greg@550: possible = set(ENTRY_TYPES[values["OBJECTTYPE"]]["require"] + lbessard@509: ENTRY_TYPES[values["OBJECTTYPE"]]["optional"]) greg@550: required = set(ENTRY_TYPES[values["OBJECTTYPE"]]["require"]) lbessard@226: # Verify that parameters defined contains all the parameters required lbessard@226: if not keys.issuperset(required): Laurent@784: missing = required.difference(keys) lbessard@226: if len(missing) > 1: laurent@580: attributes = _("Attributes %s are")%_(", ").join(["\"%s\""%attribute for attribute in missing]) lbessard@226: else: Laurent@784: attributes = _("Attribute \"%s\" is")%missing.pop() laurent@580: raise SyntaxError, _("Error on section \"[%s]\":\n%s required for a %s entry")%(section_name, attributes, ENTRY_TYPES[values["OBJECTTYPE"]]["name"]) lbessard@226: # Verify that parameters defined are all in the possible parameters lbessard@226: if not keys.issubset(possible): Laurent@784: unsupported = keys.difference(possible) lbessard@226: if len(unsupported) > 1: laurent@580: attributes = _("Attributes %s are")%_(", ").join(["\"%s\""%attribute for attribute in unsupported]) lbessard@226: else: Laurent@784: attributes = _("Attribute \"%s\" is")%unsupported.pop() laurent@580: raise SyntaxError, _("Error on section \"[%s]\":\n%s unsupported for a %s entry")%(section_name, attributes, ENTRY_TYPES[values["OBJECTTYPE"]]["name"]) lbessard@227: lbessard@513: VerifyValue(values, section_name, "ParameterValue") lbessard@513: VerifyValue(values, section_name, "DefaultValue") lbessard@227: lbessard@182: return eds_dict lbessard@182: lbessard@513: def VerifyValue(values, section_name, param): lbessard@509: if param.upper() in values: lbessard@509: try: lbessard@509: if values["DATATYPE"] in (0x09, 0x0A, 0x0B, 0x0F): lbessard@509: values[param.upper()] = str(values[param.upper()]) lbessard@509: elif values["DATATYPE"] in (0x08, 0x11): lbessard@509: values[param.upper()] = float(values[param.upper()]) lbessard@509: elif values["DATATYPE"] == 0x01: lbessard@509: values[param.upper()] = {0 : False, 1 : True}[values[param.upper()]] lbessard@509: else: lbessard@513: if not isinstance(values[param.upper()], (IntType, LongType)) and values[param.upper()].upper().find("$NODEID") == -1: lbessard@509: raise lbessard@509: except: laurent@580: raise SyntaxError, _("Error on section \"[%s]\":\n%s incompatible with DataType")%(section_name, param) lbessard@509: lbessard@182: lbessard@182: # Function that write an EDS file after generate it's content lbessard@182: def WriteFile(filepath, content): lbessard@182: # Open file in write mode lbessard@182: cfile = open(filepath,"w") lbessard@182: # Write content lbessard@182: cfile.write(content) lbessard@182: # Close file lbessard@182: cfile.close() lbessard@182: lbessard@182: lbessard@182: # Function that generate the EDS file content for the current node in the manager lbessard@489: def GenerateFileContent(Node, filepath): lbessard@182: # Dictionary of each index contents lbessard@182: indexContents = {} lbessard@182: lbessard@182: # Extract local time lbessard@182: current_time = localtime() lbessard@182: # Extract node informations lbessard@489: nodename = Node.GetNodeName() lbessard@489: nodeid = Node.GetNodeID() lbessard@489: nodetype = Node.GetNodeType() lbessard@489: description = Node.GetNodeDescription() lbessard@489: lbessard@489: # Retreiving lists of indexes defined lbessard@489: entries = Node.GetIndexes() lbessard@182: lbessard@182: # Generate FileInfo section lbessard@182: fileContent = "[FileInfo]\n" lbessard@258: fileContent += "FileName=%s\n"%os.path.split(filepath)[-1] lbessard@258: fileContent += "FileVersion=1\n" lbessard@258: fileContent += "FileRevision=1\n" lbessard@258: fileContent += "EDSVersion=4.0\n" lbessard@182: fileContent += "Description=%s\n"%description lbessard@182: fileContent += "CreationTime=%s"%strftime("%I:%M", current_time) lbessard@182: # %p option of strftime seems not working, then generate AM/PM by hands lbessard@182: if strftime("%I", current_time) == strftime("%H", current_time): lbessard@182: fileContent += "AM\n" lbessard@182: else: lbessard@182: fileContent += "PM\n" lbessard@182: fileContent += "CreationDate=%s\n"%strftime("%m-%d-%Y", current_time) lbessard@258: fileContent += "CreatedBy=CANFestival\n" lbessard@258: fileContent += "ModificationTime=%s"%strftime("%I:%M", current_time) lbessard@258: # %p option of strftime seems not working, then generate AM/PM by hands lbessard@258: if strftime("%I", current_time) == strftime("%H", current_time): lbessard@258: fileContent += "AM\n" lbessard@258: else: lbessard@258: fileContent += "PM\n" lbessard@258: fileContent += "ModificationDate=%s\n"%strftime("%m-%d-%Y", current_time) lbessard@258: fileContent += "ModifiedBy=CANFestival\n" lbessard@182: lbessard@182: # Generate DeviceInfo section lbessard@182: fileContent += "\n[DeviceInfo]\n" lbessard@182: fileContent += "VendorName=CANFestival\n" lbessard@182: # Use information typed by user in Identity entry lbessard@489: fileContent += "VendorNumber=0x%8.8X\n"%Node.GetEntry(0x1018, 1) lbessard@182: fileContent += "ProductName=%s\n"%nodename lbessard@489: fileContent += "ProductNumber=0x%8.8X\n"%Node.GetEntry(0x1018, 2) lbessard@489: fileContent += "RevisionNumber=0x%8.8X\n"%Node.GetEntry(0x1018, 3) lbessard@182: # CANFestival support all baudrates as soon as driver choosen support them lbessard@182: fileContent += "BaudRate_10=1\n" lbessard@182: fileContent += "BaudRate_20=1\n" lbessard@182: fileContent += "BaudRate_50=1\n" lbessard@182: fileContent += "BaudRate_125=1\n" lbessard@182: fileContent += "BaudRate_250=1\n" lbessard@182: fileContent += "BaudRate_500=1\n" lbessard@182: fileContent += "BaudRate_800=1\n" lbessard@182: fileContent += "BaudRate_1000=1\n" lbessard@182: # Select BootUp type from the informations given by user lbessard@182: fileContent += "SimpleBootUpMaster=%s\n"%BOOL_TRANSLATE[nodetype == "master"] lbessard@182: fileContent += "SimpleBootUpSlave=%s\n"%BOOL_TRANSLATE[nodetype == "slave"] lbessard@182: # CANFestival characteristics lbessard@182: fileContent += "Granularity=8\n" lbessard@182: fileContent += "DynamicChannelsSupported=0\n" lbessard@182: fileContent += "CompactPDO=0\n" lbessard@182: fileContent += "GroupMessaging=0\n" lbessard@182: # Calculate receive and tranmit PDO numbers with the entry available lbessard@182: fileContent += "NrOfRXPDO=%d\n"%len([idx for idx in entries if 0x1400 <= idx <= 0x15FF]) lbessard@205: fileContent += "NrOfTXPDO=%d\n"%len([idx for idx in entries if 0x1800 <= idx <= 0x19FF]) lbessard@182: # LSS not supported as soon as DS-302 was not fully implemented lbessard@182: fileContent += "LSS_Supported=0\n" lbessard@182: lbessard@182: # Generate Dummy Usage section lbessard@182: fileContent += "\n[DummyUsage]\n" lbessard@182: fileContent += "Dummy0001=0\n" lbessard@182: fileContent += "Dummy0002=1\n" lbessard@182: fileContent += "Dummy0003=1\n" lbessard@182: fileContent += "Dummy0004=1\n" lbessard@182: fileContent += "Dummy0005=1\n" lbessard@182: fileContent += "Dummy0006=1\n" lbessard@182: fileContent += "Dummy0007=1\n" lbessard@182: lbessard@182: # Generate Comments section lbessard@182: fileContent += "\n[Comments]\n" lbessard@182: fileContent += "Lines=0\n" lbessard@182: lbessard@182: # List of entry by type (Mandatory, Optional or Manufacturer lbessard@182: mandatories = [] lbessard@182: optionals = [] lbessard@182: manufacturers = [] lbessard@182: etisserant@492: # Remove all unused PDO lbessard@546: ## for entry in entries[:]: lbessard@546: ## if 0x1600 <= entry < 0x1800 or 0x1A00 <= entry < 0x1C00: lbessard@546: ## subentry_value = Node.GetEntry(entry, 1) lbessard@546: ## if subentry_value is None or subentry_value == 0: lbessard@546: ## entries.remove(entry) lbessard@546: ## entries.remove(entry - 0x200) etisserant@492: lbessard@182: # For each entry, we generate the entry section or sections if there is subindexes lbessard@182: for entry in entries: lbessard@182: # Extract infos and values for the entry lbessard@489: entry_infos = Node.GetEntryInfos(entry) lbessard@489: values = Node.GetEntry(entry, compute = False) lbessard@182: # Define section name lbessard@182: text = "\n[%X]\n"%entry lbessard@182: # If there is only one value, it's a VAR entry lbessard@182: if type(values) != ListType: lbessard@182: # Extract the informations of the first subindex lbessard@489: subentry_infos = Node.GetSubentryInfos(entry, 0) lbessard@182: # Generate EDS informations for the entry lbessard@182: text += "ParameterName=%s\n"%subentry_infos["name"] lbessard@182: text += "ObjectType=0x7\n" lbessard@182: text += "DataType=0x%4.4X\n"%subentry_infos["type"] lbessard@182: text += "AccessType=%s\n"%subentry_infos["access"] lbessard@445: if subentry_infos["type"] == 1: lbessard@445: text += "DefaultValue=%s\n"%BOOL_TRANSLATE[values] lbessard@445: else: lbessard@445: text += "DefaultValue=%s\n"%values lbessard@182: text += "PDOMapping=%s\n"%BOOL_TRANSLATE[subentry_infos["pdo"]] lbessard@182: else: lbessard@182: # Generate EDS informations for the entry lbessard@182: text += "ParameterName=%s\n"%entry_infos["name"] lbessard@182: if entry_infos["struct"] & node.OD_IdenticalSubindexes: Laurent@776: text += "ObjectType=0x8\n" Laurent@776: else: lbessard@182: text += "ObjectType=0x9\n" lbessard@182: lbessard@182: # Generate EDS informations for subindexes of the entry in a separate text lbessard@182: subtext = "" lbessard@182: # Reset number of subindex defined lbessard@182: nb_subentry = 0 lbessard@182: for subentry, value in enumerate(values): lbessard@182: # Extract the informations of each subindex lbessard@489: subentry_infos = Node.GetSubentryInfos(entry, subentry) lbessard@182: # If entry is not for the compatibility, generate informations for subindex lbessard@182: if subentry_infos["name"] != "Compatibility Entry": lbessard@182: subtext += "\n[%Xsub%X]\n"%(entry, subentry) lbessard@182: subtext += "ParameterName=%s\n"%subentry_infos["name"] lbessard@182: subtext += "ObjectType=0x7\n" lbessard@182: subtext += "DataType=0x%4.4X\n"%subentry_infos["type"] lbessard@182: subtext += "AccessType=%s\n"%subentry_infos["access"] lbessard@445: if subentry_infos["type"] == 1: lbessard@445: subtext += "DefaultValue=%s\n"%BOOL_TRANSLATE[value] lbessard@445: else: lbessard@445: subtext += "DefaultValue=%s\n"%value lbessard@182: subtext += "PDOMapping=%s\n"%BOOL_TRANSLATE[subentry_infos["pdo"]] lbessard@182: # Increment number of subindex defined lbessard@182: nb_subentry += 1 lbessard@182: # Write number of subindex defined for the entry lbessard@182: text += "SubNumber=%d\n"%nb_subentry lbessard@182: # Write subindex definitions lbessard@182: text += subtext lbessard@182: lbessard@182: # Then we add the entry in the right list lbessard@182: lbessard@182: # First case, entry is between 0x2000 and 0x5FFF, then it's a manufacturer entry lbessard@182: if 0x2000 <= entry <= 0x5FFF: lbessard@182: manufacturers.append(entry) lbessard@182: # Second case, entry is required, then it's a mandatory entry lbessard@182: elif entry_infos["need"]: lbessard@182: mandatories.append(entry) lbessard@182: # In any other case, it's an optional entry lbessard@182: else: lbessard@182: optionals.append(entry) lbessard@182: # Save text of the entry in the dictiionary of contents lbessard@182: indexContents[entry] = text lbessard@182: lbessard@182: # Before generate File Content we sort the entry list lbessard@182: manufacturers.sort() lbessard@182: mandatories.sort() lbessard@182: optionals.sort() lbessard@182: lbessard@182: # Generate Definition of mandatory objects lbessard@182: fileContent += "\n[MandatoryObjects]\n" lbessard@182: fileContent += "SupportedObjects=%d\n"%len(mandatories) lbessard@182: for idx, entry in enumerate(mandatories): lbessard@258: fileContent += "%d=0x%4.4X\n"%(idx + 1, entry) lbessard@182: # Write mandatory entries lbessard@182: for entry in mandatories: lbessard@182: fileContent += indexContents[entry] lbessard@182: lbessard@182: # Generate Definition of optional objects lbessard@182: fileContent += "\n[OptionalObjects]\n" lbessard@182: fileContent += "SupportedObjects=%d\n"%len(optionals) lbessard@182: for idx, entry in enumerate(optionals): lbessard@258: fileContent += "%d=0x%4.4X\n"%(idx + 1, entry) lbessard@182: # Write optional entries lbessard@182: for entry in optionals: lbessard@182: fileContent += indexContents[entry] lbessard@182: lbessard@182: # Generate Definition of manufacturer objects lbessard@182: fileContent += "\n[ManufacturerObjects]\n" lbessard@182: fileContent += "SupportedObjects=%d\n"%len(manufacturers) lbessard@182: for idx, entry in enumerate(manufacturers): lbessard@258: fileContent += "%d=0x%4.4X\n"%(idx + 1, entry) lbessard@182: # Write manufacturer entries lbessard@182: for entry in manufacturers: lbessard@182: fileContent += indexContents[entry] lbessard@182: lbessard@182: # Return File Content lbessard@182: return fileContent lbessard@182: lbessard@182: lbessard@182: # Function that generates EDS file from current node edited lbessard@489: def GenerateEDSFile(filepath, node): lbessard@182: try: lbessard@182: # Generate file content lbessard@489: content = GenerateFileContent(node, filepath) lbessard@182: # Write file lbessard@182: WriteFile(filepath, content) lbessard@182: return None lbessard@182: except ValueError, message: laurent@580: return _("Unable to generate EDS file\n%s")%message lbessard@182: lbessard@205: # Function that generate the CPJ file content for the nodelist lbessard@205: def GenerateCPJContent(nodelist): lbessard@205: nodes = nodelist.SlaveNodes.keys() lbessard@205: nodes.sort() lbessard@205: lbessard@205: fileContent = "[TOPOLOGY]\n" lbessard@205: fileContent += "NetName=%s\n"%nodelist.GetNetworkName() lbessard@205: fileContent += "Nodes=0x%2.2X\n"%len(nodes) lbessard@205: lbessard@205: for nodeid in nodes: lbessard@205: fileContent += "Node%dPresent=0x01\n"%nodeid lbessard@205: fileContent += "Node%dName=%s\n"%(nodeid, nodelist.SlaveNodes[nodeid]["Name"]) lbessard@205: fileContent += "Node%dDCFName=%s\n"%(nodeid, nodelist.SlaveNodes[nodeid]["EDS"]) lbessard@205: lbessard@205: fileContent += "EDSBaseName=eds\n" lbessard@205: return fileContent lbessard@182: lbessard@182: # Function that generates Node from an EDS file lbessard@258: def GenerateNode(filepath, nodeID = 0): lbessard@182: # Create a new node lbessard@205: Node = node.Node(id = nodeID) lbessard@182: try: lbessard@182: # Parse file and extract dictionary of EDS entry lbessard@205: eds_dict = ParseEDSFile(filepath) lbessard@182: # Extract Profile Number from Device Type entry lbessard@509: ProfileNb = eds_dict[0x1000].get("DEFAULTVALUE", 0) & 0x0000ffff lbessard@182: # If profile is not DS-301 or DS-302 lbessard@509: if ProfileNb not in [0, 301, 302]: lbessard@182: # Compile Profile name and path to .prf file lbessard@182: ProfileName = "DS-%d"%ProfileNb lbessard@258: ProfilePath = os.path.join(os.path.split(__file__)[0], "config/%s.prf"%ProfileName) lbessard@182: # Verify that profile is available lbessard@182: if os.path.isfile(ProfilePath): lbessard@182: try: lbessard@182: # Load Profile lbessard@182: execfile(ProfilePath) lbessard@182: Node.SetProfileName(ProfileName) lbessard@182: Node.SetProfile(Mapping) lbessard@182: Node.SetSpecificMenu(AddMenuEntries) lbessard@182: except: lbessard@182: pass lbessard@182: # Read all entries in the EDS dictionary laurent@580: for entry, values in eds_dict.iteritems(): lbessard@182: # All sections with a name in keynames are escaped lbessard@182: if entry in SECTION_KEYNAMES: lbessard@182: pass lbessard@182: else: lbessard@182: # Extract informations for the entry lbessard@205: entry_infos = Node.GetEntryInfos(entry) lbessard@182: lbessard@182: # If no informations are available, then we write them lbessard@182: if not entry_infos: lbessard@509: # First case, entry is a DOMAIN or VAR lbessard@509: if values["OBJECTTYPE"] in [2, 7]: lbessard@509: if values["OBJECTTYPE"] == 2: lbessard@509: values["DATATYPE"] = values.get("DATATYPE", 0xF) lbessard@509: if values["DATATYPE"] != 0xF: laurent@580: raise SyntaxError, _("Domain entry 0x%4.4X DataType must be 0xF(DOMAIN) if defined")%entry lbessard@182: # Add mapping for entry lbessard@182: Node.AddMappingEntry(entry, name = values["PARAMETERNAME"], struct = 1) lbessard@182: # Add mapping for first subindex lbessard@182: Node.AddMappingEntry(entry, 0, values = {"name" : values["PARAMETERNAME"], lbessard@182: "type" : values["DATATYPE"], lbessard@309: "access" : ACCESS_TRANSLATE[values["ACCESSTYPE"].upper()], lbessard@226: "pdo" : values.get("PDOMAPPING", 0) == 1}) lbessard@309: # Second case, entry is an ARRAY or RECORD lbessard@309: elif values["OBJECTTYPE"] in [8, 9]: lbessard@182: # Extract maximum subindex number defined greg@549: max_subindex = max(values["subindexes"].keys()) lbessard@182: # Add mapping for entry lbessard@182: Node.AddMappingEntry(entry, name = values["PARAMETERNAME"], struct = 3) lbessard@182: # Add mapping for first subindex lbessard@182: Node.AddMappingEntry(entry, 0, values = {"name" : "Number of Entries", "type" : 0x05, "access" : "ro", "pdo" : False}) lbessard@182: # Add mapping for other subindexes lbessard@182: for subindex in xrange(1, int(max_subindex) + 1): lbessard@182: # if subindex is defined lbessard@182: if subindex in values["subindexes"]: lbessard@182: Node.AddMappingEntry(entry, subindex, values = {"name" : values["subindexes"][subindex]["PARAMETERNAME"], lbessard@182: "type" : values["subindexes"][subindex]["DATATYPE"], lbessard@309: "access" : ACCESS_TRANSLATE[values["subindexes"][subindex]["ACCESSTYPE"].upper()], lbessard@226: "pdo" : values["subindexes"][subindex].get("PDOMAPPING", 0) == 1}) lbessard@182: # if not, we add a mapping for compatibility lbessard@182: else: lbessard@182: Node.AddMappingEntry(entry, subindex, values = {"name" : "Compatibility Entry", "type" : 0x05, "access" : "rw", "pdo" : False}) lbessard@309: ## # Third case, entry is an RECORD lbessard@309: ## elif values["OBJECTTYPE"] == 9: lbessard@309: ## # Verify that the first subindex is defined lbessard@309: ## if 0 not in values["subindexes"]: lbessard@309: ## raise SyntaxError, "Error on entry 0x%4.4X:\nSubindex 0 must be defined for a RECORD entry"%entry lbessard@309: ## # Add mapping for entry lbessard@309: ## Node.AddMappingEntry(entry, name = values["PARAMETERNAME"], struct = 7) lbessard@309: ## # Add mapping for first subindex lbessard@309: ## Node.AddMappingEntry(entry, 0, values = {"name" : "Number of Entries", "type" : 0x05, "access" : "ro", "pdo" : False}) lbessard@309: ## # Verify that second subindex is defined lbessard@309: ## if 1 in values["subindexes"]: lbessard@309: ## Node.AddMappingEntry(entry, 1, values = {"name" : values["PARAMETERNAME"] + " %d[(sub)]", lbessard@309: ## "type" : values["subindexes"][1]["DATATYPE"], lbessard@309: ## "access" : ACCESS_TRANSLATE[values["subindexes"][1]["ACCESSTYPE"].upper()], lbessard@309: ## "pdo" : values["subindexes"][1].get("PDOMAPPING", 0) == 1, lbessard@309: ## "nbmax" : 0xFE}) lbessard@309: ## else: lbessard@309: ## raise SyntaxError, "Error on entry 0x%4.4X:\nA RECORD entry must have at least 2 subindexes"%entry lbessard@182: lbessard@182: # Define entry for the new node lbessard@182: lbessard@509: # First case, entry is a DOMAIN or VAR lbessard@509: if values["OBJECTTYPE"] in [2, 7]: lbessard@182: # Take default value if it is defined lbessard@323: if "PARAMETERVALUE" in values: lbessard@323: value = values["PARAMETERVALUE"] lbessard@323: elif "DEFAULTVALUE" in values: lbessard@182: value = values["DEFAULTVALUE"] lbessard@182: # Find default value for value type of the entry lbessard@182: else: lbessard@489: value = GetDefaultValue(Node, entry) lbessard@182: Node.AddEntry(entry, 0, value) lbessard@182: # Second case, entry is an ARRAY or a RECORD lbessard@509: elif values["OBJECTTYPE"] in [8, 9]: lbessard@182: # Verify that "Subnumber" attribute is defined and has a valid value lbessard@182: if "SUBNUMBER" in values and values["SUBNUMBER"] > 0: lbessard@182: # Extract maximum subindex number defined greg@549: max_subindex = max(values["subindexes"].keys()) lbessard@299: Node.AddEntry(entry, value = []) lbessard@182: # Define value for all subindexes except the first lbessard@182: for subindex in xrange(1, int(max_subindex) + 1): lbessard@182: # Take default value if it is defined and entry is defined lbessard@323: if subindex in values["subindexes"] and "PARAMETERVALUE" in values["subindexes"][subindex]: lbessard@323: value = values["subindexes"][subindex]["PARAMETERVALUE"] lbessard@323: elif subindex in values["subindexes"] and "DEFAULTVALUE" in values["subindexes"][subindex]: lbessard@182: value = values["subindexes"][subindex]["DEFAULTVALUE"] lbessard@182: # Find default value for value type of the subindex greg@549: else: lbessard@489: value = GetDefaultValue(Node, entry, subindex) lbessard@182: Node.AddEntry(entry, subindex, value) lbessard@182: else: laurent@580: raise SyntaxError, _("Array or Record entry 0x%4.4X must have a \"SubNumber\" attribute")%entry lbessard@182: return Node lbessard@182: except SyntaxError, message: laurent@580: return _("Unable to import EDS file\n%s")%message lbessard@182: lbessard@182: #------------------------------------------------------------------------------- lbessard@182: # Main Function lbessard@182: #------------------------------------------------------------------------------- lbessard@182: lbessard@182: if __name__ == '__main__': lbessard@205: print ParseEDSFile("examples/PEAK MicroMod.eds") lbessard@205: