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})')
37 # Regular expression for finding NodeXPresent keynames
38 nodepresent_model = re.compile('NODE([0-9]{1,3})PRESENT')
39 # Regular expression for finding NodeXName keynames
40 nodename_model = re.compile('NODE([0-9]{1,3})NAME')
41 # Regular expression for finding NodeXDCFName keynames
42 nodedcfname_model = re.compile('NODE([0-9]{1,3})DCFNAME')
44 # Dictionary for quickly translate boolean into integer value
45 BOOL_TRANSLATE = {True : "1", False : "0"}
47 # Dictionary for quickly translate eds access value into canfestival access value
48 ACCESS_TRANSLATE = {"ro" : "ro", "wo" : "wo", "rw" : "rw", "rwr" : "rw", "rww" : "rw", "const" : "ro"}
50 # Function for verifying data values
51 is_integer = lambda x: type(x) in (IntType, LongType)
52 is_string = lambda x: type(x) in (StringType, UnicodeType)
53 is_boolean = lambda x: x in (0, 1)
55 # Define checking of value for each attribute
56 ENTRY_ATTRIBUTES = {"SUBNUMBER" : is_integer, "PARAMETERNAME" : is_string,
57 "OBJECTTYPE" : lambda x: x in (7, 8, 9), "DATATYPE" : is_integer,
58 "LOWLIMIT" : is_integer, "HIGHLIMIT" : is_integer,
59 "ACCESSTYPE" : lambda x: x in ["ro","wo", "rw", "rwr", "rww", "const"],
60 "DEFAULTVALUE" : lambda x: True, "PDOMAPPING" : is_boolean,
61 "OBJFLAGS" : is_integer}
63 # Define entry parameters by entry ObjectType number
64 ENTRY_TYPES = {7 : {"name" : " VAR",
65 "require" : ["PARAMETERNAME", "DATATYPE", "ACCESSTYPE"],
66 "optional" : ["OBJECTTYPE", "DEFAULTVALUE", "PDOMAPPING", "LOWLIMIT", "HIGHLIMIT", "OBJFLAGS"]},
67 8 : {"name" : "n ARRAY",
68 "require" : ["PARAMETERNAME", "OBJECTTYPE", "SUBNUMBER"],
69 "optional" : ["OBJFLAGS"]},
70 9 : {"name" : " RECORD",
71 "require" : ["PARAMETERNAME", "OBJECTTYPE", "SUBNUMBER"],
72 "optional" : ["OBJFLAGS"]}}
75 # Function that search into Node Mappings the informations about an index or a subindex
76 # and return the default value
77 def GetDefaultValue(index, subIndex = None):
78 infos = Node.GetEntryInfos(index)
79 if infos["struct"] & node.OD_MultipleSubindexes:
80 # First case entry is a record
81 if infos["struct"] & node.OD_IdenticalSubindexes:
82 subentry_infos = Node.GetSubentryInfos(index, 1)
83 # Second case entry is an array
85 subentry_infos = Node.GetSubentryInfos(index, subIndex)
86 # If a default value is defined for this subindex, returns it
87 if "default" in subentry_infos:
88 return subentry_infos["default"]
89 # If not, returns the default value for the subindex type
91 return Node.GetTypeDefaultValue(subentry_infos["type"])
92 # Third case entry is a var
94 subentry_infos = Node.GetSubentryInfos(index, 0)
95 # If a default value is defined for this subindex, returns it
96 if "default" in subentry_infos:
97 return subentry_infos["default"]
98 # If not, returns the default value for the subindex type
100 return Node.GetTypeDefaultValue(subentry_infos["type"])
104 #-------------------------------------------------------------------------------
106 #-------------------------------------------------------------------------------
109 # List of section names that are not index and subindex and that we can meet in
111 SECTION_KEYNAMES = ["FILEINFO", "DEVICEINFO", "DUMMYUSAGE", "COMMENTS",
112 "MANDATORYOBJECTS", "OPTIONALOBJECTS", "MANUFACTUREROBJECTS"]
115 # Function that extract sections from a file and returns a dictionary of the informations
116 def ExtractSections(file):
117 return [(blocktuple[0], # EntryName : Assignements dict
118 blocktuple[-1].splitlines()) # all the lines
119 for blocktuple in [ # Split the eds files into
120 block.split("]", 1) # (EntryName,Assignements) tuple
121 for block in # for each blocks staring with '['
122 ("\n"+file).split("\n[")]
123 if blocktuple[0].isalnum()] # if EntryName exists
126 # Function that parse an CPJ file and returns a dictionary of the informations
127 def ParseCPJFile(filepath):
130 cpj_file = open(filepath,'r').read()
131 sections = ExtractSections(cpj_file)
132 # Parse assignments for each section
133 for section_name, assignments in sections:
135 # Verify that section name is TOPOLOGY
136 if section_name.upper() in "TOPOLOGY":
138 # Reset values for topology
139 topology = {"Name" : "", "Nodes" : {}}
141 for assignment in assignments:
143 if assignment.startswith(";"):
145 # Verify that line is a valid assignment
146 elif assignment.find('=') > 0:
147 # Split assignment into the two values keyname and value
148 keyname, value = assignment.split("=", 1)
150 # keyname must be immediately followed by the "=" sign, so we
151 # verify that there is no whitespace into keyname
152 if keyname.isalnum():
153 # value can be preceded and followed by whitespaces, so we escape them
154 value = value.strip()
156 # First case, value starts with "0x", then it's an hexadecimal value
157 if value.startswith("0x"):
159 computed_value = int(value, 16)
161 raise SyntaxError, "\"%s\" is not a valid value for attribute \"%s\" of section \"[%s]\""%(value, keyname, section_name)
162 elif value.isdigit():
163 # Second case, value is a number and starts with "0", then it's an octal value
164 if value.startswith("0"):
165 computed_value = int(value, 8)
166 # Third case, value is a number and don't start with "0", then it's a decimal value
168 computed_value = int(value)
169 # In any other case, we keep string value
171 computed_value = value
173 # Search if the section name match any cpj expression
174 nodepresent_result = nodepresent_model.match(keyname.upper())
175 nodename_result = nodename_model.match(keyname.upper())
176 nodedcfname_result = nodedcfname_model.match(keyname.upper())
178 if keyname.upper() == "NETNAME":
179 if not is_string(computed_value):
180 raise SyntaxError, "Invalid value \"%s\" for keyname \"%s\" of section \"[%s]\""%(value, keyname, section_name)
181 topology["Name"] = computed_value
182 elif keyname.upper() == "NODES":
183 if not is_integer(computed_value):
184 raise SyntaxError, "Invalid value \"%s\" for keyname \"%s\" of section \"[%s]\""%(value, keyname, section_name)
185 topology["Number"] = computed_value
186 elif keyname.upper() == "EDSBASENAME":
187 if not is_string(computed_value):
188 raise SyntaxError, "Invalid value \"%s\" for keyname \"%s\" of section \"[%s]\""%(value, keyname, section_name)
189 topology["Path"] = computed_value
190 elif nodepresent_result:
191 if not is_boolean(computed_value):
192 raise SyntaxError, "Invalid value \"%s\" for keyname \"%s\" of section \"[%s]\""%(value, keyname, section_name)
193 nodeid = int(nodepresent_result.groups()[0])
194 if nodeid not in topology["Nodes"].keys():
195 topology["Nodes"][nodeid] = {}
196 topology["Nodes"][nodeid]["Present"] = computed_value
197 elif nodename_result:
198 if not is_string(value):
199 raise SyntaxError, "Invalid value \"%s\" for keyname \"%s\" of section \"[%s]\""%(value, keyname, section_name)
200 nodeid = int(nodename_result.groups()[0])
201 if nodeid not in topology["Nodes"].keys():
202 topology["Nodes"][nodeid] = {}
203 topology["Nodes"][nodeid]["Name"] = computed_value
204 elif nodedcfname_result:
205 if not is_string(computed_value):
206 raise SyntaxError, "Invalid value \"%s\" for keyname \"%s\" of section \"[%s]\""%(value, keyname, section_name)
207 nodeid = int(nodedcfname_result.groups()[0])
208 if nodeid not in topology["Nodes"].keys():
209 topology["Nodes"][nodeid] = {}
210 topology["Nodes"][nodeid]["DCFName"] = computed_value
212 raise SyntaxError, "Keyname \"%s\" not recognised for section \"[%s]\""%(keyname, section_name)
214 # All lines that are not empty and are neither a comment neither not a valid assignment
215 elif assignment.strip() != "":
216 raise SyntaxError, "\"%s\" is not a valid CPJ line"%assignment.strip()
218 if "Number" not in topology.keys():
219 raise SyntaxError, "\"Nodes\" keyname in \"[%s]\" section is missing"%section_name
221 if topology["Number"] != len(topology["Nodes"]):
222 raise SyntaxError, "\"Nodes\" value not corresponding to number of nodes defined"
224 for nodeid, node in topology["Nodes"].items():
225 if "Present" not in node.keys():
226 raise SyntaxError, "\"Node%dPresent\" keyname in \"[%s]\" section is missing"%(nodeid, section_name)
228 networks.append(topology)
230 # In other case, there is a syntax problem into CPJ file
232 raise SyntaxError, "Section \"[%s]\" is unrecognized"%section_name
236 # Function that parse an EDS file and returns a dictionary of the informations
237 def ParseEDSFile(filepath):
240 eds_file = open(filepath,'r').read()
241 sections = ExtractSections(eds_file)
243 # Parse assignments for each section
244 for section_name, assignments in sections:
245 # Reset values of entry
248 # Search if the section name match an index or subindex expression
249 index_result = index_model.match(section_name.upper())
250 subindex_result = subindex_model.match(section_name.upper())
252 # Compilation of the EDS information dictionary
255 # First case, section name is in SECTION_KEYNAMES
256 if section_name.upper() in SECTION_KEYNAMES:
257 # Verify that entry is not already defined
258 if section_name.upper() not in eds_dict:
259 eds_dict[section_name.upper()] = values
261 raise SyntaxError, "\"[%s]\" section is defined two times"%section_name
262 # Second case, section name is a subindex name
263 elif subindex_result:
264 # Extract index and subindex number
265 index, subindex = [int(value, 16) for value in subindex_result.groups()]
266 # If index hasn't been referenced before, we add an entry into the dictionary
267 # that will be updated later
268 if index not in eds_dict:
269 eds_dict[index] = {"subindexes" : {}}
270 if subindex not in eds_dict[index]["subindexes"]:
271 eds_dict[index]["subindexes"][subindex] = values
273 raise SyntaxError, "\"[%s]\" section is defined two times"%section_name
275 # Third case, section name is an index name
277 # Extract index number
278 index = int(index_result.groups()[0], 16)
279 # If index hasn't been referenced before, we add an entry into the dictionary
280 if index not in eds_dict:
281 eds_dict[index] = values
282 eds_dict[index]["subindexes"] = {}
283 elif eds_dict[index].keys() == ["subindexes"]:
284 values["subindexes"] = eds_dict[index]["subindexes"]
285 eds_dict[index] = values
287 raise SyntaxError, "\"[%s]\" section is defined two times"%section_name
289 # In any other case, there is a syntax problem into EDS file
291 raise SyntaxError, "Section \"[%s]\" is unrecognized"%section_name
293 for assignment in assignments:
295 if assignment.startswith(";"):
297 # Verify that line is a valid assignment
298 elif assignment.find('=') > 0:
299 # Split assignment into the two values keyname and value
300 keyname, value = assignment.split("=", 1)
302 # keyname must be immediately followed by the "=" sign, so we
303 # verify that there is no whitespace into keyname
304 if keyname.isalnum():
305 # value can be preceded and followed by whitespaces, so we escape them
306 value = value.strip()
307 # First case, value starts with "$NODEID", then it's a formula
308 if value.startswith("$NODEID"):
310 test = int(value.replace("$NODEID+", ""), 16)
311 computed_value = "\"%s\""%value
313 raise SyntaxError, "\"%s\" is not a valid formula for attribute \"%s\" of section \"[%s]\""%(value, keyname, section_name)
314 # Second case, value starts with "0x", then it's an hexadecimal value
315 elif value.startswith("0x"):
317 computed_value = int(value, 16)
319 raise SyntaxError, "\"%s\" is not a valid value for attribute \"%s\" of section \"[%s]\""%(value, keyname, section_name)
320 elif value.isdigit():
321 # Third case, value is a number and starts with "0", then it's an octal value
322 if value.startswith("0"):
323 computed_value = int(value, 8)
324 # Forth case, value is a number and don't start with "0", then it's a decimal value
326 computed_value = int(value)
327 # In any other case, we keep string value
329 computed_value = value
331 # Add value to values dictionary
332 if computed_value != "":
333 # If entry is an index or a subindex
335 # Verify that keyname is a possible attribute
336 if keyname.upper() not in ENTRY_ATTRIBUTES:
337 raise SyntaxError, "Keyname \"%s\" not recognised for section \"[%s]\""%(keyname, section_name)
338 # Verify that value is valid
339 elif not ENTRY_ATTRIBUTES[keyname.upper()](computed_value):
340 raise SyntaxError, "Invalid value \"%s\" for keyname \"%s\" of section \"[%s]\""%(value, keyname, section_name)
342 values[keyname.upper()] = computed_value
344 values[keyname.upper()] = computed_value
345 # All lines that are not empty and are neither a comment neither not a valid assignment
346 elif assignment.strip() != "":
347 raise SyntaxError, "\"%s\" is not a valid EDS line"%assignment.strip()
349 # If entry is an index or a subindex
351 # Verify that entry has an ObjectType
352 if "OBJECTTYPE" in values.keys():
353 # Extract entry ObjectType
354 objecttype = values["OBJECTTYPE"]
356 # Set ObjectType to VAR by default
358 # Extract parameters defined
359 keys = Set(values.keys())
360 keys.discard("subindexes")
361 # Extract possible parameters and parameters required
362 possible = Set(ENTRY_TYPES[objecttype]["require"] + ENTRY_TYPES[objecttype]["optional"])
363 required = Set(ENTRY_TYPES[objecttype]["require"])
364 # Verify that parameters defined contains all the parameters required
365 if not keys.issuperset(required):
366 missing = required.difference(keys)._data.keys()
368 attributes = "Attributes %s are"%", ".join(["\"%s\""%attribute for attribute in missing])
370 attributes = "Attribute \"%s\" is"%missing[0]
371 raise SyntaxError, "Error on section \"[%s]\":\n%s required for a%s entry"%(section_name, attributes, ENTRY_TYPES[objecttype]["name"])
372 # Verify that parameters defined are all in the possible parameters
373 if not keys.issubset(possible):
374 unsupported = keys.difference(possible)._data.keys()
375 if len(unsupported) > 1:
376 attributes = "Attributes %s are"%", ".join(["\"%s\""%attribute for attribute in unsupported])
378 attributes = "Attribute \"%s\" is"%unsupported[0]
379 raise SyntaxError, "Error on section \"[%s]\":\n%s unsupported for a%s entry"%(section_name, attributes, ENTRY_TYPES[objecttype]["name"])
381 if "DEFAULTVALUE" in values:
383 if values["DATATYPE"] in (0x09, 0x0A, 0x0B, 0x0F):
384 values["DEFAULTVALUE"] = str(values["DEFAULTVALUE"])
385 elif values["DATATYPE"] in (0x08, 0x11):
386 values["DEFAULTVALUE"] = float(values["DEFAULTVALUE"])
387 elif values["DATATYPE"] == 0x01:
388 values["DEFAULTVALUE"] = {0 : True, 1 : False}[values["DEFAULTVALUE"]]
390 if type(values["DEFAULTVALUE"]) != IntType and values["DEFAULTVALUE"].find("$NODEID") == -1:
393 raise SyntaxError, "Error on section \"[%s]\":\nDefaultValue incompatible with DataType"%section_name
398 # Function that write an EDS file after generate it's content
399 def WriteFile(filepath, content):
400 # Open file in write mode
401 cfile = open(filepath,"w")
408 # Function that generate the EDS file content for the current node in the manager
409 def GenerateFileContent(filepath):
410 # Dictionary of each index contents
414 current_time = localtime()
415 # Extract node informations
416 nodename, nodeid, nodetype, description = Manager.GetCurrentNodeInfos()
418 # Compiling lists of indexes defined
419 entries = [idx for name, idx in Manager.GetCurrentValidIndexes(0, 0xFFFF)]
421 # Generate FileInfo section
422 fileContent = "[FileInfo]\n"
423 fileContent += "FileName=%s\n"%os.path.split(filepath)[-1]
424 fileContent += "FileVersion=1\n"
425 fileContent += "FileRevision=1\n"
426 fileContent += "EDSVersion=4.0\n"
427 fileContent += "Description=%s\n"%description
428 fileContent += "CreationTime=%s"%strftime("%I:%M", current_time)
429 # %p option of strftime seems not working, then generate AM/PM by hands
430 if strftime("%I", current_time) == strftime("%H", current_time):
431 fileContent += "AM\n"
433 fileContent += "PM\n"
434 fileContent += "CreationDate=%s\n"%strftime("%m-%d-%Y", current_time)
435 fileContent += "CreatedBy=CANFestival\n"
436 fileContent += "ModificationTime=%s"%strftime("%I:%M", current_time)
437 # %p option of strftime seems not working, then generate AM/PM by hands
438 if strftime("%I", current_time) == strftime("%H", current_time):
439 fileContent += "AM\n"
441 fileContent += "PM\n"
442 fileContent += "ModificationDate=%s\n"%strftime("%m-%d-%Y", current_time)
443 fileContent += "ModifiedBy=CANFestival\n"
445 # Generate DeviceInfo section
446 fileContent += "\n[DeviceInfo]\n"
447 fileContent += "VendorName=CANFestival\n"
448 # Use information typed by user in Identity entry
449 fileContent += "VendorNumber=0x%8.8X\n"%Manager.GetCurrentEntry(0x1018, 1)
450 fileContent += "ProductName=%s\n"%nodename
451 fileContent += "ProductNumber=0x%8.8X\n"%Manager.GetCurrentEntry(0x1018, 2)
452 fileContent += "RevisionNumber=0x%8.8X\n"%Manager.GetCurrentEntry(0x1018, 3)
453 # CANFestival support all baudrates as soon as driver choosen support them
454 fileContent += "BaudRate_10=1\n"
455 fileContent += "BaudRate_20=1\n"
456 fileContent += "BaudRate_50=1\n"
457 fileContent += "BaudRate_125=1\n"
458 fileContent += "BaudRate_250=1\n"
459 fileContent += "BaudRate_500=1\n"
460 fileContent += "BaudRate_800=1\n"
461 fileContent += "BaudRate_1000=1\n"
462 # Select BootUp type from the informations given by user
463 fileContent += "SimpleBootUpMaster=%s\n"%BOOL_TRANSLATE[nodetype == "master"]
464 fileContent += "SimpleBootUpSlave=%s\n"%BOOL_TRANSLATE[nodetype == "slave"]
465 # CANFestival characteristics
466 fileContent += "Granularity=8\n"
467 fileContent += "DynamicChannelsSupported=0\n"
468 fileContent += "CompactPDO=0\n"
469 fileContent += "GroupMessaging=0\n"
470 # Calculate receive and tranmit PDO numbers with the entry available
471 fileContent += "NrOfRXPDO=%d\n"%len([idx for idx in entries if 0x1400 <= idx <= 0x15FF])
472 fileContent += "NrOfTXPDO=%d\n"%len([idx for idx in entries if 0x1800 <= idx <= 0x19FF])
473 # LSS not supported as soon as DS-302 was not fully implemented
474 fileContent += "LSS_Supported=0\n"
476 # Generate Dummy Usage section
477 fileContent += "\n[DummyUsage]\n"
478 fileContent += "Dummy0001=0\n"
479 fileContent += "Dummy0002=1\n"
480 fileContent += "Dummy0003=1\n"
481 fileContent += "Dummy0004=1\n"
482 fileContent += "Dummy0005=1\n"
483 fileContent += "Dummy0006=1\n"
484 fileContent += "Dummy0007=1\n"
486 # Generate Comments section
487 fileContent += "\n[Comments]\n"
488 fileContent += "Lines=0\n"
490 # List of entry by type (Mandatory, Optional or Manufacturer
495 # For each entry, we generate the entry section or sections if there is subindexes
496 for entry in entries:
497 # Extract infos and values for the entry
498 entry_infos = Manager.GetEntryInfos(entry)
499 values = Manager.GetCurrentEntry(entry, compute = False)
500 # Define section name
501 text = "\n[%X]\n"%entry
502 # If there is only one value, it's a VAR entry
503 if type(values) != ListType:
504 # Extract the informations of the first subindex
505 subentry_infos = Manager.GetSubentryInfos(entry, 0)
506 # Generate EDS informations for the entry
507 text += "ParameterName=%s\n"%subentry_infos["name"]
508 text += "ObjectType=0x7\n"
509 text += "DataType=0x%4.4X\n"%subentry_infos["type"]
510 text += "AccessType=%s\n"%subentry_infos["access"]
511 text += "DefaultValue=%s\n"%values
512 text += "PDOMapping=%s\n"%BOOL_TRANSLATE[subentry_infos["pdo"]]
514 # Generate EDS informations for the entry
515 text += "ParameterName=%s\n"%entry_infos["name"]
516 if entry_infos["struct"] & node.OD_IdenticalSubindexes:
517 text += "ObjectType=0x9\n"
519 text += "ObjectType=0x8\n"
521 # Generate EDS informations for subindexes of the entry in a separate text
523 # Reset number of subindex defined
525 for subentry, value in enumerate(values):
526 # Extract the informations of each subindex
527 subentry_infos = Manager.GetSubentryInfos(entry, subentry)
528 # If entry is not for the compatibility, generate informations for subindex
529 if subentry_infos["name"] != "Compatibility Entry":
530 subtext += "\n[%Xsub%X]\n"%(entry, subentry)
531 subtext += "ParameterName=%s\n"%subentry_infos["name"]
532 subtext += "ObjectType=0x7\n"
533 subtext += "DataType=0x%4.4X\n"%subentry_infos["type"]
534 subtext += "AccessType=%s\n"%subentry_infos["access"]
535 subtext += "DefaultValue=%s\n"%value
536 subtext += "PDOMapping=%s\n"%BOOL_TRANSLATE[subentry_infos["pdo"]]
537 # Increment number of subindex defined
539 # Write number of subindex defined for the entry
540 text += "SubNumber=%d\n"%nb_subentry
541 # Write subindex definitions
544 # Then we add the entry in the right list
546 # First case, entry is between 0x2000 and 0x5FFF, then it's a manufacturer entry
547 if 0x2000 <= entry <= 0x5FFF:
548 manufacturers.append(entry)
549 # Second case, entry is required, then it's a mandatory entry
550 elif entry_infos["need"]:
551 mandatories.append(entry)
552 # In any other case, it's an optional entry
554 optionals.append(entry)
555 # Save text of the entry in the dictiionary of contents
556 indexContents[entry] = text
558 # Before generate File Content we sort the entry list
563 # Generate Definition of mandatory objects
564 fileContent += "\n[MandatoryObjects]\n"
565 fileContent += "SupportedObjects=%d\n"%len(mandatories)
566 for idx, entry in enumerate(mandatories):
567 fileContent += "%d=0x%4.4X\n"%(idx + 1, entry)
568 # Write mandatory entries
569 for entry in mandatories:
570 fileContent += indexContents[entry]
572 # Generate Definition of optional objects
573 fileContent += "\n[OptionalObjects]\n"
574 fileContent += "SupportedObjects=%d\n"%len(optionals)
575 for idx, entry in enumerate(optionals):
576 fileContent += "%d=0x%4.4X\n"%(idx + 1, entry)
577 # Write optional entries
578 for entry in optionals:
579 fileContent += indexContents[entry]
581 # Generate Definition of manufacturer objects
582 fileContent += "\n[ManufacturerObjects]\n"
583 fileContent += "SupportedObjects=%d\n"%len(manufacturers)
584 for idx, entry in enumerate(manufacturers):
585 fileContent += "%d=0x%4.4X\n"%(idx + 1, entry)
586 # Write manufacturer entries
587 for entry in manufacturers:
588 fileContent += indexContents[entry]
590 # Return File Content
594 # Function that generates EDS file from current node edited
595 def GenerateEDSFile(filepath, manager):
599 # Generate file content
600 content = GenerateFileContent(filepath)
602 WriteFile(filepath, content)
604 except ValueError, message:
605 return "Unable to generate EDS file\n%s"%message
607 # Function that generate the CPJ file content for the nodelist
608 def GenerateCPJContent(nodelist):
609 nodes = nodelist.SlaveNodes.keys()
612 fileContent = "[TOPOLOGY]\n"
613 fileContent += "NetName=%s\n"%nodelist.GetNetworkName()
614 fileContent += "Nodes=0x%2.2X\n"%len(nodes)
617 fileContent += "Node%dPresent=0x01\n"%nodeid
618 fileContent += "Node%dName=%s\n"%(nodeid, nodelist.SlaveNodes[nodeid]["Name"])
619 fileContent += "Node%dDCFName=%s\n"%(nodeid, nodelist.SlaveNodes[nodeid]["EDS"])
621 fileContent += "EDSBaseName=eds\n"
624 # Function that generates Node from an EDS file
625 def GenerateNode(filepath, nodeID = 0):
628 Node = node.Node(id = nodeID)
630 # Parse file and extract dictionary of EDS entry
631 eds_dict = ParseEDSFile(filepath)
632 # Extract Profile Number from Device Type entry
633 ProfileNb = eds_dict[0x1000]["DEFAULTVALUE"] & 0x0000ffff
634 # If profile is not DS-301 or DS-302
635 if ProfileNb not in [301, 302]:
636 # Compile Profile name and path to .prf file
637 ProfileName = "DS-%d"%ProfileNb
638 ProfilePath = os.path.join(os.path.split(__file__)[0], "config/%s.prf"%ProfileName)
639 # Verify that profile is available
640 if os.path.isfile(ProfilePath):
643 execfile(ProfilePath)
644 Node.SetProfileName(ProfileName)
645 Node.SetProfile(Mapping)
646 Node.SetSpecificMenu(AddMenuEntries)
649 # Read all entries in the EDS dictionary
650 for entry, values in eds_dict.items():
651 # All sections with a name in keynames are escaped
652 if entry in SECTION_KEYNAMES:
655 # Extract informations for the entry
656 entry_infos = Node.GetEntryInfos(entry)
658 # If no informations are available, then we write them
660 # First case, entry is a VAR
661 if values["OBJECTTYPE"] == 7:
662 # Add mapping for entry
663 Node.AddMappingEntry(entry, name = values["PARAMETERNAME"], struct = 1)
664 # Add mapping for first subindex
665 Node.AddMappingEntry(entry, 0, values = {"name" : values["PARAMETERNAME"],
666 "type" : values["DATATYPE"],
667 "access" : ACCESS_TRANSLATE[values["ACCESSTYPE"]],
668 "pdo" : values.get("PDOMAPPING", 0) == 1})
669 # Second case, entry is an ARRAY
670 elif values["OBJECTTYPE"] == 8:
671 # Extract maximum subindex number defined
673 max_subindex = values["subindexes"][0]["DEFAULTVALUE"]
675 raise SyntaxError, "Error on entry 0x%4.4X:\nSubindex 0 must be defined for an ARRAY entry"%entry
676 # Add mapping for entry
677 Node.AddMappingEntry(entry, name = values["PARAMETERNAME"], struct = 3)
678 # Add mapping for first subindex
679 Node.AddMappingEntry(entry, 0, values = {"name" : "Number of Entries", "type" : 0x05, "access" : "ro", "pdo" : False})
680 # Add mapping for other subindexes
681 for subindex in xrange(1, int(max_subindex) + 1):
682 # if subindex is defined
683 if subindex in values["subindexes"]:
684 Node.AddMappingEntry(entry, subindex, values = {"name" : values["subindexes"][subindex]["PARAMETERNAME"],
685 "type" : values["subindexes"][subindex]["DATATYPE"],
686 "access" : ACCESS_TRANSLATE[values["subindexes"][subindex]["ACCESSTYPE"]],
687 "pdo" : values["subindexes"][subindex].get("PDOMAPPING", 0) == 1})
688 # if not, we add a mapping for compatibility
690 Node.AddMappingEntry(entry, subindex, values = {"name" : "Compatibility Entry", "type" : 0x05, "access" : "rw", "pdo" : False})
691 # Third case, entry is an RECORD
692 elif values["OBJECTTYPE"] == 9:
693 # Verify that the first subindex is defined
694 if 0 not in values["subindexes"]:
695 raise SyntaxError, "Error on entry 0x%4.4X:\nSubindex 0 must be defined for a RECORD entry"%entry
696 # Add mapping for entry
697 Node.AddMappingEntry(entry, name = values["PARAMETERNAME"], struct = 7)
698 # Add mapping for first subindex
699 Node.AddMappingEntry(entry, 0, values = {"name" : "Number of Entries", "type" : 0x05, "access" : "ro", "pdo" : False})
700 # Verify that second subindex is defined
702 Node.AddMappingEntry(entry, 1, values = {"name" : values["PARAMETERNAME"] + " %d[(sub)]",
703 "type" : values["subindexes"][1]["DATATYPE"],
704 "access" : ACCESS_TRANSLATE[values["subindexes"][1]["ACCESSTYPE"]],
705 "pdo" : values["subindexes"][1].get("PDOMAPPING", 0) == 1})
707 raise SyntaxError, "Error on entry 0x%4.4X:\nA RECORD entry must have at least 2 subindexes"%entry
709 # Define entry for the new node
711 # First case, entry is a VAR
712 if values["OBJECTTYPE"] == 7:
713 # Take default value if it is defined
714 if "DEFAULTVALUE" in values:
715 value = values["DEFAULTVALUE"]
716 # Find default value for value type of the entry
718 value = GetDefaultValue(entry)
719 Node.AddEntry(entry, 0, value)
720 # Second case, entry is an ARRAY or a RECORD
721 elif values["OBJECTTYPE"] in (8, 9):
722 # Verify that "Subnumber" attribute is defined and has a valid value
723 if "SUBNUMBER" in values and values["SUBNUMBER"] > 0:
724 # Extract maximum subindex number defined
726 max_subindex = values["subindexes"][0]["DEFAULTVALUE"]
728 raise SyntaxError, "Error on entry 0x%4.4X:\nSubindex 0 must be defined for an ARRAY or a RECORD entry"%entry
729 Node.AddEntry(entry, value = [])
730 # Define value for all subindexes except the first
731 for subindex in xrange(1, int(max_subindex) + 1):
732 # Take default value if it is defined and entry is defined
733 if subindex in values["subindexes"] and "DEFAULTVALUE" in values["subindexes"][subindex]:
734 value = values["subindexes"][subindex]["DEFAULTVALUE"]
735 # Find default value for value type of the subindex
737 value = GetDefaultValue(entry, subindex)
738 Node.AddEntry(entry, subindex, value)
740 raise SyntaxError, "Array or Record entry 0x%4.4X must have a \"SubNumber\" attribute"%entry
742 except SyntaxError, message:
743 return "Unable to import EDS file\n%s"%message
745 #-------------------------------------------------------------------------------
747 #-------------------------------------------------------------------------------
749 if __name__ == '__main__':
750 print ParseEDSFile("examples/PEAK MicroMod.eds")