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
30 from sets import Set as set
35 # Regular expression for finding index section names
36 index_model = re.compile('([0-9A-F]{1,4}$)')
37 # Regular expression for finding subindex section names
38 subindex_model = re.compile('([0-9A-F]{1,4})SUB([0-9A-F]{1,2}$)')
39 # Regular expression for finding index section names
40 index_objectlinks_model = re.compile('([0-9A-F]{1,4}OBJECTLINKS$)')
42 # Regular expression for finding NodeXPresent keynames
43 nodepresent_model = re.compile('NODE([0-9]{1,3})PRESENT$')
44 # Regular expression for finding NodeXName keynames
45 nodename_model = re.compile('NODE([0-9]{1,3})NAME$')
46 # Regular expression for finding NodeXDCFName keynames
47 nodedcfname_model = re.compile('NODE([0-9]{1,3})DCFNAME$')
49 # Dictionary for quickly translate boolean into integer value
50 BOOL_TRANSLATE = {True : "1", False : "0"}
52 # Dictionary for quickly translate eds access value into canfestival access value
53 ACCESS_TRANSLATE = {"RO" : "ro", "WO" : "wo", "RW" : "rw", "RWR" : "rw", "RWW" : "rw", "CONST" : "ro"}
55 # Function for verifying data values
56 is_integer = lambda x: type(x) in (IntType, LongType)
57 is_string = lambda x: type(x) in (StringType, UnicodeType)
58 is_boolean = lambda x: x in (0, 1)
60 # Define checking of value for each attribute
61 ENTRY_ATTRIBUTES = {"SUBNUMBER" : is_integer,
62 "PARAMETERNAME" : is_string,
63 "OBJECTTYPE" : lambda x: x in (2, 7, 8, 9),
64 "DATATYPE" : is_integer,
65 "LOWLIMIT" : is_integer,
66 "HIGHLIMIT" : is_integer,
67 "ACCESSTYPE" : lambda x: x.upper() in ACCESS_TRANSLATE.keys(),
68 "DEFAULTVALUE" : lambda x: True,
69 "PDOMAPPING" : is_boolean,
70 "OBJFLAGS" : is_integer,
71 "PARAMETERVALUE" : lambda x: True,
72 "UPLOADFILE" : is_string,
73 "DOWNLOADFILE" : is_string}
75 # Define entry parameters by entry ObjectType number
76 ENTRY_TYPES = {2 : {"name" : " DOMAIN",
77 "require" : ["PARAMETERNAME", "OBJECTTYPE"],
78 "optional" : ["DATATYPE", "ACCESSTYPE", "DEFAULTVALUE", "OBJFLAGS"]},
80 "require" : ["PARAMETERNAME", "DATATYPE", "ACCESSTYPE"],
81 "optional" : ["OBJECTTYPE", "DEFAULTVALUE", "PDOMAPPING", "LOWLIMIT", "HIGHLIMIT", "OBJFLAGS", "PARAMETERVALUE"]},
82 8 : {"name" : "n ARRAY",
83 "require" : ["PARAMETERNAME", "OBJECTTYPE", "SUBNUMBER"],
84 "optional" : ["OBJFLAGS"]},
85 9 : {"name" : " RECORD",
86 "require" : ["PARAMETERNAME", "OBJECTTYPE", "SUBNUMBER"],
87 "optional" : ["OBJFLAGS"]}}
90 # Function that search into Node Mappings the informations about an index or a subindex
91 # and return the default value
92 def GetDefaultValue(Node, index, subIndex = None):
93 infos = Node.GetEntryInfos(index)
94 if infos["struct"] & node.OD_MultipleSubindexes:
95 # First case entry is a record
96 if infos["struct"] & node.OD_IdenticalSubindexes:
97 subentry_infos = Node.GetSubentryInfos(index, 1)
98 # Second case entry is an array
100 subentry_infos = Node.GetSubentryInfos(index, subIndex)
101 # If a default value is defined for this subindex, returns it
102 if "default" in subentry_infos:
103 return subentry_infos["default"]
104 # If not, returns the default value for the subindex type
106 return Node.GetTypeDefaultValue(subentry_infos["type"])
107 # Third case entry is a var
109 subentry_infos = Node.GetSubentryInfos(index, 0)
110 # If a default value is defined for this subindex, returns it
111 if "default" in subentry_infos:
112 return subentry_infos["default"]
113 # If not, returns the default value for the subindex type
115 return Node.GetTypeDefaultValue(subentry_infos["type"])
119 #-------------------------------------------------------------------------------
121 #-------------------------------------------------------------------------------
124 # List of section names that are not index and subindex and that we can meet in
126 SECTION_KEYNAMES = ["FILEINFO", "DEVICEINFO", "DUMMYUSAGE", "COMMENTS",
127 "MANDATORYOBJECTS", "OPTIONALOBJECTS", "MANUFACTUREROBJECTS",
128 "STANDARDDATATYPES", "SUPPORTEDMODULES"]
131 # Function that extract sections from a file and returns a dictionary of the informations
132 def ExtractSections(file):
133 return [(blocktuple[0], # EntryName : Assignements dict
134 blocktuple[-1].splitlines()) # all the lines
135 for blocktuple in [ # Split the eds files into
136 block.split("]", 1) # (EntryName,Assignements) tuple
137 for block in # for each blocks staring with '['
138 ("\n"+file).split("\n[")]
139 if blocktuple[0].isalnum()] # if EntryName exists
142 # Function that parse an CPJ file and returns a dictionary of the informations
143 def ParseCPJFile(filepath):
146 cpj_file = open(filepath,'r').read()
147 sections = ExtractSections(cpj_file)
148 # Parse assignments for each section
149 for section_name, assignments in sections:
151 # Verify that section name is TOPOLOGY
152 if section_name.upper() in "TOPOLOGY":
154 # Reset values for topology
155 topology = {"Name" : "", "Nodes" : {}}
157 for assignment in assignments:
159 if assignment.startswith(";"):
161 # Verify that line is a valid assignment
162 elif assignment.find('=') > 0:
163 # Split assignment into the two values keyname and value
164 keyname, value = assignment.split("=", 1)
166 # keyname must be immediately followed by the "=" sign, so we
167 # verify that there is no whitespace into keyname
168 if keyname.isalnum():
169 # value can be preceded and followed by whitespaces, so we escape them
170 value = value.strip()
172 # First case, value starts with "0x" or "-0x", then it's an hexadecimal value
173 if value.startswith("0x") or value.startswith("-0x"):
175 computed_value = int(value, 16)
177 raise SyntaxError, "\"%s\" is not a valid value for attribute \"%s\" of section \"[%s]\""%(value, keyname, section_name)
178 elif value.isdigit() or value.startswith("-") and value[1:].isdigit():
179 # Second case, value is a number and starts with "0" or "-0", then it's an octal value
180 if value.startswith("0") or value.startswith("-0"):
181 computed_value = int(value, 8)
182 # Third case, value is a number and don't start with "0", then it's a decimal value
184 computed_value = int(value)
185 # In any other case, we keep string value
187 computed_value = value
189 # Search if the section name match any cpj expression
190 nodepresent_result = nodepresent_model.match(keyname.upper())
191 nodename_result = nodename_model.match(keyname.upper())
192 nodedcfname_result = nodedcfname_model.match(keyname.upper())
194 if keyname.upper() == "NETNAME":
195 if not is_string(computed_value):
196 raise SyntaxError, "Invalid value \"%s\" for keyname \"%s\" of section \"[%s]\""%(value, keyname, section_name)
197 topology["Name"] = computed_value
198 elif keyname.upper() == "NODES":
199 if not is_integer(computed_value):
200 raise SyntaxError, "Invalid value \"%s\" for keyname \"%s\" of section \"[%s]\""%(value, keyname, section_name)
201 topology["Number"] = computed_value
202 elif keyname.upper() == "EDSBASENAME":
203 if not is_string(computed_value):
204 raise SyntaxError, "Invalid value \"%s\" for keyname \"%s\" of section \"[%s]\""%(value, keyname, section_name)
205 topology["Path"] = computed_value
206 elif nodepresent_result:
207 if not is_boolean(computed_value):
208 raise SyntaxError, "Invalid value \"%s\" for keyname \"%s\" of section \"[%s]\""%(value, keyname, section_name)
209 nodeid = int(nodepresent_result.groups()[0])
210 if nodeid not in topology["Nodes"].keys():
211 topology["Nodes"][nodeid] = {}
212 topology["Nodes"][nodeid]["Present"] = computed_value
213 elif nodename_result:
214 if not is_string(value):
215 raise SyntaxError, "Invalid value \"%s\" for keyname \"%s\" of section \"[%s]\""%(value, keyname, section_name)
216 nodeid = int(nodename_result.groups()[0])
217 if nodeid not in topology["Nodes"].keys():
218 topology["Nodes"][nodeid] = {}
219 topology["Nodes"][nodeid]["Name"] = computed_value
220 elif nodedcfname_result:
221 if not is_string(computed_value):
222 raise SyntaxError, "Invalid value \"%s\" for keyname \"%s\" of section \"[%s]\""%(value, keyname, section_name)
223 nodeid = int(nodedcfname_result.groups()[0])
224 if nodeid not in topology["Nodes"].keys():
225 topology["Nodes"][nodeid] = {}
226 topology["Nodes"][nodeid]["DCFName"] = computed_value
228 raise SyntaxError, "Keyname \"%s\" not recognised for section \"[%s]\""%(keyname, section_name)
230 # All lines that are not empty and are neither a comment neither not a valid assignment
231 elif assignment.strip() != "":
232 raise SyntaxError, "\"%s\" is not a valid CPJ line"%assignment.strip()
234 if "Number" not in topology.keys():
235 raise SyntaxError, "\"Nodes\" keyname in \"[%s]\" section is missing"%section_name
237 if topology["Number"] != len(topology["Nodes"]):
238 raise SyntaxError, "\"Nodes\" value not corresponding to number of nodes defined"
240 for nodeid, node in topology["Nodes"].items():
241 if "Present" not in node.keys():
242 raise SyntaxError, "\"Node%dPresent\" keyname in \"[%s]\" section is missing"%(nodeid, section_name)
244 networks.append(topology)
246 # In other case, there is a syntax problem into CPJ file
248 raise SyntaxError, "Section \"[%s]\" is unrecognized"%section_name
252 # Function that parse an EDS file and returns a dictionary of the informations
253 def ParseEDSFile(filepath):
256 eds_file = open(filepath,'r').read()
257 sections = ExtractSections(eds_file)
259 # Parse assignments for each section
260 for section_name, assignments in sections:
261 # Reset values of entry
264 # Search if the section name match an index or subindex expression
265 index_result = index_model.match(section_name.upper())
266 subindex_result = subindex_model.match(section_name.upper())
267 index_objectlinks_result = index_objectlinks_model.match(section_name.upper())
269 # Compilation of the EDS information dictionary
272 # First case, section name is in SECTION_KEYNAMES
273 if section_name.upper() in SECTION_KEYNAMES:
274 # Verify that entry is not already defined
275 if section_name.upper() not in eds_dict:
276 eds_dict[section_name.upper()] = values
278 raise SyntaxError, "\"[%s]\" section is defined two times"%section_name
279 # Second case, section name is an index name
281 # Extract index number
282 index = int(index_result.groups()[0], 16)
283 # If index hasn't been referenced before, we add an entry into the dictionary
284 if index not in eds_dict:
285 eds_dict[index] = values
286 eds_dict[index]["subindexes"] = {}
287 elif eds_dict[index].keys() == ["subindexes"]:
288 values["subindexes"] = eds_dict[index]["subindexes"]
289 eds_dict[index] = values
291 raise SyntaxError, "\"[%s]\" section is defined two times"%section_name
293 # Third case, section name is a subindex name
294 elif subindex_result:
295 # Extract index and subindex number
296 index, subindex = [int(value, 16) for value in subindex_result.groups()]
297 # If index hasn't been referenced before, we add an entry into the dictionary
298 # that will be updated later
299 if index not in eds_dict:
300 eds_dict[index] = {"subindexes" : {}}
301 if subindex not in eds_dict[index]["subindexes"]:
302 eds_dict[index]["subindexes"][subindex] = values
304 raise SyntaxError, "\"[%s]\" section is defined two times"%section_name
306 # Third case, section name is a subindex name
307 elif index_objectlinks_result:
309 # In any other case, there is a syntax problem into EDS file
311 raise SyntaxError, "Section \"[%s]\" is unrecognized"%section_name
313 for assignment in assignments:
315 if assignment.startswith(";"):
317 # Verify that line is a valid assignment
318 elif assignment.find('=') > 0:
319 # Split assignment into the two values keyname and value
320 keyname, value = assignment.split("=", 1)
322 # keyname must be immediately followed by the "=" sign, so we
323 # verify that there is no whitespace into keyname
324 if keyname.isalnum():
325 # value can be preceded and followed by whitespaces, so we escape them
326 value = value.strip()
327 # First case, value starts with "$NODEID", then it's a formula
328 if value.upper().startswith("$NODEID"):
330 test = int(value.upper().replace("$NODEID+", ""), 16)
331 computed_value = "\"%s\""%value
333 raise SyntaxError, "\"%s\" is not a valid formula for attribute \"%s\" of section \"[%s]\""%(value, keyname, section_name)
334 # Second case, value starts with "0x", then it's an hexadecimal value
335 elif value.startswith("0x") or value.startswith("-0x"):
337 computed_value = int(value, 16)
339 raise SyntaxError, "\"%s\" is not a valid value for attribute \"%s\" of section \"[%s]\""%(value, keyname, section_name)
340 elif value.isdigit() or value.startswith("-") and value[1:].isdigit():
341 # Third case, value is a number and starts with "0", then it's an octal value
342 if value.startswith("0") or value.startswith("-0"):
343 computed_value = int(value, 8)
344 # Forth case, value is a number and don't start with "0", then it's a decimal value
346 computed_value = int(value)
347 # In any other case, we keep string value
349 computed_value = value
351 # Add value to values dictionary
352 if computed_value != "":
353 # If entry is an index or a subindex
355 # Verify that keyname is a possible attribute
356 if keyname.upper() not in ENTRY_ATTRIBUTES:
357 raise SyntaxError, "Keyname \"%s\" not recognised for section \"[%s]\""%(keyname, section_name)
358 # Verify that value is valid
359 elif not ENTRY_ATTRIBUTES[keyname.upper()](computed_value):
360 raise SyntaxError, "Invalid value \"%s\" for keyname \"%s\" of section \"[%s]\""%(value, keyname, section_name)
362 values[keyname.upper()] = computed_value
364 values[keyname.upper()] = computed_value
365 # All lines that are not empty and are neither a comment neither not a valid assignment
366 elif assignment.strip() != "":
367 raise SyntaxError, "\"%s\" is not a valid EDS line"%assignment.strip()
369 # If entry is an index or a subindex
371 # Verify that entry has an ObjectType
372 values["OBJECTTYPE"] = values.get("OBJECTTYPE", 7)
373 # Extract parameters defined
374 keys = set(values.keys())
375 keys.discard("subindexes")
376 # Extract possible parameters and parameters required
377 possible = set(ENTRY_TYPES[values["OBJECTTYPE"]]["require"] +
378 ENTRY_TYPES[values["OBJECTTYPE"]]["optional"])
379 required = set(ENTRY_TYPES[values["OBJECTTYPE"]]["require"])
380 # Verify that parameters defined contains all the parameters required
381 if not keys.issuperset(required):
382 missing = required.difference(keys)._data.keys()
384 attributes = "Attributes %s are"%", ".join(["\"%s\""%attribute for attribute in missing])
386 attributes = "Attribute \"%s\" is"%missing[0]
387 raise SyntaxError, "Error on section \"[%s]\":\n%s required for a%s entry"%(section_name, attributes, ENTRY_TYPES[values["OBJECTTYPE"]]["name"])
388 # Verify that parameters defined are all in the possible parameters
389 if not keys.issubset(possible):
390 unsupported = keys.difference(possible)._data.keys()
391 if len(unsupported) > 1:
392 attributes = "Attributes %s are"%", ".join(["\"%s\""%attribute for attribute in unsupported])
394 attributes = "Attribute \"%s\" is"%unsupported[0]
395 raise SyntaxError, "Error on section \"[%s]\":\n%s unsupported for a%s entry"%(section_name, attributes, ENTRY_TYPES[values["OBJECTTYPE"]]["name"])
397 VerifyValue(values, section_name, "ParameterValue")
398 VerifyValue(values, section_name, "DefaultValue")
402 def VerifyValue(values, section_name, param):
403 if param.upper() in values:
405 if values["DATATYPE"] in (0x09, 0x0A, 0x0B, 0x0F):
406 values[param.upper()] = str(values[param.upper()])
407 elif values["DATATYPE"] in (0x08, 0x11):
408 values[param.upper()] = float(values[param.upper()])
409 elif values["DATATYPE"] == 0x01:
410 values[param.upper()] = {0 : False, 1 : True}[values[param.upper()]]
412 if not isinstance(values[param.upper()], (IntType, LongType)) and values[param.upper()].upper().find("$NODEID") == -1:
415 raise SyntaxError, "Error on section \"[%s]\":\n%s incompatible with DataType"%(section_name, param)
418 # Function that write an EDS file after generate it's content
419 def WriteFile(filepath, content):
420 # Open file in write mode
421 cfile = open(filepath,"w")
428 # Function that generate the EDS file content for the current node in the manager
429 def GenerateFileContent(Node, filepath):
430 # Dictionary of each index contents
434 current_time = localtime()
435 # Extract node informations
436 nodename = Node.GetNodeName()
437 nodeid = Node.GetNodeID()
438 nodetype = Node.GetNodeType()
439 description = Node.GetNodeDescription()
441 # Retreiving lists of indexes defined
442 entries = Node.GetIndexes()
444 # Generate FileInfo section
445 fileContent = "[FileInfo]\n"
446 fileContent += "FileName=%s\n"%os.path.split(filepath)[-1]
447 fileContent += "FileVersion=1\n"
448 fileContent += "FileRevision=1\n"
449 fileContent += "EDSVersion=4.0\n"
450 fileContent += "Description=%s\n"%description
451 fileContent += "CreationTime=%s"%strftime("%I:%M", current_time)
452 # %p option of strftime seems not working, then generate AM/PM by hands
453 if strftime("%I", current_time) == strftime("%H", current_time):
454 fileContent += "AM\n"
456 fileContent += "PM\n"
457 fileContent += "CreationDate=%s\n"%strftime("%m-%d-%Y", current_time)
458 fileContent += "CreatedBy=CANFestival\n"
459 fileContent += "ModificationTime=%s"%strftime("%I:%M", current_time)
460 # %p option of strftime seems not working, then generate AM/PM by hands
461 if strftime("%I", current_time) == strftime("%H", current_time):
462 fileContent += "AM\n"
464 fileContent += "PM\n"
465 fileContent += "ModificationDate=%s\n"%strftime("%m-%d-%Y", current_time)
466 fileContent += "ModifiedBy=CANFestival\n"
468 # Generate DeviceInfo section
469 fileContent += "\n[DeviceInfo]\n"
470 fileContent += "VendorName=CANFestival\n"
471 # Use information typed by user in Identity entry
472 fileContent += "VendorNumber=0x%8.8X\n"%Node.GetEntry(0x1018, 1)
473 fileContent += "ProductName=%s\n"%nodename
474 fileContent += "ProductNumber=0x%8.8X\n"%Node.GetEntry(0x1018, 2)
475 fileContent += "RevisionNumber=0x%8.8X\n"%Node.GetEntry(0x1018, 3)
476 # CANFestival support all baudrates as soon as driver choosen support them
477 fileContent += "BaudRate_10=1\n"
478 fileContent += "BaudRate_20=1\n"
479 fileContent += "BaudRate_50=1\n"
480 fileContent += "BaudRate_125=1\n"
481 fileContent += "BaudRate_250=1\n"
482 fileContent += "BaudRate_500=1\n"
483 fileContent += "BaudRate_800=1\n"
484 fileContent += "BaudRate_1000=1\n"
485 # Select BootUp type from the informations given by user
486 fileContent += "SimpleBootUpMaster=%s\n"%BOOL_TRANSLATE[nodetype == "master"]
487 fileContent += "SimpleBootUpSlave=%s\n"%BOOL_TRANSLATE[nodetype == "slave"]
488 # CANFestival characteristics
489 fileContent += "Granularity=8\n"
490 fileContent += "DynamicChannelsSupported=0\n"
491 fileContent += "CompactPDO=0\n"
492 fileContent += "GroupMessaging=0\n"
493 # Calculate receive and tranmit PDO numbers with the entry available
494 fileContent += "NrOfRXPDO=%d\n"%len([idx for idx in entries if 0x1400 <= idx <= 0x15FF])
495 fileContent += "NrOfTXPDO=%d\n"%len([idx for idx in entries if 0x1800 <= idx <= 0x19FF])
496 # LSS not supported as soon as DS-302 was not fully implemented
497 fileContent += "LSS_Supported=0\n"
499 # Generate Dummy Usage section
500 fileContent += "\n[DummyUsage]\n"
501 fileContent += "Dummy0001=0\n"
502 fileContent += "Dummy0002=1\n"
503 fileContent += "Dummy0003=1\n"
504 fileContent += "Dummy0004=1\n"
505 fileContent += "Dummy0005=1\n"
506 fileContent += "Dummy0006=1\n"
507 fileContent += "Dummy0007=1\n"
509 # Generate Comments section
510 fileContent += "\n[Comments]\n"
511 fileContent += "Lines=0\n"
513 # List of entry by type (Mandatory, Optional or Manufacturer
518 # Remove all unused PDO
519 ## for entry in entries[:]:
520 ## if 0x1600 <= entry < 0x1800 or 0x1A00 <= entry < 0x1C00:
521 ## subentry_value = Node.GetEntry(entry, 1)
522 ## if subentry_value is None or subentry_value == 0:
523 ## entries.remove(entry)
524 ## entries.remove(entry - 0x200)
526 # For each entry, we generate the entry section or sections if there is subindexes
527 for entry in entries:
528 # Extract infos and values for the entry
529 entry_infos = Node.GetEntryInfos(entry)
530 values = Node.GetEntry(entry, compute = False)
531 # Define section name
532 text = "\n[%X]\n"%entry
533 # If there is only one value, it's a VAR entry
534 if type(values) != ListType:
535 # Extract the informations of the first subindex
536 subentry_infos = Node.GetSubentryInfos(entry, 0)
537 # Generate EDS informations for the entry
538 text += "ParameterName=%s\n"%subentry_infos["name"]
539 text += "ObjectType=0x7\n"
540 text += "DataType=0x%4.4X\n"%subentry_infos["type"]
541 text += "AccessType=%s\n"%subentry_infos["access"]
542 if subentry_infos["type"] == 1:
543 text += "DefaultValue=%s\n"%BOOL_TRANSLATE[values]
545 text += "DefaultValue=%s\n"%values
546 text += "PDOMapping=%s\n"%BOOL_TRANSLATE[subentry_infos["pdo"]]
548 # Generate EDS informations for the entry
549 text += "ParameterName=%s\n"%entry_infos["name"]
550 if entry_infos["struct"] & node.OD_IdenticalSubindexes:
551 text += "ObjectType=0x9\n"
553 text += "ObjectType=0x8\n"
555 # Generate EDS informations for subindexes of the entry in a separate text
557 # Reset number of subindex defined
559 for subentry, value in enumerate(values):
560 # Extract the informations of each subindex
561 subentry_infos = Node.GetSubentryInfos(entry, subentry)
562 # If entry is not for the compatibility, generate informations for subindex
563 if subentry_infos["name"] != "Compatibility Entry":
564 subtext += "\n[%Xsub%X]\n"%(entry, subentry)
565 subtext += "ParameterName=%s\n"%subentry_infos["name"]
566 subtext += "ObjectType=0x7\n"
567 subtext += "DataType=0x%4.4X\n"%subentry_infos["type"]
568 subtext += "AccessType=%s\n"%subentry_infos["access"]
569 if subentry_infos["type"] == 1:
570 subtext += "DefaultValue=%s\n"%BOOL_TRANSLATE[value]
572 subtext += "DefaultValue=%s\n"%value
573 subtext += "PDOMapping=%s\n"%BOOL_TRANSLATE[subentry_infos["pdo"]]
574 # Increment number of subindex defined
576 # Write number of subindex defined for the entry
577 text += "SubNumber=%d\n"%nb_subentry
578 # Write subindex definitions
581 # Then we add the entry in the right list
583 # First case, entry is between 0x2000 and 0x5FFF, then it's a manufacturer entry
584 if 0x2000 <= entry <= 0x5FFF:
585 manufacturers.append(entry)
586 # Second case, entry is required, then it's a mandatory entry
587 elif entry_infos["need"]:
588 mandatories.append(entry)
589 # In any other case, it's an optional entry
591 optionals.append(entry)
592 # Save text of the entry in the dictiionary of contents
593 indexContents[entry] = text
595 # Before generate File Content we sort the entry list
600 # Generate Definition of mandatory objects
601 fileContent += "\n[MandatoryObjects]\n"
602 fileContent += "SupportedObjects=%d\n"%len(mandatories)
603 for idx, entry in enumerate(mandatories):
604 fileContent += "%d=0x%4.4X\n"%(idx + 1, entry)
605 # Write mandatory entries
606 for entry in mandatories:
607 fileContent += indexContents[entry]
609 # Generate Definition of optional objects
610 fileContent += "\n[OptionalObjects]\n"
611 fileContent += "SupportedObjects=%d\n"%len(optionals)
612 for idx, entry in enumerate(optionals):
613 fileContent += "%d=0x%4.4X\n"%(idx + 1, entry)
614 # Write optional entries
615 for entry in optionals:
616 fileContent += indexContents[entry]
618 # Generate Definition of manufacturer objects
619 fileContent += "\n[ManufacturerObjects]\n"
620 fileContent += "SupportedObjects=%d\n"%len(manufacturers)
621 for idx, entry in enumerate(manufacturers):
622 fileContent += "%d=0x%4.4X\n"%(idx + 1, entry)
623 # Write manufacturer entries
624 for entry in manufacturers:
625 fileContent += indexContents[entry]
627 # Return File Content
631 # Function that generates EDS file from current node edited
632 def GenerateEDSFile(filepath, node):
634 # Generate file content
635 content = GenerateFileContent(node, filepath)
637 WriteFile(filepath, content)
639 except ValueError, message:
640 return "Unable to generate EDS file\n%s"%message
642 # Function that generate the CPJ file content for the nodelist
643 def GenerateCPJContent(nodelist):
644 nodes = nodelist.SlaveNodes.keys()
647 fileContent = "[TOPOLOGY]\n"
648 fileContent += "NetName=%s\n"%nodelist.GetNetworkName()
649 fileContent += "Nodes=0x%2.2X\n"%len(nodes)
652 fileContent += "Node%dPresent=0x01\n"%nodeid
653 fileContent += "Node%dName=%s\n"%(nodeid, nodelist.SlaveNodes[nodeid]["Name"])
654 fileContent += "Node%dDCFName=%s\n"%(nodeid, nodelist.SlaveNodes[nodeid]["EDS"])
656 fileContent += "EDSBaseName=eds\n"
659 # Function that generates Node from an EDS file
660 def GenerateNode(filepath, nodeID = 0):
662 Node = node.Node(id = nodeID)
664 # Parse file and extract dictionary of EDS entry
665 eds_dict = ParseEDSFile(filepath)
666 # Extract Profile Number from Device Type entry
667 ProfileNb = eds_dict[0x1000].get("DEFAULTVALUE", 0) & 0x0000ffff
668 # If profile is not DS-301 or DS-302
669 if ProfileNb not in [0, 301, 302]:
670 # Compile Profile name and path to .prf file
671 ProfileName = "DS-%d"%ProfileNb
672 ProfilePath = os.path.join(os.path.split(__file__)[0], "config/%s.prf"%ProfileName)
673 # Verify that profile is available
674 if os.path.isfile(ProfilePath):
677 execfile(ProfilePath)
678 Node.SetProfileName(ProfileName)
679 Node.SetProfile(Mapping)
680 Node.SetSpecificMenu(AddMenuEntries)
683 # Read all entries in the EDS dictionary
684 for entry, values in eds_dict.items():
685 # All sections with a name in keynames are escaped
686 if entry in SECTION_KEYNAMES:
689 # Extract informations for the entry
690 entry_infos = Node.GetEntryInfos(entry)
692 # If no informations are available, then we write them
694 # First case, entry is a DOMAIN or VAR
695 if values["OBJECTTYPE"] in [2, 7]:
696 if values["OBJECTTYPE"] == 2:
697 values["DATATYPE"] = values.get("DATATYPE", 0xF)
698 if values["DATATYPE"] != 0xF:
699 raise SyntaxError, "Domain entry 0x%4.4X DataType must be 0xF(DOMAIN) if defined"%entry
700 # Add mapping for entry
701 Node.AddMappingEntry(entry, name = values["PARAMETERNAME"], struct = 1)
702 # Add mapping for first subindex
703 Node.AddMappingEntry(entry, 0, values = {"name" : values["PARAMETERNAME"],
704 "type" : values["DATATYPE"],
705 "access" : ACCESS_TRANSLATE[values["ACCESSTYPE"].upper()],
706 "pdo" : values.get("PDOMAPPING", 0) == 1})
707 # Second case, entry is an ARRAY or RECORD
708 elif values["OBJECTTYPE"] in [8, 9]:
709 # Extract maximum subindex number defined
710 max_subindex = max(values["subindexes"].keys())
711 # Add mapping for entry
712 Node.AddMappingEntry(entry, name = values["PARAMETERNAME"], struct = 3)
713 # Add mapping for first subindex
714 Node.AddMappingEntry(entry, 0, values = {"name" : "Number of Entries", "type" : 0x05, "access" : "ro", "pdo" : False})
715 # Add mapping for other subindexes
716 for subindex in xrange(1, int(max_subindex) + 1):
717 # if subindex is defined
718 if subindex in values["subindexes"]:
719 Node.AddMappingEntry(entry, subindex, values = {"name" : values["subindexes"][subindex]["PARAMETERNAME"],
720 "type" : values["subindexes"][subindex]["DATATYPE"],
721 "access" : ACCESS_TRANSLATE[values["subindexes"][subindex]["ACCESSTYPE"].upper()],
722 "pdo" : values["subindexes"][subindex].get("PDOMAPPING", 0) == 1})
723 # if not, we add a mapping for compatibility
725 Node.AddMappingEntry(entry, subindex, values = {"name" : "Compatibility Entry", "type" : 0x05, "access" : "rw", "pdo" : False})
726 ## # Third case, entry is an RECORD
727 ## elif values["OBJECTTYPE"] == 9:
728 ## # Verify that the first subindex is defined
729 ## if 0 not in values["subindexes"]:
730 ## raise SyntaxError, "Error on entry 0x%4.4X:\nSubindex 0 must be defined for a RECORD entry"%entry
731 ## # Add mapping for entry
732 ## Node.AddMappingEntry(entry, name = values["PARAMETERNAME"], struct = 7)
733 ## # Add mapping for first subindex
734 ## Node.AddMappingEntry(entry, 0, values = {"name" : "Number of Entries", "type" : 0x05, "access" : "ro", "pdo" : False})
735 ## # Verify that second subindex is defined
736 ## if 1 in values["subindexes"]:
737 ## Node.AddMappingEntry(entry, 1, values = {"name" : values["PARAMETERNAME"] + " %d[(sub)]",
738 ## "type" : values["subindexes"][1]["DATATYPE"],
739 ## "access" : ACCESS_TRANSLATE[values["subindexes"][1]["ACCESSTYPE"].upper()],
740 ## "pdo" : values["subindexes"][1].get("PDOMAPPING", 0) == 1,
743 ## raise SyntaxError, "Error on entry 0x%4.4X:\nA RECORD entry must have at least 2 subindexes"%entry
745 # Define entry for the new node
747 # First case, entry is a DOMAIN or VAR
748 if values["OBJECTTYPE"] in [2, 7]:
749 # Take default value if it is defined
750 if "PARAMETERVALUE" in values:
751 value = values["PARAMETERVALUE"]
752 elif "DEFAULTVALUE" in values:
753 value = values["DEFAULTVALUE"]
754 # Find default value for value type of the entry
756 value = GetDefaultValue(Node, entry)
757 Node.AddEntry(entry, 0, value)
758 # Second case, entry is an ARRAY or a RECORD
759 elif values["OBJECTTYPE"] in [8, 9]:
760 # Verify that "Subnumber" attribute is defined and has a valid value
761 if "SUBNUMBER" in values and values["SUBNUMBER"] > 0:
762 # Extract maximum subindex number defined
763 max_subindex = max(values["subindexes"].keys())
764 Node.AddEntry(entry, value = [])
765 # Define value for all subindexes except the first
766 for subindex in xrange(1, int(max_subindex) + 1):
767 # Take default value if it is defined and entry is defined
768 if subindex in values["subindexes"] and "PARAMETERVALUE" in values["subindexes"][subindex]:
769 value = values["subindexes"][subindex]["PARAMETERVALUE"]
770 elif subindex in values["subindexes"] and "DEFAULTVALUE" in values["subindexes"][subindex]:
771 value = values["subindexes"][subindex]["DEFAULTVALUE"]
772 # Find default value for value type of the subindex
774 value = GetDefaultValue(Node, entry, subindex)
775 Node.AddEntry(entry, subindex, value)
777 raise SyntaxError, "Array or Record entry 0x%4.4X must have a \"SubNumber\" attribute"%entry
779 except SyntaxError, message:
780 return "Unable to import EDS file\n%s"%message
782 #-------------------------------------------------------------------------------
784 #-------------------------------------------------------------------------------
786 if __name__ == '__main__':
787 print ParseEDSFile("examples/PEAK MicroMod.eds")