2 # -*- coding: utf-8 -*-
4 #This file is part of CanFestival, a library implementing CanOpen Stack.
6 #Copyright (C): Edouard TISSERANT, Francis DUPIN and Laurent BESSARD
8 #See COPYING file for copyrights details.
10 #This library is free software; you can redistribute it and/or
11 #modify it under the terms of the GNU Lesser General Public
12 #License as published by the Free Software Foundation; either
13 #version 2.1 of the License, or (at your option) any later version.
15 #This library is distributed in the hope that it will be useful,
16 #but WITHOUT ANY WARRANTY; without even the implied warranty of
17 #MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18 #Lesser General Public License for more details.
20 #You should have received a copy of the GNU Lesser General Public
21 #License along with this library; if not, write to the Free Software
22 #Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
26 from node import nosub, var, array, rec, plurivar, pluriarray, plurirec
32 # Regular expression for finding index section names
33 index_model = re.compile('([0-9A-F]{1,4}$)')
34 # Regular expression for finding subindex section names
35 subindex_model = re.compile('([0-9A-F]{1,4})SUB([0-9A-F]{1,2}$)')
36 # Regular expression for finding index section names
37 index_objectlinks_model = re.compile('([0-9A-F]{1,4}OBJECTLINKS$)')
39 # Regular expression for finding NodeXPresent keynames
40 nodepresent_model = re.compile('NODE([0-9]{1,3})PRESENT$')
41 # Regular expression for finding NodeXName keynames
42 nodename_model = re.compile('NODE([0-9]{1,3})NAME$')
43 # Regular expression for finding NodeXDCFName keynames
44 nodedcfname_model = re.compile('NODE([0-9]{1,3})DCFNAME$')
46 # Dictionary for quickly translate boolean into integer value
47 BOOL_TRANSLATE = {True : "1", False : "0"}
49 # Dictionary for quickly translate eds access value into canfestival access value
50 ACCESS_TRANSLATE = {"RO" : "ro", "WO" : "wo", "RW" : "rw", "RWR" : "rw", "RWW" : "rw", "CONST" : "ro"}
52 # Function for verifying data values
53 is_integer = lambda x: type(x) in (IntType, LongType)
54 is_string = lambda x: type(x) in (StringType, UnicodeType)
55 is_boolean = lambda x: x in (0, 1)
57 # Define checking of value for each attribute
58 ENTRY_ATTRIBUTES = {"SUBNUMBER" : is_integer, "PARAMETERNAME" : is_string,
59 "OBJECTTYPE" : lambda x: x in (7, 8, 9), "DATATYPE" : is_integer,
60 "LOWLIMIT" : is_integer, "HIGHLIMIT" : is_integer,
61 "ACCESSTYPE" : lambda x: x.upper() in ACCESS_TRANSLATE.keys(),
62 "DEFAULTVALUE" : lambda x: True, "PDOMAPPING" : is_boolean,
63 "OBJFLAGS" : is_integer, "PARAMETERVALUE" : lambda x: True,}
65 # Define entry parameters by entry ObjectType number
66 ENTRY_TYPES = {7 : {"name" : " VAR",
67 "require" : ["PARAMETERNAME", "DATATYPE", "ACCESSTYPE"],
68 "optional" : ["OBJECTTYPE", "DEFAULTVALUE", "PDOMAPPING", "LOWLIMIT", "HIGHLIMIT", "OBJFLAGS", "PARAMETERVALUE"]},
69 8 : {"name" : "n ARRAY",
70 "require" : ["PARAMETERNAME", "OBJECTTYPE", "SUBNUMBER"],
71 "optional" : ["OBJFLAGS"]},
72 9 : {"name" : " RECORD",
73 "require" : ["PARAMETERNAME", "OBJECTTYPE", "SUBNUMBER"],
74 "optional" : ["OBJFLAGS"]}}
77 # Function that search into Node Mappings the informations about an index or a subindex
78 # and return the default value
79 def GetDefaultValue(Node, index, subIndex = None):
80 infos = Node.GetEntryInfos(index)
81 if infos["struct"] & node.OD_MultipleSubindexes:
82 # First case entry is a record
83 if infos["struct"] & node.OD_IdenticalSubindexes:
84 subentry_infos = Node.GetSubentryInfos(index, 1)
85 # Second case entry is an array
87 subentry_infos = Node.GetSubentryInfos(index, subIndex)
88 # If a default value is defined for this subindex, returns it
89 if "default" in subentry_infos:
90 return subentry_infos["default"]
91 # If not, returns the default value for the subindex type
93 return Node.GetTypeDefaultValue(subentry_infos["type"])
94 # Third case entry is a var
96 subentry_infos = Node.GetSubentryInfos(index, 0)
97 # If a default value is defined for this subindex, returns it
98 if "default" in subentry_infos:
99 return subentry_infos["default"]
100 # If not, returns the default value for the subindex type
102 return Node.GetTypeDefaultValue(subentry_infos["type"])
106 #-------------------------------------------------------------------------------
108 #-------------------------------------------------------------------------------
111 # List of section names that are not index and subindex and that we can meet in
113 SECTION_KEYNAMES = ["FILEINFO", "DEVICEINFO", "DUMMYUSAGE", "COMMENTS",
114 "MANDATORYOBJECTS", "OPTIONALOBJECTS", "MANUFACTUREROBJECTS",
115 "STANDARDDATATYPES", "SUPPORTEDMODULES"]
118 # Function that extract sections from a file and returns a dictionary of the informations
119 def ExtractSections(file):
120 return [(blocktuple[0], # EntryName : Assignements dict
121 blocktuple[-1].splitlines()) # all the lines
122 for blocktuple in [ # Split the eds files into
123 block.split("]", 1) # (EntryName,Assignements) tuple
124 for block in # for each blocks staring with '['
125 ("\n"+file).split("\n[")]
126 if blocktuple[0].isalnum()] # if EntryName exists
129 # Function that parse an CPJ file and returns a dictionary of the informations
130 def ParseCPJFile(filepath):
133 cpj_file = open(filepath,'r').read()
134 sections = ExtractSections(cpj_file)
135 # Parse assignments for each section
136 for section_name, assignments in sections:
138 # Verify that section name is TOPOLOGY
139 if section_name.upper() in "TOPOLOGY":
141 # Reset values for topology
142 topology = {"Name" : "", "Nodes" : {}}
144 for assignment in assignments:
146 if assignment.startswith(";"):
148 # Verify that line is a valid assignment
149 elif assignment.find('=') > 0:
150 # Split assignment into the two values keyname and value
151 keyname, value = assignment.split("=", 1)
153 # keyname must be immediately followed by the "=" sign, so we
154 # verify that there is no whitespace into keyname
155 if keyname.isalnum():
156 # value can be preceded and followed by whitespaces, so we escape them
157 value = value.strip()
159 # First case, value starts with "0x" or "-0x", then it's an hexadecimal value
160 if value.startswith("0x") or value.startswith("-0x"):
162 computed_value = int(value, 16)
164 raise SyntaxError, "\"%s\" is not a valid value for attribute \"%s\" of section \"[%s]\""%(value, keyname, section_name)
165 elif value.isdigit() or value.startswith("-") and value[1:].isdigit():
166 # Second case, value is a number and starts with "0" or "-0", then it's an octal value
167 if value.startswith("0") or value.startswith("-0"):
168 computed_value = int(value, 8)
169 # Third case, value is a number and don't start with "0", then it's a decimal value
171 computed_value = int(value)
172 # In any other case, we keep string value
174 computed_value = value
176 # Search if the section name match any cpj expression
177 nodepresent_result = nodepresent_model.match(keyname.upper())
178 nodename_result = nodename_model.match(keyname.upper())
179 nodedcfname_result = nodedcfname_model.match(keyname.upper())
181 if keyname.upper() == "NETNAME":
182 if not is_string(computed_value):
183 raise SyntaxError, "Invalid value \"%s\" for keyname \"%s\" of section \"[%s]\""%(value, keyname, section_name)
184 topology["Name"] = computed_value
185 elif keyname.upper() == "NODES":
186 if not is_integer(computed_value):
187 raise SyntaxError, "Invalid value \"%s\" for keyname \"%s\" of section \"[%s]\""%(value, keyname, section_name)
188 topology["Number"] = computed_value
189 elif keyname.upper() == "EDSBASENAME":
190 if not is_string(computed_value):
191 raise SyntaxError, "Invalid value \"%s\" for keyname \"%s\" of section \"[%s]\""%(value, keyname, section_name)
192 topology["Path"] = computed_value
193 elif nodepresent_result:
194 if not is_boolean(computed_value):
195 raise SyntaxError, "Invalid value \"%s\" for keyname \"%s\" of section \"[%s]\""%(value, keyname, section_name)
196 nodeid = int(nodepresent_result.groups()[0])
197 if nodeid not in topology["Nodes"].keys():
198 topology["Nodes"][nodeid] = {}
199 topology["Nodes"][nodeid]["Present"] = computed_value
200 elif nodename_result:
201 if not is_string(value):
202 raise SyntaxError, "Invalid value \"%s\" for keyname \"%s\" of section \"[%s]\""%(value, keyname, section_name)
203 nodeid = int(nodename_result.groups()[0])
204 if nodeid not in topology["Nodes"].keys():
205 topology["Nodes"][nodeid] = {}
206 topology["Nodes"][nodeid]["Name"] = computed_value
207 elif nodedcfname_result:
208 if not is_string(computed_value):
209 raise SyntaxError, "Invalid value \"%s\" for keyname \"%s\" of section \"[%s]\""%(value, keyname, section_name)
210 nodeid = int(nodedcfname_result.groups()[0])
211 if nodeid not in topology["Nodes"].keys():
212 topology["Nodes"][nodeid] = {}
213 topology["Nodes"][nodeid]["DCFName"] = computed_value
215 raise SyntaxError, "Keyname \"%s\" not recognised for section \"[%s]\""%(keyname, section_name)
217 # All lines that are not empty and are neither a comment neither not a valid assignment
218 elif assignment.strip() != "":
219 raise SyntaxError, "\"%s\" is not a valid CPJ line"%assignment.strip()
221 if "Number" not in topology.keys():
222 raise SyntaxError, "\"Nodes\" keyname in \"[%s]\" section is missing"%section_name
224 if topology["Number"] != len(topology["Nodes"]):
225 raise SyntaxError, "\"Nodes\" value not corresponding to number of nodes defined"
227 for nodeid, node in topology["Nodes"].items():
228 if "Present" not in node.keys():
229 raise SyntaxError, "\"Node%dPresent\" keyname in \"[%s]\" section is missing"%(nodeid, section_name)
231 networks.append(topology)
233 # In other case, there is a syntax problem into CPJ file
235 raise SyntaxError, "Section \"[%s]\" is unrecognized"%section_name
239 # Function that parse an EDS file and returns a dictionary of the informations
240 def ParseEDSFile(filepath):
243 eds_file = open(filepath,'r').read()
244 sections = ExtractSections(eds_file)
246 # Parse assignments for each section
247 for section_name, assignments in sections:
248 # Reset values of entry
251 # Search if the section name match an index or subindex expression
252 index_result = index_model.match(section_name.upper())
253 subindex_result = subindex_model.match(section_name.upper())
254 index_objectlinks_result = index_objectlinks_model.match(section_name.upper())
256 # Compilation of the EDS information dictionary
259 # First case, section name is in SECTION_KEYNAMES
260 if section_name.upper() in SECTION_KEYNAMES:
261 # Verify that entry is not already defined
262 if section_name.upper() not in eds_dict:
263 eds_dict[section_name.upper()] = values
265 raise SyntaxError, "\"[%s]\" section is defined two times"%section_name
266 # Second case, section name is an index name
268 # Extract index number
269 index = int(index_result.groups()[0], 16)
270 # If index hasn't been referenced before, we add an entry into the dictionary
271 if index not in eds_dict:
272 eds_dict[index] = values
273 eds_dict[index]["subindexes"] = {}
274 elif eds_dict[index].keys() == ["subindexes"]:
275 values["subindexes"] = eds_dict[index]["subindexes"]
276 eds_dict[index] = values
278 raise SyntaxError, "\"[%s]\" section is defined two times"%section_name
280 # Third case, section name is a subindex name
281 elif subindex_result:
282 # Extract index and subindex number
283 index, subindex = [int(value, 16) for value in subindex_result.groups()]
284 # If index hasn't been referenced before, we add an entry into the dictionary
285 # that will be updated later
286 if index not in eds_dict:
287 eds_dict[index] = {"subindexes" : {}}
288 if subindex not in eds_dict[index]["subindexes"]:
289 eds_dict[index]["subindexes"][subindex] = values
291 raise SyntaxError, "\"[%s]\" section is defined two times"%section_name
293 # Third case, section name is a subindex name
294 elif index_objectlinks_result:
296 # In any other case, there is a syntax problem into EDS file
298 raise SyntaxError, "Section \"[%s]\" is unrecognized"%section_name
300 for assignment in assignments:
302 if assignment.startswith(";"):
304 # Verify that line is a valid assignment
305 elif assignment.find('=') > 0:
306 # Split assignment into the two values keyname and value
307 keyname, value = assignment.split("=", 1)
309 # keyname must be immediately followed by the "=" sign, so we
310 # verify that there is no whitespace into keyname
311 if keyname.isalnum():
312 # value can be preceded and followed by whitespaces, so we escape them
313 value = value.strip()
314 # First case, value starts with "$NODEID", then it's a formula
315 if value.startswith("$NODEID"):
317 test = int(value.replace("$NODEID+", ""), 16)
318 computed_value = "\"%s\""%value
320 raise SyntaxError, "\"%s\" is not a valid formula for attribute \"%s\" of section \"[%s]\""%(value, keyname, section_name)
321 # Second case, value starts with "0x", then it's an hexadecimal value
322 elif value.startswith("0x") or value.startswith("-0x"):
324 computed_value = int(value, 16)
326 raise SyntaxError, "\"%s\" is not a valid value for attribute \"%s\" of section \"[%s]\""%(value, keyname, section_name)
327 elif value.isdigit() or value.startswith("-") and value[1:].isdigit():
328 # Third case, value is a number and starts with "0", then it's an octal value
329 if value.startswith("0") or value.startswith("-0"):
330 computed_value = int(value, 8)
331 # Forth case, value is a number and don't start with "0", then it's a decimal value
333 computed_value = int(value)
334 # In any other case, we keep string value
336 computed_value = value
338 # Add value to values dictionary
339 if computed_value != "":
340 # If entry is an index or a subindex
342 # Verify that keyname is a possible attribute
343 if keyname.upper() not in ENTRY_ATTRIBUTES:
344 raise SyntaxError, "Keyname \"%s\" not recognised for section \"[%s]\""%(keyname, section_name)
345 # Verify that value is valid
346 elif not ENTRY_ATTRIBUTES[keyname.upper()](computed_value):
347 raise SyntaxError, "Invalid value \"%s\" for keyname \"%s\" of section \"[%s]\""%(value, keyname, section_name)
349 values[keyname.upper()] = computed_value
351 values[keyname.upper()] = computed_value
352 # All lines that are not empty and are neither a comment neither not a valid assignment
353 elif assignment.strip() != "":
354 raise SyntaxError, "\"%s\" is not a valid EDS line"%assignment.strip()
356 # If entry is an index or a subindex
358 # Verify that entry has an ObjectType
359 if "OBJECTTYPE" in values.keys():
360 # Extract entry ObjectType
361 objecttype = values["OBJECTTYPE"]
363 # Set ObjectType to VAR by default
365 # Extract parameters defined
366 keys = Set(values.keys())
367 keys.discard("subindexes")
368 # Extract possible parameters and parameters required
369 possible = Set(ENTRY_TYPES[objecttype]["require"] + ENTRY_TYPES[objecttype]["optional"])
370 required = Set(ENTRY_TYPES[objecttype]["require"])
371 # Verify that parameters defined contains all the parameters required
372 if not keys.issuperset(required):
373 missing = required.difference(keys)._data.keys()
375 attributes = "Attributes %s are"%", ".join(["\"%s\""%attribute for attribute in missing])
377 attributes = "Attribute \"%s\" is"%missing[0]
378 raise SyntaxError, "Error on section \"[%s]\":\n%s required for a%s entry"%(section_name, attributes, ENTRY_TYPES[objecttype]["name"])
379 # Verify that parameters defined are all in the possible parameters
380 if not keys.issubset(possible):
381 unsupported = keys.difference(possible)._data.keys()
382 if len(unsupported) > 1:
383 attributes = "Attributes %s are"%", ".join(["\"%s\""%attribute for attribute in unsupported])
385 attributes = "Attribute \"%s\" is"%unsupported[0]
386 raise SyntaxError, "Error on section \"[%s]\":\n%s unsupported for a%s entry"%(section_name, attributes, ENTRY_TYPES[objecttype]["name"])
388 if "PARAMETERVALUE" in values:
390 if values["DATATYPE"] in (0x09, 0x0A, 0x0B, 0x0F):
391 values["PARAMETERVALUE"] = str(values["PARAMETERVALUE"])
392 elif values["DATATYPE"] in (0x08, 0x11):
393 values["PARAMETERVALUE"] = float(values["PARAMETERVALUE"])
394 elif values["DATATYPE"] == 0x01:
395 values["PARAMETERVALUE"] = {0 : False, 1 : True}[values["PARAMETERVALUE"]]
397 if type(values["PARAMETERVALUE"]) != IntType and values["PARAMETERVALUE"].find("$NODEID") == -1:
400 raise SyntaxError, "Error on section \"[%s]\":\nParameterValue incompatible with DataType"%section_name
402 if "DEFAULTVALUE" in values:
404 if values["DATATYPE"] in (0x09, 0x0A, 0x0B, 0x0F):
405 values["DEFAULTVALUE"] = str(values["DEFAULTVALUE"])
406 elif values["DATATYPE"] in (0x08, 0x11):
407 values["DEFAULTVALUE"] = float(values["DEFAULTVALUE"])
408 elif values["DATATYPE"] == 0x01:
409 values["DEFAULTVALUE"] = {0 : True, 1 : False}[values["DEFAULTVALUE"]]
411 if type(values["DEFAULTVALUE"]) != IntType and values["DEFAULTVALUE"].find("$NODEID") == -1:
414 raise SyntaxError, "Error on section \"[%s]\":\nDefaultValue incompatible with DataType"%section_name
419 # Function that write an EDS file after generate it's content
420 def WriteFile(filepath, content):
421 # Open file in write mode
422 cfile = open(filepath,"w")
429 # Function that generate the EDS file content for the current node in the manager
430 def GenerateFileContent(Node, filepath):
431 # Dictionary of each index contents
435 current_time = localtime()
436 # Extract node informations
437 nodename = Node.GetNodeName()
438 nodeid = Node.GetNodeID()
439 nodetype = Node.GetNodeType()
440 description = Node.GetNodeDescription()
442 # Retreiving lists of indexes defined
443 entries = Node.GetIndexes()
445 # Generate FileInfo section
446 fileContent = "[FileInfo]\n"
447 fileContent += "FileName=%s\n"%os.path.split(filepath)[-1]
448 fileContent += "FileVersion=1\n"
449 fileContent += "FileRevision=1\n"
450 fileContent += "EDSVersion=4.0\n"
451 fileContent += "Description=%s\n"%description
452 fileContent += "CreationTime=%s"%strftime("%I:%M", current_time)
453 # %p option of strftime seems not working, then generate AM/PM by hands
454 if strftime("%I", current_time) == strftime("%H", current_time):
455 fileContent += "AM\n"
457 fileContent += "PM\n"
458 fileContent += "CreationDate=%s\n"%strftime("%m-%d-%Y", current_time)
459 fileContent += "CreatedBy=CANFestival\n"
460 fileContent += "ModificationTime=%s"%strftime("%I:%M", current_time)
461 # %p option of strftime seems not working, then generate AM/PM by hands
462 if strftime("%I", current_time) == strftime("%H", current_time):
463 fileContent += "AM\n"
465 fileContent += "PM\n"
466 fileContent += "ModificationDate=%s\n"%strftime("%m-%d-%Y", current_time)
467 fileContent += "ModifiedBy=CANFestival\n"
469 # Generate DeviceInfo section
470 fileContent += "\n[DeviceInfo]\n"
471 fileContent += "VendorName=CANFestival\n"
472 # Use information typed by user in Identity entry
473 fileContent += "VendorNumber=0x%8.8X\n"%Node.GetEntry(0x1018, 1)
474 fileContent += "ProductName=%s\n"%nodename
475 fileContent += "ProductNumber=0x%8.8X\n"%Node.GetEntry(0x1018, 2)
476 fileContent += "RevisionNumber=0x%8.8X\n"%Node.GetEntry(0x1018, 3)
477 # CANFestival support all baudrates as soon as driver choosen support them
478 fileContent += "BaudRate_10=1\n"
479 fileContent += "BaudRate_20=1\n"
480 fileContent += "BaudRate_50=1\n"
481 fileContent += "BaudRate_125=1\n"
482 fileContent += "BaudRate_250=1\n"
483 fileContent += "BaudRate_500=1\n"
484 fileContent += "BaudRate_800=1\n"
485 fileContent += "BaudRate_1000=1\n"
486 # Select BootUp type from the informations given by user
487 fileContent += "SimpleBootUpMaster=%s\n"%BOOL_TRANSLATE[nodetype == "master"]
488 fileContent += "SimpleBootUpSlave=%s\n"%BOOL_TRANSLATE[nodetype == "slave"]
489 # CANFestival characteristics
490 fileContent += "Granularity=8\n"
491 fileContent += "DynamicChannelsSupported=0\n"
492 fileContent += "CompactPDO=0\n"
493 fileContent += "GroupMessaging=0\n"
494 # Calculate receive and tranmit PDO numbers with the entry available
495 fileContent += "NrOfRXPDO=%d\n"%len([idx for idx in entries if 0x1400 <= idx <= 0x15FF])
496 fileContent += "NrOfTXPDO=%d\n"%len([idx for idx in entries if 0x1800 <= idx <= 0x19FF])
497 # LSS not supported as soon as DS-302 was not fully implemented
498 fileContent += "LSS_Supported=0\n"
500 # Generate Dummy Usage section
501 fileContent += "\n[DummyUsage]\n"
502 fileContent += "Dummy0001=0\n"
503 fileContent += "Dummy0002=1\n"
504 fileContent += "Dummy0003=1\n"
505 fileContent += "Dummy0004=1\n"
506 fileContent += "Dummy0005=1\n"
507 fileContent += "Dummy0006=1\n"
508 fileContent += "Dummy0007=1\n"
510 # Generate Comments section
511 fileContent += "\n[Comments]\n"
512 fileContent += "Lines=0\n"
514 # List of entry by type (Mandatory, Optional or Manufacturer
519 # Remove all unused PDO
520 for entry in entries[:]:
521 if 0x1600 <= entry < 0x1800 or 0x1A00 <= entry < 0x1C00:
522 subentry_value = Node.GetEntry(entry, 1)
523 if subentry_value is None or subentry_value == 0:
524 entries.remove(entry)
525 entries.remove(entry - 0x200)
527 # For each entry, we generate the entry section or sections if there is subindexes
528 for entry in entries:
529 # Extract infos and values for the entry
530 entry_infos = Node.GetEntryInfos(entry)
531 values = Node.GetEntry(entry, compute = False)
532 # Define section name
533 text = "\n[%X]\n"%entry
534 # If there is only one value, it's a VAR entry
535 if type(values) != ListType:
536 # Extract the informations of the first subindex
537 subentry_infos = Node.GetSubentryInfos(entry, 0)
538 # Generate EDS informations for the entry
539 text += "ParameterName=%s\n"%subentry_infos["name"]
540 text += "ObjectType=0x7\n"
541 text += "DataType=0x%4.4X\n"%subentry_infos["type"]
542 text += "AccessType=%s\n"%subentry_infos["access"]
543 if subentry_infos["type"] == 1:
544 text += "DefaultValue=%s\n"%BOOL_TRANSLATE[values]
546 text += "DefaultValue=%s\n"%values
547 text += "PDOMapping=%s\n"%BOOL_TRANSLATE[subentry_infos["pdo"]]
549 # Generate EDS informations for the entry
550 text += "ParameterName=%s\n"%entry_infos["name"]
551 if entry_infos["struct"] & node.OD_IdenticalSubindexes:
552 text += "ObjectType=0x9\n"
554 text += "ObjectType=0x8\n"
556 # Generate EDS informations for subindexes of the entry in a separate text
558 # Reset number of subindex defined
560 for subentry, value in enumerate(values):
561 # Extract the informations of each subindex
562 subentry_infos = Node.GetSubentryInfos(entry, subentry)
563 # If entry is not for the compatibility, generate informations for subindex
564 if subentry_infos["name"] != "Compatibility Entry":
565 subtext += "\n[%Xsub%X]\n"%(entry, subentry)
566 subtext += "ParameterName=%s\n"%subentry_infos["name"]
567 subtext += "ObjectType=0x7\n"
568 subtext += "DataType=0x%4.4X\n"%subentry_infos["type"]
569 subtext += "AccessType=%s\n"%subentry_infos["access"]
570 if subentry_infos["type"] == 1:
571 subtext += "DefaultValue=%s\n"%BOOL_TRANSLATE[value]
573 subtext += "DefaultValue=%s\n"%value
574 subtext += "PDOMapping=%s\n"%BOOL_TRANSLATE[subentry_infos["pdo"]]
575 # Increment number of subindex defined
577 # Write number of subindex defined for the entry
578 text += "SubNumber=%d\n"%nb_subentry
579 # Write subindex definitions
582 # Then we add the entry in the right list
584 # First case, entry is between 0x2000 and 0x5FFF, then it's a manufacturer entry
585 if 0x2000 <= entry <= 0x5FFF:
586 manufacturers.append(entry)
587 # Second case, entry is required, then it's a mandatory entry
588 elif entry_infos["need"]:
589 mandatories.append(entry)
590 # In any other case, it's an optional entry
592 optionals.append(entry)
593 # Save text of the entry in the dictiionary of contents
594 indexContents[entry] = text
596 # Before generate File Content we sort the entry list
601 # Generate Definition of mandatory objects
602 fileContent += "\n[MandatoryObjects]\n"
603 fileContent += "SupportedObjects=%d\n"%len(mandatories)
604 for idx, entry in enumerate(mandatories):
605 fileContent += "%d=0x%4.4X\n"%(idx + 1, entry)
606 # Write mandatory entries
607 for entry in mandatories:
608 fileContent += indexContents[entry]
610 # Generate Definition of optional objects
611 fileContent += "\n[OptionalObjects]\n"
612 fileContent += "SupportedObjects=%d\n"%len(optionals)
613 for idx, entry in enumerate(optionals):
614 fileContent += "%d=0x%4.4X\n"%(idx + 1, entry)
615 # Write optional entries
616 for entry in optionals:
617 fileContent += indexContents[entry]
619 # Generate Definition of manufacturer objects
620 fileContent += "\n[ManufacturerObjects]\n"
621 fileContent += "SupportedObjects=%d\n"%len(manufacturers)
622 for idx, entry in enumerate(manufacturers):
623 fileContent += "%d=0x%4.4X\n"%(idx + 1, entry)
624 # Write manufacturer entries
625 for entry in manufacturers:
626 fileContent += indexContents[entry]
628 # Return File Content
632 # Function that generates EDS file from current node edited
633 def GenerateEDSFile(filepath, node):
635 # Generate file content
636 content = GenerateFileContent(node, filepath)
638 WriteFile(filepath, content)
640 except ValueError, message:
641 return "Unable to generate EDS file\n%s"%message
643 # Function that generate the CPJ file content for the nodelist
644 def GenerateCPJContent(nodelist):
645 nodes = nodelist.SlaveNodes.keys()
648 fileContent = "[TOPOLOGY]\n"
649 fileContent += "NetName=%s\n"%nodelist.GetNetworkName()
650 fileContent += "Nodes=0x%2.2X\n"%len(nodes)
653 fileContent += "Node%dPresent=0x01\n"%nodeid
654 fileContent += "Node%dName=%s\n"%(nodeid, nodelist.SlaveNodes[nodeid]["Name"])
655 fileContent += "Node%dDCFName=%s\n"%(nodeid, nodelist.SlaveNodes[nodeid]["EDS"])
657 fileContent += "EDSBaseName=eds\n"
660 # Function that generates Node from an EDS file
661 def GenerateNode(filepath, nodeID = 0):
663 Node = node.Node(id = nodeID)
665 # Parse file and extract dictionary of EDS entry
666 eds_dict = ParseEDSFile(filepath)
667 # Extract Profile Number from Device Type entry
668 ProfileNb = eds_dict[0x1000]["DEFAULTVALUE"] & 0x0000ffff
669 # If profile is not DS-301 or DS-302
670 if ProfileNb not in [301, 302]:
671 # Compile Profile name and path to .prf file
672 ProfileName = "DS-%d"%ProfileNb
673 ProfilePath = os.path.join(os.path.split(__file__)[0], "config/%s.prf"%ProfileName)
674 # Verify that profile is available
675 if os.path.isfile(ProfilePath):
678 execfile(ProfilePath)
679 Node.SetProfileName(ProfileName)
680 Node.SetProfile(Mapping)
681 Node.SetSpecificMenu(AddMenuEntries)
684 # Read all entries in the EDS dictionary
685 for entry, values in eds_dict.items():
686 # All sections with a name in keynames are escaped
687 if entry in SECTION_KEYNAMES:
690 # Extract informations for the entry
691 entry_infos = Node.GetEntryInfos(entry)
693 # If no informations are available, then we write them
695 # First case, entry is a VAR
696 if values["OBJECTTYPE"] == 7:
697 # Add mapping for entry
698 Node.AddMappingEntry(entry, name = values["PARAMETERNAME"], struct = 1)
699 # Add mapping for first subindex
700 Node.AddMappingEntry(entry, 0, values = {"name" : values["PARAMETERNAME"],
701 "type" : values["DATATYPE"],
702 "access" : ACCESS_TRANSLATE[values["ACCESSTYPE"].upper()],
703 "pdo" : values.get("PDOMAPPING", 0) == 1})
704 # Second case, entry is an ARRAY or RECORD
705 elif values["OBJECTTYPE"] in [8, 9]:
706 # Extract maximum subindex number defined
708 max_subindex = values["subindexes"][0]["DEFAULTVALUE"]
710 max_subindex = max(*values["subindexes"].keys())
711 #raise SyntaxError, "Error on entry 0x%4.4X:\nSubindex 0 must be defined for an ARRAY or RECORD entry"%entry
712 # Add mapping for entry
713 Node.AddMappingEntry(entry, name = values["PARAMETERNAME"], struct = 3)
714 # Add mapping for first subindex
715 Node.AddMappingEntry(entry, 0, values = {"name" : "Number of Entries", "type" : 0x05, "access" : "ro", "pdo" : False})
716 # Add mapping for other subindexes
717 for subindex in xrange(1, int(max_subindex) + 1):
718 # if subindex is defined
719 if subindex in values["subindexes"]:
720 Node.AddMappingEntry(entry, subindex, values = {"name" : values["subindexes"][subindex]["PARAMETERNAME"],
721 "type" : values["subindexes"][subindex]["DATATYPE"],
722 "access" : ACCESS_TRANSLATE[values["subindexes"][subindex]["ACCESSTYPE"].upper()],
723 "pdo" : values["subindexes"][subindex].get("PDOMAPPING", 0) == 1})
724 # if not, we add a mapping for compatibility
726 Node.AddMappingEntry(entry, subindex, values = {"name" : "Compatibility Entry", "type" : 0x05, "access" : "rw", "pdo" : False})
727 ## # Third case, entry is an RECORD
728 ## elif values["OBJECTTYPE"] == 9:
729 ## # Verify that the first subindex is defined
730 ## if 0 not in values["subindexes"]:
731 ## raise SyntaxError, "Error on entry 0x%4.4X:\nSubindex 0 must be defined for a RECORD entry"%entry
732 ## # Add mapping for entry
733 ## Node.AddMappingEntry(entry, name = values["PARAMETERNAME"], struct = 7)
734 ## # Add mapping for first subindex
735 ## Node.AddMappingEntry(entry, 0, values = {"name" : "Number of Entries", "type" : 0x05, "access" : "ro", "pdo" : False})
736 ## # Verify that second subindex is defined
737 ## if 1 in values["subindexes"]:
738 ## Node.AddMappingEntry(entry, 1, values = {"name" : values["PARAMETERNAME"] + " %d[(sub)]",
739 ## "type" : values["subindexes"][1]["DATATYPE"],
740 ## "access" : ACCESS_TRANSLATE[values["subindexes"][1]["ACCESSTYPE"].upper()],
741 ## "pdo" : values["subindexes"][1].get("PDOMAPPING", 0) == 1,
744 ## raise SyntaxError, "Error on entry 0x%4.4X:\nA RECORD entry must have at least 2 subindexes"%entry
746 # Define entry for the new node
748 # First case, entry is a VAR
749 if values["OBJECTTYPE"] == 7:
750 # Take default value if it is defined
751 if "PARAMETERVALUE" in values:
752 value = values["PARAMETERVALUE"]
753 elif "DEFAULTVALUE" in values:
754 value = values["DEFAULTVALUE"]
755 # Find default value for value type of the entry
757 value = GetDefaultValue(Node, entry)
758 Node.AddEntry(entry, 0, value)
759 # Second case, entry is an ARRAY or a RECORD
760 elif values["OBJECTTYPE"] in (8, 9):
761 # Verify that "Subnumber" attribute is defined and has a valid value
762 if "SUBNUMBER" in values and values["SUBNUMBER"] > 0:
764 # Extract maximum subindex number defined
766 max_subindex = values["subindexes"][0]["DEFAULTVALUE"]
768 max_subindex = values["SUBNUMBER"] - 1
770 #raise SyntaxError, "Error on entry 0x%4.4X:\nSubindex 0 must be defined for an ARRAY or a RECORD entry"%entry
771 Node.AddEntry(entry, value = [])
772 # Define value for all subindexes except the first
773 for subindex in xrange(1, int(max_subindex) + 1):
774 # Take default value if it is defined and entry is defined
775 if subindex in values["subindexes"] and "PARAMETERVALUE" in values["subindexes"][subindex]:
776 value = values["subindexes"][subindex]["PARAMETERVALUE"]
777 elif subindex in values["subindexes"] and "DEFAULTVALUE" in values["subindexes"][subindex]:
778 value = values["subindexes"][subindex]["DEFAULTVALUE"]
779 # Find default value for value type of the subindex
780 elif subindex in values["subindexes"] or not consecutive:
781 value = GetDefaultValue(Node, entry, subindex)
783 raise SyntaxError, "Error on entry 0x%4.4X:\nCan't recompose implemented subindexes in this ARRAY or RECORD entry"%entry
784 Node.AddEntry(entry, subindex, value)
786 raise SyntaxError, "Array or Record entry 0x%4.4X must have a \"SubNumber\" attribute"%entry
788 except SyntaxError, message:
789 return "Unable to import EDS file\n%s"%message
791 #-------------------------------------------------------------------------------
793 #-------------------------------------------------------------------------------
795 if __name__ == '__main__':
796 print ParseEDSFile("examples/PEAK MicroMod.eds")