nico@207: nico@207: nico@207: CanFestival: /home/epimerde/documents/tc11/CanFestival-3/objdictgen/eds_utils.py Source File nico@207: nico@207: nico@207: nico@207: nico@207:
nico@207:
nico@207:
nico@207:
nico@207:

/home/epimerde/documents/tc11/CanFestival-3/objdictgen/eds_utils.py

Go to the documentation of this file.
00001 #!/usr/bin/env python
nico@207: 00002 # -*- coding: utf-8 -*-
nico@207: 00003 
nico@207: 00004 #This file is part of CanFestival, a library implementing CanOpen Stack. 
nico@207: 00005 #
nico@207: 00006 #Copyright (C): Edouard TISSERANT, Francis DUPIN and Laurent BESSARD
nico@207: 00007 #
nico@207: 00008 #See COPYING file for copyrights details.
nico@207: 00009 #
nico@207: 00010 #This library is free software; you can redistribute it and/or
nico@207: 00011 #modify it under the terms of the GNU Lesser General Public
nico@207: 00012 #License as published by the Free Software Foundation; either
nico@207: 00013 #version 2.1 of the License, or (at your option) any later version.
nico@207: 00014 #
nico@207: 00015 #This library is distributed in the hope that it will be useful,
nico@207: 00016 #but WITHOUT ANY WARRANTY; without even the implied warranty of
nico@207: 00017 #MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
nico@207: 00018 #Lesser General Public License for more details.
nico@207: 00019 #
nico@207: 00020 #You should have received a copy of the GNU Lesser General Public
nico@207: 00021 #License along with this library; if not, write to the Free Software
nico@207: 00022 #Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
nico@207: 00023 
nico@207: 00024 
nico@207: 00025 import node
nico@207: 00026 from node import nosub, var, array, rec, plurivar, pluriarray, plurirec
nico@207: 00027 from sets import *
nico@207: 00028 from types import *
nico@207: 00029 from time import *
nico@207: 00030 import os,re
nico@207: 00031 
nico@207: 00032 # Regular expression for finding index section names
nico@207: 00033 index_model = re.compile('([0-9A-F]{1,4})')
nico@207: 00034 # Regular expression for finding subindex section names
nico@207: 00035 subindex_model = re.compile('([0-9A-F]{1,4})SUB([0-9A-F]{1,2})')
nico@207: 00036 
nico@207: 00037 # Regular expression for finding NodeXPresent keynames
nico@207: 00038 nodepresent_model = re.compile('NODE([0-9]{1,3})PRESENT')
nico@207: 00039 # Regular expression for finding NodeXName keynames
nico@207: 00040 nodename_model = re.compile('NODE([0-9]{1,3})NAME')
nico@207: 00041 # Regular expression for finding NodeXDCFName keynames
nico@207: 00042 nodedcfname_model = re.compile('NODE([0-9]{1,3})DCFNAME')
nico@207: 00043 
nico@207: 00044 # Dictionary for quickly translate boolean into integer value
nico@207: 00045 BOOL_TRANSLATE = {True : "1", False : "0"}
nico@207: 00046 
nico@207: 00047 # Dictionary for quickly translate eds access value into canfestival access value
nico@207: 00048 ACCESS_TRANSLATE = {"ro" : "ro", "wo" : "wo", "rw" : "rw", "rwr" : "rw", "rww" : "rw", "const" : "ro"}
nico@207: 00049 
nico@207: 00050 # Function for verifying data values
nico@207: 00051 is_integer = lambda x: type(x) in (IntType, LongType)
nico@207: 00052 is_string = lambda x: type(x) in (StringType, UnicodeType)
nico@207: 00053 is_boolean = lambda x: x in (0, 1)
nico@207: 00054 
nico@207: 00055 # Define checking of value for each attribute
nico@207: 00056 ENTRY_ATTRIBUTES = {"SUBNUMBER" : is_integer, "PARAMETERNAME" : is_string, 
nico@207: 00057                     "OBJECTTYPE" : lambda x: x in (7, 8, 9), "DATATYPE" : is_integer, 
nico@207: 00058                     "LOWLIMIT" : is_integer, "HIGHLIMIT" : is_integer,
nico@207: 00059                     "ACCESSTYPE" : lambda x: x in ["ro","wo", "rw", "rwr", "rww", "const"],
nico@207: 00060                     "DEFAULTVALUE" : lambda x: True, "PDOMAPPING" : is_boolean,
nico@207: 00061                     "OBJFLAGS" : is_integer}
nico@207: 00062 
nico@207: 00063 # Define entry parameters by entry ObjectType number
nico@207: 00064 ENTRY_TYPES = {7 : {"name" : " VAR",
nico@207: 00065                     "require" : ["PARAMETERNAME", "OBJECTTYPE", "DATATYPE", "ACCESSTYPE", "PDOMAPPING"],
nico@207: 00066                     "optional" : ["LOWLIMIT", "HIGHLIMIT", "DEFAULTVALUE", "OBJFLAGS"]},
nico@207: 00067                8 : {"name" : "n ARRAY",
nico@207: 00068                     "require" : ["SUBNUMBER", "PARAMETERNAME", "OBJECTTYPE"],
nico@207: 00069                     "optional" : ["OBJFLAGS"]},
nico@207: 00070                9 : {"name" : " RECORD",
nico@207: 00071                     "require" : ["SUBNUMBER", "PARAMETERNAME", "OBJECTTYPE"],
nico@207: 00072                     "optional" : ["OBJFLAGS"]}}
nico@207: 00073 
nico@207: 00074 
nico@207: 00075 # Function that search into Node Mappings the informations about an index or a subindex
nico@207: 00076 # and return the default value
nico@207: 00077 def GetDefaultValue(index, subIndex = None):
nico@207: 00078     infos = Node.GetEntryInfos(index)
nico@207: 00079     if infos["struct"] & node.OD_MultipleSubindexes:
nico@207: 00080         # First case entry is a record
nico@207: 00081         if infos["struct"] & node.OD_IdenticalSubindexes:
nico@207: 00082             subentry_infos = Node.GetSubentryInfos(index, 1)
nico@207: 00083         # Second case entry is an array
nico@207: 00084         else:
nico@207: 00085             subentry_infos = Node.GetSubentryInfos(index, subIndex)
nico@207: 00086         # If a default value is defined for this subindex, returns it
nico@207: 00087         if "default" in subentry_infos:
nico@207: 00088             return subentry_infos["default"]
nico@207: 00089         # If not, returns the default value for the subindex type
nico@207: 00090         else:
nico@207: 00091             return Node.GetTypeDefaultValue(subentry_infos["type"])
nico@207: 00092     # Third case entry is a var
nico@207: 00093     else:
nico@207: 00094         subentry_infos = Node.GetSubentryInfos(index, 0)
nico@207: 00095         # If a default value is defined for this subindex, returns it
nico@207: 00096         if "default" in subentry_infos:
nico@207: 00097             return subentry_infos["default"]
nico@207: 00098         # If not, returns the default value for the subindex type
nico@207: 00099         else:
nico@207: 00100             return Node.GetTypeDefaultValue(subentry_infos["type"])
nico@207: 00101     return None
nico@207: 00102 
nico@207: 00103 
nico@207: 00104 #-------------------------------------------------------------------------------
nico@207: 00105 #                               Parse file
nico@207: 00106 #-------------------------------------------------------------------------------
nico@207: 00107 
nico@207: 00108 
nico@207: 00109 # List of section names that are not index and subindex and that we can meet in
nico@207: 00110 # an EDS file
nico@207: 00111 SECTION_KEYNAMES = ["FILEINFO", "DEVICEINFO", "DUMMYUSAGE", "COMMENTS", 
nico@207: 00112                     "MANDATORYOBJECTS", "OPTIONALOBJECTS", "MANUFACTUREROBJECTS"]
nico@207: 00113 
nico@207: 00114 
nico@207: 00115 # Function that extract sections from a file and returns a dictionary of the informations
nico@207: 00116 def ExtractSections(file):
nico@207: 00117     return [(blocktuple[0],                # EntryName : Assignements dict
nico@207: 00118              blocktuple[-1].splitlines())  # all the lines
nico@207: 00119              for blocktuple in [           # Split the eds files into
nico@207: 00120              block.split("]")              # (EntryName,Assignements) tuple
nico@207: 00121              for block in                  # for each blocks staring with '['
nico@207: 00122              file.split("[")]
nico@207: 00123              if blocktuple[0].isalnum()]   # if EntryName exists
nico@207: 00124     
nico@207: 00125 
nico@207: 00126 # Function that parse an CPJ file and returns a dictionary of the informations
nico@207: 00127 def ParseCPJFile(filepath):
nico@207: 00128     networks = []
nico@207: 00129     # Read file text
nico@207: 00130     cpj_file = open(filepath,'r').read()
nico@207: 00131     sections = ExtractSections(cpj_file)
nico@207: 00132     # Parse assignments for each section
nico@207: 00133     for section_name, assignments in sections:
nico@207: 00134         
nico@207: 00135         # Verify that section name is TOPOLOGY 
nico@207: 00136         if section_name.upper() in "TOPOLOGY":
nico@207: 00137             
nico@207: 00138             # Reset values for topology
nico@207: 00139             topology = {"Name" : "", "Nodes" : {}}
nico@207: 00140             
nico@207: 00141             for assignment in assignments:
nico@207: 00142                 # Escape any comment
nico@207: 00143                 if assignment.startswith(";"):
nico@207: 00144                     pass
nico@207: 00145                 # Verify that line is a valid assignment
nico@207: 00146                 elif assignment.find('=') > 0:
nico@207: 00147                     # Split assignment into the two values keyname and value
nico@207: 00148                     # Verify that there is only one '=' character in the line
nico@207: 00149                     try:
nico@207: 00150                         keyname, value = assignment.split("=")
nico@207: 00151                     except:
nico@207: 00152                         raise SyntaxError, "\"%s\" is not a valid EDS line"%assignment.strip()
nico@207: 00153                     
nico@207: 00154                     # keyname must be immediately followed by the "=" sign, so we
nico@207: 00155                     # verify that there is no whitespace into keyname
nico@207: 00156                     if keyname.isalnum():
nico@207: 00157                         # value can be preceded and followed by whitespaces, so we escape them
nico@207: 00158                         value = value.strip()
nico@207: 00159                 
nico@207: 00160                         # First case, value starts with "0x", then it's an hexadecimal value
nico@207: 00161                         if value.startswith("0x"):
nico@207: 00162                             try:
nico@207: 00163                                 computed_value = int(value, 16)
nico@207: 00164                             except:
nico@207: 00165                                 raise SyntaxError, "\"%s\" is not a valid value for attribute \"%s\" of section \"[%s]\""%(value, keyname, section_name)
nico@207: 00166                         elif value.isdigit():
nico@207: 00167                             # Second case, value is a number and starts with "0", then it's an octal value
nico@207: 00168                             if value.startswith("0"):
nico@207: 00169                                 computed_value = int(value, 8)
nico@207: 00170                             # Third case, value is a number and don't start with "0", then it's a decimal value
nico@207: 00171                             else:
nico@207: 00172                                 computed_value = int(value)
nico@207: 00173                         # In any other case, we keep string value
nico@207: 00174                         else:
nico@207: 00175                             computed_value = value
nico@207: 00176                         
nico@207: 00177                         # Search if the section name match any cpj expression
nico@207: 00178                         nodepresent_result = nodepresent_model.match(keyname.upper())
nico@207: 00179                         nodename_result = nodename_model.match(keyname.upper())
nico@207: 00180                         nodedcfname_result = nodedcfname_model.match(keyname.upper())
nico@207: 00181                         
nico@207: 00182                         if keyname.upper() == "NETNAME":
nico@207: 00183                             if not is_string(computed_value):
nico@207: 00184                                 raise SyntaxError, "Invalid value \"%s\" for keyname \"%s\" of section \"[%s]\""%(value, keyname, section_name)
nico@207: 00185                             topology["Name"] = computed_value
nico@207: 00186                         elif keyname.upper() == "NODES":
nico@207: 00187                             if not is_integer(computed_value):
nico@207: 00188                                 raise SyntaxError, "Invalid value \"%s\" for keyname \"%s\" of section \"[%s]\""%(value, keyname, section_name)
nico@207: 00189                             topology["Number"] = computed_value
nico@207: 00190                         elif keyname.upper() == "EDSBASENAME":
nico@207: 00191                             if not is_string(computed_value):
nico@207: 00192                                 raise SyntaxError, "Invalid value \"%s\" for keyname \"%s\" of section \"[%s]\""%(value, keyname, section_name)
nico@207: 00193                             topology["Path"] = computed_value
nico@207: 00194                         elif nodepresent_result:
nico@207: 00195                             if not is_boolean(computed_value):
nico@207: 00196                                 raise SyntaxError, "Invalid value \"%s\" for keyname \"%s\" of section \"[%s]\""%(value, keyname, section_name)
nico@207: 00197                             nodeid = int(nodepresent_result.groups()[0])
nico@207: 00198                             if nodeid not in topology["Nodes"].keys():
nico@207: 00199                                 topology["Nodes"][nodeid] = {}
nico@207: 00200                             topology["Nodes"][nodeid]["Present"] = computed_value
nico@207: 00201                         elif nodename_result:
nico@207: 00202                             if not is_string(value):
nico@207: 00203                                 raise SyntaxError, "Invalid value \"%s\" for keyname \"%s\" of section \"[%s]\""%(value, keyname, section_name)
nico@207: 00204                             nodeid = int(nodename_result.groups()[0])
nico@207: 00205                             if nodeid not in topology["Nodes"].keys():
nico@207: 00206                                 topology["Nodes"][nodeid] = {}
nico@207: 00207                             topology["Nodes"][nodeid]["Name"] = computed_value
nico@207: 00208                         elif nodedcfname_result:
nico@207: 00209                             if not is_string(computed_value):
nico@207: 00210                                 raise SyntaxError, "Invalid value \"%s\" for keyname \"%s\" of section \"[%s]\""%(value, keyname, section_name)
nico@207: 00211                             nodeid = int(nodedcfname_result.groups()[0])
nico@207: 00212                             if nodeid not in topology["Nodes"].keys():
nico@207: 00213                                 topology["Nodes"][nodeid] = {}
nico@207: 00214                             topology["Nodes"][nodeid]["DCFName"] = computed_value
nico@207: 00215                         else:
nico@207: 00216                             raise SyntaxError, "Keyname \"%s\" not recognised for section \"[%s]\""%(keyname, section_name)
nico@207: 00217                         
nico@207: 00218                 # All lines that are not empty and are neither a comment neither not a valid assignment
nico@207: 00219                 elif assignment.strip() != "":
nico@207: 00220                     raise SyntaxError, "\"%s\" is not a valid CPJ line"%assignment.strip()
nico@207: 00221         
nico@207: 00222             if "Number" not in topology.keys():
nico@207: 00223                 raise SyntaxError, "\"Nodes\" keyname in \"[%s]\" section is missing"%section_name
nico@207: 00224         
nico@207: 00225             if topology["Number"] != len(topology["Nodes"]):
nico@207: 00226                 raise SyntaxError, "\"Nodes\" value not corresponding to number of nodes defined"
nico@207: 00227             
nico@207: 00228             for nodeid, node in topology["Nodes"].items():
nico@207: 00229                 if "Present" not in node.keys():
nico@207: 00230                     raise SyntaxError, "\"Node%dPresent\" keyname in \"[%s]\" section is missing"%(nodeid, section_name)
nico@207: 00231             
nico@207: 00232             networks.append(topology)
nico@207: 00233             
nico@207: 00234         # In other case, there is a syntax problem into CPJ file
nico@207: 00235         else:
nico@207: 00236             raise SyntaxError, "Section \"[%s]\" is unrecognized"%section_name
nico@207: 00237     
nico@207: 00238     return networks
nico@207: 00239 
nico@207: 00240 # Function that parse an EDS file and returns a dictionary of the informations
nico@207: 00241 def ParseEDSFile(filepath):
nico@207: 00242     eds_dict = {}
nico@207: 00243     # Read file text
nico@207: 00244     eds_file = open(filepath,'r').read()
nico@207: 00245     sections = ExtractSections(eds_file)
nico@207: 00246     
nico@207: 00247     # Parse assignments for each section
nico@207: 00248     for section_name, assignments in sections:
nico@207: 00249         # Reset values of entry
nico@207: 00250         values = {}
nico@207: 00251         
nico@207: 00252         # Search if the section name match an index or subindex expression
nico@207: 00253         index_result = index_model.match(section_name.upper())
nico@207: 00254         subindex_result = subindex_model.match(section_name.upper())
nico@207: 00255         
nico@207: 00256         # Compilation of the EDS information dictionary
nico@207: 00257         
nico@207: 00258         is_entry = False
nico@207: 00259         # First case, section name is in SECTION_KEYNAMES 
nico@207: 00260         if section_name.upper() in SECTION_KEYNAMES:
nico@207: 00261             # Verify that entry is not already defined
nico@207: 00262             if section_name.upper() not in eds_dict:
nico@207: 00263                 eds_dict[section_name.upper()] = values
nico@207: 00264             else:
nico@207: 00265                 raise SyntaxError, "\"[%s]\" section is defined two times"%section_name
nico@207: 00266         # Second case, section name is a subindex name 
nico@207: 00267         elif subindex_result:
nico@207: 00268             # Extract index and subindex number
nico@207: 00269             index, subindex = [int(value, 16) for value in subindex_result.groups()]
nico@207: 00270             # If index hasn't been referenced before, we add an entry into the dictionary
nico@207: 00271             # that will be updated later
nico@207: 00272             if index not in eds_dict:
nico@207: 00273                 eds_dict[index] = {"subindexes" : {}}
nico@207: 00274             if subindex not in eds_dict[index]["subindexes"]:
nico@207: 00275                 eds_dict[index]["subindexes"][subindex] = values
nico@207: 00276             else:
nico@207: 00277                 raise SyntaxError, "\"[%s]\" section is defined two times"%section_name
nico@207: 00278             is_entry = True
nico@207: 00279         # Third case, section name is an index name 
nico@207: 00280         elif index_result:
nico@207: 00281             # Extract index number
nico@207: 00282             index = int(index_result.groups()[0], 16)
nico@207: 00283             # If index hasn't been referenced before, we add an entry into the dictionary
nico@207: 00284             if index not in eds_dict:
nico@207: 00285                 eds_dict[index] = values
nico@207: 00286                 eds_dict[index]["subindexes"] = {}
nico@207: 00287             elif eds_dict[index].keys() == ["subindexes"]:
nico@207: 00288                 values["subindexes"] = eds_dict[index]["subindexes"]
nico@207: 00289                 eds_dict[index] = values
nico@207: 00290             else:
nico@207: 00291                 raise SyntaxError, "\"[%s]\" section is defined two times"%section_name
nico@207: 00292             is_entry = True
nico@207: 00293         # In any other case, there is a syntax problem into EDS file
nico@207: 00294         else:
nico@207: 00295             raise SyntaxError, "Section \"[%s]\" is unrecognized"%section_name
nico@207: 00296         
nico@207: 00297         for assignment in assignments:
nico@207: 00298             # Escape any comment
nico@207: 00299             if assignment.startswith(";"):
nico@207: 00300                 pass
nico@207: 00301             # Verify that line is a valid assignment
nico@207: 00302             elif assignment.find('=') > 0:
nico@207: 00303                 # Split assignment into the two values keyname and value
nico@207: 00304                 # Verify that there is only one '=' character in the line
nico@207: 00305                 try:
nico@207: 00306                     keyname, value = assignment.split("=")
nico@207: 00307                 except:
nico@207: 00308                     raise SyntaxError, "\"%s\" is not a valid EDS line"%assignment.strip()
nico@207: 00309                 # keyname must be immediately followed by the "=" sign, so we
nico@207: 00310                 # verify that there is no whitespace into keyname
nico@207: 00311                 if keyname.isalnum():
nico@207: 00312                     # value can be preceded and followed by whitespaces, so we escape them
nico@207: 00313                     value = value.strip()
nico@207: 00314                     # First case, value starts with "$NODEID", then it's a formula
nico@207: 00315                     if value.startswith("$NODEID"):
nico@207: 00316                         try:
nico@207: 00317                             test = int(value.replace("$NODEID+", ""), 16)
nico@207: 00318                             computed_value = value.replace("$NODEID", "self.ID")
nico@207: 00319                         except:
nico@207: 00320                             raise SyntaxError, "\"%s\" is not a valid formula for attribute \"%s\" of section \"[%s]\""%(value, keyname, section_name)
nico@207: 00321                     # Second case, value starts with "0x", then it's an hexadecimal value
nico@207: 00322                     elif value.startswith("0x"):
nico@207: 00323                         try:
nico@207: 00324                             computed_value = int(value, 16)
nico@207: 00325                         except:
nico@207: 00326                             raise SyntaxError, "\"%s\" is not a valid value for attribute \"%s\" of section \"[%s]\""%(value, keyname, section_name)
nico@207: 00327                     elif value.isdigit():
nico@207: 00328                         # Third case, value is a number and starts with "0", then it's an octal value
nico@207: 00329                         if value.startswith("0"):
nico@207: 00330                             computed_value = int(value, 8)
nico@207: 00331                         # Forth case, value is a number and don't start with "0", then it's a decimal value
nico@207: 00332                         else:
nico@207: 00333                             computed_value = int(value)
nico@207: 00334                     # In any other case, we keep string value
nico@207: 00335                     else:
nico@207: 00336                         computed_value = value
nico@207: 00337                     
nico@207: 00338                     # Add value to values dictionary
nico@207: 00339                     if computed_value != "":
nico@207: 00340                         # If entry is an index or a subindex
nico@207: 00341                         if is_entry:
nico@207: 00342                             # Verify that keyname is a possible attribute
nico@207: 00343                             if keyname.upper() not in ENTRY_ATTRIBUTES:
nico@207: 00344                                 raise SyntaxError, "Keyname \"%s\" not recognised for section \"[%s]\""%(keyname, section_name)
nico@207: 00345                             # Verify that value is valid
nico@207: 00346                             elif not ENTRY_ATTRIBUTES[keyname.upper()](computed_value):
nico@207: 00347                                 raise SyntaxError, "Invalid value \"%s\" for keyname \"%s\" of section \"[%s]\""%(value, keyname, section_name)
nico@207: 00348                             else:
nico@207: 00349                                 values[keyname.upper()] = computed_value
nico@207: 00350                         else:
nico@207: 00351                             values[keyname.upper()] = computed_value
nico@207: 00352             # All lines that are not empty and are neither a comment neither not a valid assignment
nico@207: 00353             elif assignment.strip() != "":
nico@207: 00354                 raise SyntaxError, "\"%s\" is not a valid EDS line"%assignment.strip()
nico@207: 00355         
nico@207: 00356         # If entry is an index or a subindex
nico@207: 00357         if is_entry:
nico@207: 00358             # Verify that entry has an ObjectType
nico@207: 00359             if "OBJECTTYPE" in values.keys():
nico@207: 00360                 # Extract entry ObjectType
nico@207: 00361                 objecttype = values["OBJECTTYPE"]
nico@207: 00362                 # Extract parameters defined
nico@207: 00363                 keys = Set(values.keys())
nico@207: 00364                 keys.discard("subindexes")
nico@207: 00365                 # Extract possible parameters and parameters required
nico@207: 00366                 possible = Set(ENTRY_TYPES[objecttype]["require"] + ENTRY_TYPES[objecttype]["optional"])
nico@207: 00367                 required = Set(ENTRY_TYPES[objecttype]["require"])
nico@207: 00368                 # Verify that parameters defined contains all the parameters required
nico@207: 00369                 if not keys.issuperset(required):
nico@207: 00370                     missing = required.difference(keys)._data.keys()
nico@207: 00371                     if len(missing) > 1:
nico@207: 00372                         attributes = "Attributes %s are"%", ".join(["\"%s\""%attribute for attribute in missing])
nico@207: 00373                     else:
nico@207: 00374                         attributes = "Attribute \"%s\" is"%missing[0]
nico@207: 00375                     raise SyntaxError, "Error on section \"[%s]\":\n%s required for a%s entry"%(section_name, attributes, ENTRY_TYPES[objecttype]["name"])
nico@207: 00376                 # Verify that parameters defined are all in the possible parameters
nico@207: 00377                 if not keys.issubset(possible):
nico@207: 00378                     unsupported = keys.difference(possible)._data.keys()
nico@207: 00379                     if len(unsupported) > 1:
nico@207: 00380                         attributes = "Attributes %s are"%", ".join(["\"%s\""%attribute for attribute in unsupported])
nico@207: 00381                     else:
nico@207: 00382                         attributes = "Attribute \"%s\" is"%unsupported[0]
nico@207: 00383                     raise SyntaxError, "Error on section \"[%s]\":\n%s unsupported for a%s entry"%(section_name, attributes, ENTRY_TYPES[objecttype]["name"])
nico@207: 00384             else:
nico@207: 00385                 raise SyntaxError, "Error on section \"[%s]\":\nAttribute OBJECTTYPE is required"%section_name
nico@207: 00386         
nico@207: 00387     return eds_dict
nico@207: 00388 
nico@207: 00389 
nico@207: 00390 # Function that write an EDS file after generate it's content
nico@207: 00391 def WriteFile(filepath, content):
nico@207: 00392     # Open file in write mode
nico@207: 00393     cfile = open(filepath,"w")
nico@207: 00394     # Write content
nico@207: 00395     cfile.write(content)
nico@207: 00396     # Close file
nico@207: 00397     cfile.close()
nico@207: 00398 
nico@207: 00399 
nico@207: 00400 # Function that generate the EDS file content for the current node in the manager
nico@207: 00401 def GenerateFileContent(filepath):
nico@207: 00402     # Dictionary of each index contents
nico@207: 00403     indexContents = {}
nico@207: 00404     
nico@207: 00405     # Extract local time
nico@207: 00406     current_time = localtime()
nico@207: 00407     # Extract node informations
nico@207: 00408     nodename, nodeid, nodetype, description = Manager.GetCurrentNodeInfos()
nico@207: 00409     
nico@207: 00410     # Compiling lists of indexes defined
nico@207: 00411     entries = [idx for name, idx in Manager.GetCurrentValidIndexes(0, 0xFFFF)]
nico@207: 00412     
nico@207: 00413     # Generate FileInfo section
nico@207: 00414     fileContent = "[FileInfo]\n"
nico@207: 00415     fileContent += "CreatedBy=CANFestival\n"
nico@207: 00416     fileContent += "Description=%s\n"%description
nico@207: 00417     fileContent += "CreationTime=%s"%strftime("%I:%M", current_time)
nico@207: 00418     # %p option of strftime seems not working, then generate AM/PM by hands
nico@207: 00419     if strftime("%I", current_time) == strftime("%H", current_time):
nico@207: 00420         fileContent += "AM\n"
nico@207: 00421     else:
nico@207: 00422         fileContent += "PM\n"
nico@207: 00423     fileContent += "CreationDate=%s\n"%strftime("%m-%d-%Y", current_time)
nico@207: 00424     fileContent += "FileName=%s\n"%os.path.split(filepath)[-1]
nico@207: 00425     fileContent += "FileVersion=1\n"
nico@207: 00426     fileContent += "FileRevision=1\n"
nico@207: 00427     fileContent += "EDSVersion=3.0\n"
nico@207: 00428     
nico@207: 00429     # Generate DeviceInfo section
nico@207: 00430     fileContent += "\n[DeviceInfo]\n"
nico@207: 00431     fileContent += "VendorName=CANFestival\n"
nico@207: 00432     # Use information typed by user in Identity entry
nico@207: 00433     fileContent += "VendorNumber=0x%8.8X\n"%Manager.GetCurrentEntry(0x1018, 1)
nico@207: 00434     fileContent += "ProductName=%s\n"%nodename
nico@207: 00435     fileContent += "ProductNumber=0x%8.8X\n"%Manager.GetCurrentEntry(0x1018, 2)
nico@207: 00436     fileContent += "RevisionNumber=0x%8.8X\n"%Manager.GetCurrentEntry(0x1018, 3)
nico@207: 00437     # CANFestival support all baudrates as soon as driver choosen support them
nico@207: 00438     fileContent += "BaudRate_10=1\n"
nico@207: 00439     fileContent += "BaudRate_20=1\n"
nico@207: 00440     fileContent += "BaudRate_50=1\n"
nico@207: 00441     fileContent += "BaudRate_125=1\n"
nico@207: 00442     fileContent += "BaudRate_250=1\n"
nico@207: 00443     fileContent += "BaudRate_500=1\n"
nico@207: 00444     fileContent += "BaudRate_800=1\n"
nico@207: 00445     fileContent += "BaudRate_1000=1\n"
nico@207: 00446     # Select BootUp type from the informations given by user
nico@207: 00447     fileContent += "SimpleBootUpMaster=%s\n"%BOOL_TRANSLATE[nodetype == "master"]
nico@207: 00448     fileContent += "SimpleBootUpSlave=%s\n"%BOOL_TRANSLATE[nodetype == "slave"]
nico@207: 00449     # CANFestival characteristics
nico@207: 00450     fileContent += "Granularity=8\n"
nico@207: 00451     fileContent += "DynamicChannelsSupported=0\n"
nico@207: 00452     fileContent += "CompactPDO=0\n"
nico@207: 00453     fileContent += "GroupMessaging=0\n"
nico@207: 00454     # Calculate receive and tranmit PDO numbers with the entry available
nico@207: 00455     fileContent += "NrOfRXPDO=%d\n"%len([idx for idx in entries if 0x1400 <= idx <= 0x15FF])
nico@207: 00456     fileContent += "NrOfTXPDO=%d\n"%len([idx for idx in entries if 0x1800 <= idx <= 0x19FF])
nico@207: 00457     # LSS not supported as soon as DS-302 was not fully implemented
nico@207: 00458     fileContent += "LSS_Supported=0\n"
nico@207: 00459     
nico@207: 00460     # Generate Dummy Usage section
nico@207: 00461     fileContent += "\n[DummyUsage]\n"
nico@207: 00462     fileContent += "Dummy0001=0\n"
nico@207: 00463     fileContent += "Dummy0002=1\n"
nico@207: 00464     fileContent += "Dummy0003=1\n"
nico@207: 00465     fileContent += "Dummy0004=1\n"
nico@207: 00466     fileContent += "Dummy0005=1\n"
nico@207: 00467     fileContent += "Dummy0006=1\n"
nico@207: 00468     fileContent += "Dummy0007=1\n"
nico@207: 00469 
nico@207: 00470     # Generate Comments section
nico@207: 00471     fileContent += "\n[Comments]\n"
nico@207: 00472     fileContent += "Lines=0\n"
nico@207: 00473     
nico@207: 00474     # List of entry by type (Mandatory, Optional or Manufacturer
nico@207: 00475     mandatories = []
nico@207: 00476     optionals = []
nico@207: 00477     manufacturers = []
nico@207: 00478     
nico@207: 00479     # For each entry, we generate the entry section or sections if there is subindexes
nico@207: 00480     for entry in entries:
nico@207: 00481         # Extract infos and values for the entry
nico@207: 00482         entry_infos = Manager.GetEntryInfos(entry)
nico@207: 00483         values = Manager.GetCurrentEntry(entry)
nico@207: 00484         # Define section name
nico@207: 00485         text = "\n[%X]\n"%entry
nico@207: 00486         # If there is only one value, it's a VAR entry
nico@207: 00487         if type(values) != ListType:
nico@207: 00488             # Extract the informations of the first subindex
nico@207: 00489             subentry_infos = Manager.GetSubentryInfos(entry, 0)
nico@207: 00490             # Generate EDS informations for the entry
nico@207: 00491             text += "ParameterName=%s\n"%subentry_infos["name"]
nico@207: 00492             text += "ObjectType=0x7\n"
nico@207: 00493             text += "DataType=0x%4.4X\n"%subentry_infos["type"]
nico@207: 00494             text += "AccessType=%s\n"%subentry_infos["access"]
nico@207: 00495             text += "DefaultValue=%s\n"%values
nico@207: 00496             text += "PDOMapping=%s\n"%BOOL_TRANSLATE[subentry_infos["pdo"]]
nico@207: 00497         else:
nico@207: 00498             # Generate EDS informations for the entry
nico@207: 00499             text += "ParameterName=%s\n"%entry_infos["name"]
nico@207: 00500             if entry_infos["struct"] & node.OD_IdenticalSubindexes:
nico@207: 00501                 text += "ObjectType=0x9\n"
nico@207: 00502             else:
nico@207: 00503                 text += "ObjectType=0x8\n"
nico@207: 00504             
nico@207: 00505             # Generate EDS informations for subindexes of the entry in a separate text
nico@207: 00506             subtext = ""
nico@207: 00507             # Reset number of subindex defined 
nico@207: 00508             nb_subentry = 0
nico@207: 00509             for subentry, value in enumerate(values):
nico@207: 00510                 # Extract the informations of each subindex
nico@207: 00511                 subentry_infos = Manager.GetSubentryInfos(entry, subentry)
nico@207: 00512                 # If entry is not for the compatibility, generate informations for subindex
nico@207: 00513                 if subentry_infos["name"] != "Compatibility Entry":
nico@207: 00514                     subtext += "\n[%Xsub%X]\n"%(entry, subentry)
nico@207: 00515                     subtext += "ParameterName=%s\n"%subentry_infos["name"]
nico@207: 00516                     subtext += "ObjectType=0x7\n"
nico@207: 00517                     subtext += "DataType=0x%4.4X\n"%subentry_infos["type"]
nico@207: 00518                     subtext += "AccessType=%s\n"%subentry_infos["access"]
nico@207: 00519                     subtext += "DefaultValue=%s\n"%value
nico@207: 00520                     subtext += "PDOMapping=%s\n"%BOOL_TRANSLATE[subentry_infos["pdo"]]
nico@207: 00521                     # Increment number of subindex defined 
nico@207: 00522                     nb_subentry += 1
nico@207: 00523             # Write number of subindex defined for the entry
nico@207: 00524             text += "SubNumber=%d\n"%nb_subentry
nico@207: 00525             # Write subindex definitions
nico@207: 00526             text += subtext
nico@207: 00527         
nico@207: 00528         # Then we add the entry in the right list
nico@207: 00529         
nico@207: 00530         # First case, entry is between 0x2000 and 0x5FFF, then it's a manufacturer entry
nico@207: 00531         if 0x2000 <= entry <= 0x5FFF:
nico@207: 00532             manufacturers.append(entry)
nico@207: 00533         # Second case, entry is required, then it's a mandatory entry
nico@207: 00534         elif entry_infos["need"]:
nico@207: 00535             mandatories.append(entry)
nico@207: 00536         # In any other case, it's an optional entry
nico@207: 00537         else:
nico@207: 00538             optionals.append(entry)
nico@207: 00539         # Save text of the entry in the dictiionary of contents
nico@207: 00540         indexContents[entry] = text
nico@207: 00541     
nico@207: 00542     # Before generate File Content we sort the entry list
nico@207: 00543     manufacturers.sort()
nico@207: 00544     mandatories.sort()
nico@207: 00545     optionals.sort()
nico@207: 00546     
nico@207: 00547     # Generate Definition of mandatory objects
nico@207: 00548     fileContent += "\n[MandatoryObjects]\n"
nico@207: 00549     fileContent += "SupportedObjects=%d\n"%len(mandatories)
nico@207: 00550     for idx, entry in enumerate(mandatories):
nico@207: 00551         fileContent += "%d=0x%4.4X\n"%(idx, entry)
nico@207: 00552     # Write mandatory entries
nico@207: 00553     for entry in mandatories:
nico@207: 00554         fileContent += indexContents[entry]
nico@207: 00555     
nico@207: 00556     # Generate Definition of optional objects
nico@207: 00557     fileContent += "\n[OptionalObjects]\n"
nico@207: 00558     fileContent += "SupportedObjects=%d\n"%len(optionals)
nico@207: 00559     for idx, entry in enumerate(optionals):
nico@207: 00560         fileContent += "%d=0x%4.4X\n"%(idx, entry)
nico@207: 00561     # Write optional entries
nico@207: 00562     for entry in optionals:
nico@207: 00563         fileContent += indexContents[entry]
nico@207: 00564 
nico@207: 00565     # Generate Definition of manufacturer objects
nico@207: 00566     fileContent += "\n[ManufacturerObjects]\n"
nico@207: 00567     fileContent += "SupportedObjects=%d\n"%len(manufacturers)
nico@207: 00568     for idx, entry in enumerate(manufacturers):
nico@207: 00569         fileContent += "%d=0x%4.4X\n"%(idx, entry)
nico@207: 00570     # Write manufacturer entries
nico@207: 00571     for entry in manufacturers:
nico@207: 00572         fileContent += indexContents[entry]
nico@207: 00573     
nico@207: 00574     # Return File Content
nico@207: 00575     return fileContent
nico@207: 00576 
nico@207: 00577 
nico@207: 00578 # Function that generates EDS file from current node edited
nico@207: 00579 def GenerateEDSFile(filepath, manager):
nico@207: 00580     global Manager
nico@207: 00581     Manager = manager
nico@207: 00582     try:
nico@207: 00583         # Generate file content
nico@207: 00584         content = GenerateFileContent(filepath)
nico@207: 00585         # Write file
nico@207: 00586         WriteFile(filepath, content)
nico@207: 00587         return None
nico@207: 00588     except ValueError, message:
nico@207: 00589         return "Unable to generate EDS file\n%s"%message
nico@207: 00590     
nico@207: 00591 # Function that generate the CPJ file content for the nodelist
nico@207: 00592 def GenerateCPJContent(nodelist):
nico@207: 00593     nodes = nodelist.SlaveNodes.keys()
nico@207: 00594     nodes.sort()
nico@207: 00595     
nico@207: 00596     fileContent = "[TOPOLOGY]\n"
nico@207: 00597     fileContent += "NetName=%s\n"%nodelist.GetNetworkName()
nico@207: 00598     fileContent += "Nodes=0x%2.2X\n"%len(nodes)
nico@207: 00599     
nico@207: 00600     for nodeid in nodes:
nico@207: 00601         fileContent += "Node%dPresent=0x01\n"%nodeid
nico@207: 00602         fileContent += "Node%dName=%s\n"%(nodeid, nodelist.SlaveNodes[nodeid]["Name"])
nico@207: 00603         fileContent += "Node%dDCFName=%s\n"%(nodeid, nodelist.SlaveNodes[nodeid]["EDS"])
nico@207: 00604         
nico@207: 00605     fileContent += "EDSBaseName=eds\n"
nico@207: 00606     return fileContent
nico@207: 00607 
nico@207: 00608 # Function that generates Node from an EDS file
nico@207: 00609 def GenerateNode(filepath, cwd, nodeID = 0):
nico@207: 00610     global Node
nico@207: 00611     # Create a new node
nico@207: 00612     Node = node.Node(id = nodeID)
nico@207: 00613     try:
nico@207: 00614         # Parse file and extract dictionary of EDS entry
nico@207: 00615         eds_dict = ParseEDSFile(filepath)
nico@207: 00616         # Extract Profile Number from Device Type entry
nico@207: 00617         ProfileNb = eds_dict[0x1000]["DEFAULTVALUE"] & 0x0000ffff
nico@207: 00618         # If profile is not DS-301 or DS-302
nico@207: 00619         if ProfileNb not in [301, 302]:
nico@207: 00620             # Compile Profile name and path to .prf file
nico@207: 00621             ProfileName = "DS-%d"%ProfileNb
nico@207: 00622             ProfilePath = os.path.join(cwd, "config/%s.prf"%ProfileName)
nico@207: 00623             # Verify that profile is available
nico@207: 00624             if os.path.isfile(ProfilePath):
nico@207: 00625                 try:
nico@207: 00626                     # Load Profile
nico@207: 00627                     execfile(ProfilePath)
nico@207: 00628                     Node.SetProfileName(ProfileName)
nico@207: 00629                     Node.SetProfile(Mapping)
nico@207: 00630                     Node.SetSpecificMenu(AddMenuEntries)
nico@207: 00631                 except:
nico@207: 00632                     pass
nico@207: 00633         # Read all entries in the EDS dictionary 
nico@207: 00634         for entry, values in eds_dict.items():
nico@207: 00635             # All sections with a name in keynames are escaped
nico@207: 00636             if entry in SECTION_KEYNAMES:
nico@207: 00637                 pass
nico@207: 00638             else:
nico@207: 00639                 # Extract informations for the entry
nico@207: 00640                 entry_infos = Node.GetEntryInfos(entry)
nico@207: 00641                 
nico@207: 00642                 # If no informations are available, then we write them
nico@207: 00643                 if not entry_infos:
nico@207: 00644                     # First case, entry is a VAR
nico@207: 00645                     if values["OBJECTTYPE"] == 7:
nico@207: 00646                         # Add mapping for entry
nico@207: 00647                         Node.AddMappingEntry(entry, name = values["PARAMETERNAME"], struct = 1)
nico@207: 00648                         # Add mapping for first subindex
nico@207: 00649                         Node.AddMappingEntry(entry, 0, values = {"name" : values["PARAMETERNAME"], 
nico@207: 00650                                                                  "type" : values["DATATYPE"], 
nico@207: 00651                                                                  "access" : ACCESS_TRANSLATE[values["ACCESSTYPE"]], 
nico@207: 00652                                                                  "pdo" : values["PDOMAPPING"] == 1})
nico@207: 00653                     # Second case, entry is an ARRAY
nico@207: 00654                     elif values["OBJECTTYPE"] == 8:
nico@207: 00655                         # Extract maximum subindex number defined
nico@207: 00656                         try:
nico@207: 00657                             max_subindex = values["subindexes"][0]["DEFAULTVALUE"]
nico@207: 00658                         except:
nico@207: 00659                             raise SyntaxError, "Error on entry 0x%4.4X:\nSubindex 0 must be defined for an ARRAY entry"%entry
nico@207: 00660                         # Add mapping for entry
nico@207: 00661                         Node.AddMappingEntry(entry, name = values["PARAMETERNAME"], struct = 3)
nico@207: 00662                         # Add mapping for first subindex
nico@207: 00663                         Node.AddMappingEntry(entry, 0, values = {"name" : "Number of Entries", "type" : 0x05, "access" : "ro", "pdo" : False})
nico@207: 00664                         # Add mapping for other subindexes
nico@207: 00665                         for subindex in xrange(1, int(max_subindex) + 1):
nico@207: 00666                             # if subindex is defined
nico@207: 00667                             if subindex in values["subindexes"]:
nico@207: 00668                                 Node.AddMappingEntry(entry, subindex, values = {"name" : values["subindexes"][subindex]["PARAMETERNAME"], 
nico@207: 00669                                                                                 "type" : values["subindexes"][subindex]["DATATYPE"], 
nico@207: 00670                                                                                 "access" : ACCESS_TRANSLATE[values["subindexes"][subindex]["ACCESSTYPE"]], 
nico@207: 00671                                                                                 "pdo" : values["subindexes"][subindex]["PDOMAPPING"] == 1})
nico@207: 00672                             # if not, we add a mapping for compatibility 
nico@207: 00673                             else:
nico@207: 00674                                 Node.AddMappingEntry(entry, subindex, values = {"name" : "Compatibility Entry", "type" : 0x05, "access" : "rw", "pdo" : False})
nico@207: 00675                     # Third case, entry is an RECORD
nico@207: 00676                     elif values["OBJECTTYPE"] == 9:
nico@207: 00677                         # Verify that the first subindex is defined
nico@207: 00678                         if 0 not in values["subindexes"]:
nico@207: 00679                             raise SyntaxError, "Error on entry 0x%4.4X:\nSubindex 0 must be defined for a RECORD entry"%entry
nico@207: 00680                         # Add mapping for entry
nico@207: 00681                         Node.AddMappingEntry(entry, name = values["PARAMETERNAME"], struct = 7)
nico@207: 00682                         # Add mapping for first subindex
nico@207: 00683                         Node.AddMappingEntry(entry, 0, values = {"name" : "Number of Entries", "type" : 0x05, "access" : "ro", "pdo" : False})
nico@207: 00684                         # Verify that second subindex is defined
nico@207: 00685                         if 1 in values:
nico@207: 00686                             Node.AddMappingEntry(entry, 1, values = {"name" : values["PARAMETERNAME"] + " %d[(sub)]", 
nico@207: 00687                                                                      "type" : values["subindexes"][1]["DATATYPE"], 
nico@207: 00688                                                                      "access" : ACCESS_TRANSLATE[values["subindexes"][1]["ACCESSTYPE"]], 
nico@207: 00689                                                                      "pdo" : values["subindexes"][1]["PDOMAPPING"] == 1})
nico@207: 00690                         else:
nico@207: 00691                             raise SyntaxError, "Error on entry 0x%4.4X:\nA RECORD entry must have at least 2 subindexes"%entry
nico@207: 00692                 
nico@207: 00693                 # Define entry for the new node
nico@207: 00694                 
nico@207: 00695                 # First case, entry is a VAR
nico@207: 00696                 if values["OBJECTTYPE"] == 7:
nico@207: 00697                     # Take default value if it is defined
nico@207: 00698                     if "DEFAULTVALUE" in values:
nico@207: 00699                         value = values["DEFAULTVALUE"]
nico@207: 00700                     # Find default value for value type of the entry
nico@207: 00701                     else:
nico@207: 00702                         value = GetDefaultValue(entry)
nico@207: 00703                     Node.AddEntry(entry, 0, value)
nico@207: 00704                 # Second case, entry is an ARRAY or a RECORD
nico@207: 00705                 elif values["OBJECTTYPE"] in (8, 9):
nico@207: 00706                     # Verify that "Subnumber" attribute is defined and has a valid value
nico@207: 00707                     if "SUBNUMBER" in values and values["SUBNUMBER"] > 0:
nico@207: 00708                         # Extract maximum subindex number defined
nico@207: 00709                         try:
nico@207: 00710                             max_subindex = values["subindexes"][0]["DEFAULTVALUE"]
nico@207: 00711                         except:
nico@207: 00712                             raise SyntaxError, "Error on entry 0x%4.4X:\nSubindex 0 must be defined for an ARRAY or a RECORD entry"%entry
nico@207: 00713                         # Define value for all subindexes except the first 
nico@207: 00714                         for subindex in xrange(1, int(max_subindex) + 1):
nico@207: 00715                             # Take default value if it is defined and entry is defined
nico@207: 00716                             if subindex in values["subindexes"] and "DEFAULTVALUE" in values["subindexes"][subindex]:
nico@207: 00717                                 value = values["subindexes"][subindex]["DEFAULTVALUE"]
nico@207: 00718                             # Find default value for value type of the subindex
nico@207: 00719                             else:
nico@207: 00720                                 value = GetDefaultValue(entry, subindex)
nico@207: 00721                             Node.AddEntry(entry, subindex, value)
nico@207: 00722                     else:
nico@207: 00723                         raise SyntaxError, "Array or Record entry 0x%4.4X must have a \"SubNumber\" attribute"%entry
nico@207: 00724         return Node
nico@207: 00725     except SyntaxError, message:
nico@207: 00726         return "Unable to import EDS file\n%s"%message
nico@207: 00727 
nico@207: 00728 #-------------------------------------------------------------------------------
nico@207: 00729 #                             Main Function
nico@207: 00730 #-------------------------------------------------------------------------------
nico@207: 00731 
nico@207: 00732 if __name__ == '__main__':
nico@207: 00733     print ParseEDSFile("examples/PEAK MicroMod.eds")
nico@207: 00734 
nico@207: 

Generated on Mon Jun 4 16:29:06 2007 for CanFestival by  nico@207: nico@207: doxygen 1.5.1
nico@207: nico@207: