nico@207: nico@207:
nico@207: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: