]> rtime.felk.cvut.cz Git - CanFestival-3.git/blob - objdictgen/eds_utils.py
Bugs corrected on objdictedit and networkdict
[CanFestival-3.git] / objdictgen / eds_utils.py
1 #!/usr/bin/env python
2 # -*- coding: utf-8 -*-
3
4 #This file is part of CanFestival, a library implementing CanOpen Stack. 
5 #
6 #Copyright (C): Edouard TISSERANT, Francis DUPIN and Laurent BESSARD
7 #
8 #See COPYING file for copyrights details.
9 #
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.
14 #
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.
19 #
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
23
24
25 import node
26 from node import nosub, var, array, rec, plurivar, pluriarray, plurirec
27 from sets import *
28 from types import *
29 from time import *
30 import os,re
31
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
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')
43
44 # Dictionary for quickly translate boolean into integer value
45 BOOL_TRANSLATE = {True : "1", False : "0"}
46
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"}
49
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)
54
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}
62
63 # Define entry parameters by entry ObjectType number
64 ENTRY_TYPES = {7 : {"name" : " VAR",
65                     "require" : ["PARAMETERNAME", "OBJECTTYPE", "DATATYPE", "ACCESSTYPE", "PDOMAPPING"],
66                     "optional" : ["LOWLIMIT", "HIGHLIMIT", "DEFAULTVALUE", "OBJFLAGS"]},
67                8 : {"name" : "n ARRAY",
68                     "require" : ["SUBNUMBER", "PARAMETERNAME", "OBJECTTYPE"],
69                     "optional" : ["OBJFLAGS"]},
70                9 : {"name" : " RECORD",
71                     "require" : ["SUBNUMBER", "PARAMETERNAME", "OBJECTTYPE"],
72                     "optional" : ["OBJFLAGS"]}}
73
74
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
84         else:
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
90         else:
91             return Node.GetTypeDefaultValue(subentry_infos["type"])
92     # Third case entry is a var
93     else:
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
99         else:
100             return Node.GetTypeDefaultValue(subentry_infos["type"])
101     return None
102
103
104 #-------------------------------------------------------------------------------
105 #                               Parse file
106 #-------------------------------------------------------------------------------
107
108
109 # List of section names that are not index and subindex and that we can meet in
110 # an EDS file
111 SECTION_KEYNAMES = ["FILEINFO", "DEVICEINFO", "DUMMYUSAGE", "COMMENTS", 
112                     "MANDATORYOBJECTS", "OPTIONALOBJECTS", "MANUFACTUREROBJECTS"]
113
114
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("]")              # (EntryName,Assignements) tuple
121              for block in                  # for each blocks staring with '['
122              file.split("[")]
123              if blocktuple[0].isalnum()]   # if EntryName exists
124     
125
126 # Function that parse an CPJ file and returns a dictionary of the informations
127 def ParseCPJFile(filepath):
128     networks = []
129     # Read file text
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:
134         
135         # Verify that section name is TOPOLOGY 
136         if section_name.upper() in "TOPOLOGY":
137             
138             # Reset values for topology
139             topology = {"Name" : "", "Nodes" : {}}
140             
141             for assignment in assignments:
142                 # Escape any comment
143                 if assignment.startswith(";"):
144                     pass
145                 # Verify that line is a valid assignment
146                 elif assignment.find('=') > 0:
147                     # Split assignment into the two values keyname and value
148                     # Verify that there is only one '=' character in the line
149                     try:
150                         keyname, value = assignment.split("=")
151                     except:
152                         raise SyntaxError, "\"%s\" is not a valid EDS line"%assignment.strip()
153                     
154                     # keyname must be immediately followed by the "=" sign, so we
155                     # verify that there is no whitespace into keyname
156                     if keyname.isalnum():
157                         # value can be preceded and followed by whitespaces, so we escape them
158                         value = value.strip()
159                 
160                         # First case, value starts with "0x", then it's an hexadecimal value
161                         if value.startswith("0x"):
162                             try:
163                                 computed_value = int(value, 16)
164                             except:
165                                 raise SyntaxError, "\"%s\" is not a valid value for attribute \"%s\" of section \"[%s]\""%(value, keyname, section_name)
166                         elif value.isdigit():
167                             # Second case, value is a number and starts with "0", then it's an octal value
168                             if value.startswith("0"):
169                                 computed_value = int(value, 8)
170                             # Third case, value is a number and don't start with "0", then it's a decimal value
171                             else:
172                                 computed_value = int(value)
173                         # In any other case, we keep string value
174                         else:
175                             computed_value = value
176                         
177                         # Search if the section name match any cpj expression
178                         nodepresent_result = nodepresent_model.match(keyname.upper())
179                         nodename_result = nodename_model.match(keyname.upper())
180                         nodedcfname_result = nodedcfname_model.match(keyname.upper())
181                         
182                         if keyname.upper() == "NETNAME":
183                             if not is_string(computed_value):
184                                 raise SyntaxError, "Invalid value \"%s\" for keyname \"%s\" of section \"[%s]\""%(value, keyname, section_name)
185                             topology["Name"] = computed_value
186                         elif keyname.upper() == "NODES":
187                             if not is_integer(computed_value):
188                                 raise SyntaxError, "Invalid value \"%s\" for keyname \"%s\" of section \"[%s]\""%(value, keyname, section_name)
189                             topology["Number"] = computed_value
190                         elif keyname.upper() == "EDSBASENAME":
191                             if not is_string(computed_value):
192                                 raise SyntaxError, "Invalid value \"%s\" for keyname \"%s\" of section \"[%s]\""%(value, keyname, section_name)
193                             topology["Path"] = computed_value
194                         elif nodepresent_result:
195                             if not is_boolean(computed_value):
196                                 raise SyntaxError, "Invalid value \"%s\" for keyname \"%s\" of section \"[%s]\""%(value, keyname, section_name)
197                             nodeid = int(nodepresent_result.groups()[0])
198                             if nodeid not in topology["Nodes"].keys():
199                                 topology["Nodes"][nodeid] = {}
200                             topology["Nodes"][nodeid]["Present"] = computed_value
201                         elif nodename_result:
202                             if not is_string(value):
203                                 raise SyntaxError, "Invalid value \"%s\" for keyname \"%s\" of section \"[%s]\""%(value, keyname, section_name)
204                             nodeid = int(nodename_result.groups()[0])
205                             if nodeid not in topology["Nodes"].keys():
206                                 topology["Nodes"][nodeid] = {}
207                             topology["Nodes"][nodeid]["Name"] = computed_value
208                         elif nodedcfname_result:
209                             if not is_string(computed_value):
210                                 raise SyntaxError, "Invalid value \"%s\" for keyname \"%s\" of section \"[%s]\""%(value, keyname, section_name)
211                             nodeid = int(nodedcfname_result.groups()[0])
212                             if nodeid not in topology["Nodes"].keys():
213                                 topology["Nodes"][nodeid] = {}
214                             topology["Nodes"][nodeid]["DCFName"] = computed_value
215                         else:
216                             raise SyntaxError, "Keyname \"%s\" not recognised for section \"[%s]\""%(keyname, section_name)
217                         
218                 # All lines that are not empty and are neither a comment neither not a valid assignment
219                 elif assignment.strip() != "":
220                     raise SyntaxError, "\"%s\" is not a valid CPJ line"%assignment.strip()
221         
222             if "Number" not in topology.keys():
223                 raise SyntaxError, "\"Nodes\" keyname in \"[%s]\" section is missing"%section_name
224         
225             if topology["Number"] != len(topology["Nodes"]):
226                 raise SyntaxError, "\"Nodes\" value not corresponding to number of nodes defined"
227             
228             for nodeid, node in topology["Nodes"].items():
229                 if "Present" not in node.keys():
230                     raise SyntaxError, "\"Node%dPresent\" keyname in \"[%s]\" section is missing"%(nodeid, section_name)
231             
232             networks.append(topology)
233             
234         # In other case, there is a syntax problem into CPJ file
235         else:
236             raise SyntaxError, "Section \"[%s]\" is unrecognized"%section_name
237     
238     return networks
239
240 # Function that parse an EDS file and returns a dictionary of the informations
241 def ParseEDSFile(filepath):
242     eds_dict = {}
243     # Read file text
244     eds_file = open(filepath,'r').read()
245     sections = ExtractSections(eds_file)
246     
247     # Parse assignments for each section
248     for section_name, assignments in sections:
249         # Reset values of entry
250         values = {}
251         
252         # Search if the section name match an index or subindex expression
253         index_result = index_model.match(section_name.upper())
254         subindex_result = subindex_model.match(section_name.upper())
255         
256         # Compilation of the EDS information dictionary
257         
258         is_entry = False
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
264             else:
265                 raise SyntaxError, "\"[%s]\" section is defined two times"%section_name
266         # Second case, section name is a subindex name 
267         elif subindex_result:
268             # Extract index and subindex number
269             index, subindex = [int(value, 16) for value in subindex_result.groups()]
270             # If index hasn't been referenced before, we add an entry into the dictionary
271             # that will be updated later
272             if index not in eds_dict:
273                 eds_dict[index] = {"subindexes" : {}}
274             if subindex not in eds_dict[index]["subindexes"]:
275                 eds_dict[index]["subindexes"][subindex] = values
276             else:
277                 raise SyntaxError, "\"[%s]\" section is defined two times"%section_name
278             is_entry = True
279         # Third case, section name is an index name 
280         elif index_result:
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
290             else:
291                 raise SyntaxError, "\"[%s]\" section is defined two times"%section_name
292             is_entry = True
293         # In any other case, there is a syntax problem into EDS file
294         else:
295             raise SyntaxError, "Section \"[%s]\" is unrecognized"%section_name
296         
297         for assignment in assignments:
298             # Escape any comment
299             if assignment.startswith(";"):
300                 pass
301             # Verify that line is a valid assignment
302             elif assignment.find('=') > 0:
303                 # Split assignment into the two values keyname and value
304                 # Verify that there is only one '=' character in the line
305                 try:
306                     keyname, value = assignment.split("=")
307                 except:
308                     raise SyntaxError, "\"%s\" is not a valid EDS line"%assignment.strip()
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"):
316                         try:
317                             test = int(value.replace("$NODEID+", ""), 16)
318                             computed_value = value.replace("$NODEID", "self.ID")
319                         except:
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"):
323                         try:
324                             computed_value = int(value, 16)
325                         except:
326                             raise SyntaxError, "\"%s\" is not a valid value for attribute \"%s\" of section \"[%s]\""%(value, keyname, section_name)
327                     elif value.isdigit():
328                         # Third case, value is a number and starts with "0", then it's an octal value
329                         if 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
332                         else:
333                             computed_value = int(value)
334                     # In any other case, we keep string value
335                     else:
336                         computed_value = value
337                     
338                     # Add value to values dictionary
339                     if computed_value != "":
340                         # If entry is an index or a subindex
341                         if is_entry:
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)
348                             else:
349                                 values[keyname.upper()] = computed_value
350                         else:
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()
355         
356         # If entry is an index or a subindex
357         if is_entry:
358             # Verify that entry has an ObjectType
359             if "OBJECTTYPE" in values.keys():
360                 # Extract entry ObjectType
361                 objecttype = values["OBJECTTYPE"]
362                 # Extract parameters defined
363                 keys = Set(values.keys())
364                 keys.discard("subindexes")
365                 # Extract possible parameters and parameters required
366                 possible = Set(ENTRY_TYPES[objecttype]["require"] + ENTRY_TYPES[objecttype]["optional"])
367                 required = Set(ENTRY_TYPES[objecttype]["require"])
368                 # Verify that parameters defined contains all the parameters required
369                 if not keys.issuperset(required):
370                     missing = required.difference(keys)._data.keys()
371                     if len(missing) > 1:
372                         attributes = "Attributes %s are"%", ".join(["\"%s\""%attribute for attribute in missing])
373                     else:
374                         attributes = "Attribute \"%s\" is"%missing[0]
375                     raise SyntaxError, "Error on section \"[%s]\":\n%s required for a%s entry"%(section_name, attributes, ENTRY_TYPES[objecttype]["name"])
376                 # Verify that parameters defined are all in the possible parameters
377                 if not keys.issubset(possible):
378                     unsupported = keys.difference(possible)._data.keys()
379                     if len(unsupported) > 1:
380                         attributes = "Attributes %s are"%", ".join(["\"%s\""%attribute for attribute in unsupported])
381                     else:
382                         attributes = "Attribute \"%s\" is"%unsupported[0]
383                     raise SyntaxError, "Error on section \"[%s]\":\n%s unsupported for a%s entry"%(section_name, attributes, ENTRY_TYPES[objecttype]["name"])
384             else:
385                 raise SyntaxError, "Error on section \"[%s]\":\nAttribute OBJECTTYPE is required"%section_name
386         
387     return eds_dict
388
389
390 # Function that write an EDS file after generate it's content
391 def WriteFile(filepath, content):
392     # Open file in write mode
393     cfile = open(filepath,"w")
394     # Write content
395     cfile.write(content)
396     # Close file
397     cfile.close()
398
399
400 # Function that generate the EDS file content for the current node in the manager
401 def GenerateFileContent(filepath):
402     # Dictionary of each index contents
403     indexContents = {}
404     
405     # Extract local time
406     current_time = localtime()
407     # Extract node informations
408     nodename, nodeid, nodetype, description = Manager.GetCurrentNodeInfos()
409     
410     # Compiling lists of indexes defined
411     entries = [idx for name, idx in Manager.GetCurrentValidIndexes(0, 0xFFFF)]
412     
413     # Generate FileInfo section
414     fileContent = "[FileInfo]\n"
415     fileContent += "CreatedBy=CANFestival\n"
416     fileContent += "Description=%s\n"%description
417     fileContent += "CreationTime=%s"%strftime("%I:%M", current_time)
418     # %p option of strftime seems not working, then generate AM/PM by hands
419     if strftime("%I", current_time) == strftime("%H", current_time):
420         fileContent += "AM\n"
421     else:
422         fileContent += "PM\n"
423     fileContent += "CreationDate=%s\n"%strftime("%m-%d-%Y", current_time)
424     fileContent += "FileName=%s\n"%os.path.split(filepath)[-1]
425     fileContent += "FileVersion=1\n"
426     fileContent += "FileRevision=1\n"
427     fileContent += "EDSVersion=3.0\n"
428     
429     # Generate DeviceInfo section
430     fileContent += "\n[DeviceInfo]\n"
431     fileContent += "VendorName=CANFestival\n"
432     # Use information typed by user in Identity entry
433     fileContent += "VendorNumber=0x%8.8X\n"%Manager.GetCurrentEntry(0x1018, 1)
434     fileContent += "ProductName=%s\n"%nodename
435     fileContent += "ProductNumber=0x%8.8X\n"%Manager.GetCurrentEntry(0x1018, 2)
436     fileContent += "RevisionNumber=0x%8.8X\n"%Manager.GetCurrentEntry(0x1018, 3)
437     # CANFestival support all baudrates as soon as driver choosen support them
438     fileContent += "BaudRate_10=1\n"
439     fileContent += "BaudRate_20=1\n"
440     fileContent += "BaudRate_50=1\n"
441     fileContent += "BaudRate_125=1\n"
442     fileContent += "BaudRate_250=1\n"
443     fileContent += "BaudRate_500=1\n"
444     fileContent += "BaudRate_800=1\n"
445     fileContent += "BaudRate_1000=1\n"
446     # Select BootUp type from the informations given by user
447     fileContent += "SimpleBootUpMaster=%s\n"%BOOL_TRANSLATE[nodetype == "master"]
448     fileContent += "SimpleBootUpSlave=%s\n"%BOOL_TRANSLATE[nodetype == "slave"]
449     # CANFestival characteristics
450     fileContent += "Granularity=8\n"
451     fileContent += "DynamicChannelsSupported=0\n"
452     fileContent += "CompactPDO=0\n"
453     fileContent += "GroupMessaging=0\n"
454     # Calculate receive and tranmit PDO numbers with the entry available
455     fileContent += "NrOfRXPDO=%d\n"%len([idx for idx in entries if 0x1400 <= idx <= 0x15FF])
456     fileContent += "NrOfTXPDO=%d\n"%len([idx for idx in entries if 0x1800 <= idx <= 0x19FF])
457     # LSS not supported as soon as DS-302 was not fully implemented
458     fileContent += "LSS_Supported=0\n"
459     
460     # Generate Dummy Usage section
461     fileContent += "\n[DummyUsage]\n"
462     fileContent += "Dummy0001=0\n"
463     fileContent += "Dummy0002=1\n"
464     fileContent += "Dummy0003=1\n"
465     fileContent += "Dummy0004=1\n"
466     fileContent += "Dummy0005=1\n"
467     fileContent += "Dummy0006=1\n"
468     fileContent += "Dummy0007=1\n"
469
470     # Generate Comments section
471     fileContent += "\n[Comments]\n"
472     fileContent += "Lines=0\n"
473     
474     # List of entry by type (Mandatory, Optional or Manufacturer
475     mandatories = []
476     optionals = []
477     manufacturers = []
478     
479     # For each entry, we generate the entry section or sections if there is subindexes
480     for entry in entries:
481         # Extract infos and values for the entry
482         entry_infos = Manager.GetEntryInfos(entry)
483         values = Manager.GetCurrentEntry(entry)
484         # Define section name
485         text = "\n[%X]\n"%entry
486         # If there is only one value, it's a VAR entry
487         if type(values) != ListType:
488             # Extract the informations of the first subindex
489             subentry_infos = Manager.GetSubentryInfos(entry, 0)
490             # Generate EDS informations for the entry
491             text += "ParameterName=%s\n"%subentry_infos["name"]
492             text += "ObjectType=0x7\n"
493             text += "DataType=0x%4.4X\n"%subentry_infos["type"]
494             text += "AccessType=%s\n"%subentry_infos["access"]
495             text += "DefaultValue=%s\n"%values
496             text += "PDOMapping=%s\n"%BOOL_TRANSLATE[subentry_infos["pdo"]]
497         else:
498             # Generate EDS informations for the entry
499             text += "ParameterName=%s\n"%entry_infos["name"]
500             if entry_infos["struct"] & node.OD_IdenticalSubindexes:
501                 text += "ObjectType=0x9\n"
502             else:
503                 text += "ObjectType=0x8\n"
504             
505             # Generate EDS informations for subindexes of the entry in a separate text
506             subtext = ""
507             # Reset number of subindex defined 
508             nb_subentry = 0
509             for subentry, value in enumerate(values):
510                 # Extract the informations of each subindex
511                 subentry_infos = Manager.GetSubentryInfos(entry, subentry)
512                 # If entry is not for the compatibility, generate informations for subindex
513                 if subentry_infos["name"] != "Compatibility Entry":
514                     subtext += "\n[%Xsub%X]\n"%(entry, subentry)
515                     subtext += "ParameterName=%s\n"%subentry_infos["name"]
516                     subtext += "ObjectType=0x7\n"
517                     subtext += "DataType=0x%4.4X\n"%subentry_infos["type"]
518                     subtext += "AccessType=%s\n"%subentry_infos["access"]
519                     subtext += "DefaultValue=%s\n"%value
520                     subtext += "PDOMapping=%s\n"%BOOL_TRANSLATE[subentry_infos["pdo"]]
521                     # Increment number of subindex defined 
522                     nb_subentry += 1
523             # Write number of subindex defined for the entry
524             text += "SubNumber=%d\n"%nb_subentry
525             # Write subindex definitions
526             text += subtext
527         
528         # Then we add the entry in the right list
529         
530         # First case, entry is between 0x2000 and 0x5FFF, then it's a manufacturer entry
531         if 0x2000 <= entry <= 0x5FFF:
532             manufacturers.append(entry)
533         # Second case, entry is required, then it's a mandatory entry
534         elif entry_infos["need"]:
535             mandatories.append(entry)
536         # In any other case, it's an optional entry
537         else:
538             optionals.append(entry)
539         # Save text of the entry in the dictiionary of contents
540         indexContents[entry] = text
541     
542     # Before generate File Content we sort the entry list
543     manufacturers.sort()
544     mandatories.sort()
545     optionals.sort()
546     
547     # Generate Definition of mandatory objects
548     fileContent += "\n[MandatoryObjects]\n"
549     fileContent += "SupportedObjects=%d\n"%len(mandatories)
550     for idx, entry in enumerate(mandatories):
551         fileContent += "%d=0x%4.4X\n"%(idx, entry)
552     # Write mandatory entries
553     for entry in mandatories:
554         fileContent += indexContents[entry]
555     
556     # Generate Definition of optional objects
557     fileContent += "\n[OptionalObjects]\n"
558     fileContent += "SupportedObjects=%d\n"%len(optionals)
559     for idx, entry in enumerate(optionals):
560         fileContent += "%d=0x%4.4X\n"%(idx, entry)
561     # Write optional entries
562     for entry in optionals:
563         fileContent += indexContents[entry]
564
565     # Generate Definition of manufacturer objects
566     fileContent += "\n[ManufacturerObjects]\n"
567     fileContent += "SupportedObjects=%d\n"%len(manufacturers)
568     for idx, entry in enumerate(manufacturers):
569         fileContent += "%d=0x%4.4X\n"%(idx, entry)
570     # Write manufacturer entries
571     for entry in manufacturers:
572         fileContent += indexContents[entry]
573     
574     # Return File Content
575     return fileContent
576
577
578 # Function that generates EDS file from current node edited
579 def GenerateEDSFile(filepath, manager):
580     global Manager
581     Manager = manager
582     try:
583         # Generate file content
584         content = GenerateFileContent(filepath)
585         # Write file
586         WriteFile(filepath, content)
587         return None
588     except ValueError, message:
589         return "Unable to generate EDS file\n%s"%message
590     
591 # Function that generate the CPJ file content for the nodelist
592 def GenerateCPJContent(nodelist):
593     nodes = nodelist.SlaveNodes.keys()
594     nodes.sort()
595     
596     fileContent = "[TOPOLOGY]\n"
597     fileContent += "NetName=%s\n"%nodelist.GetNetworkName()
598     fileContent += "Nodes=0x%2.2X\n"%len(nodes)
599     
600     for nodeid in nodes:
601         fileContent += "Node%dPresent=0x01\n"%nodeid
602         fileContent += "Node%dName=%s\n"%(nodeid, nodelist.SlaveNodes[nodeid]["Name"])
603         fileContent += "Node%dDCFName=%s\n"%(nodeid, nodelist.SlaveNodes[nodeid]["EDS"])
604         
605     fileContent += "EDSBaseName=eds\n"
606     return fileContent
607
608 # Function that generates Node from an EDS file
609 def GenerateNode(filepath, cwd, nodeID = 0):
610     global Node
611     # Create a new node
612     Node = node.Node(id = nodeID)
613     try:
614         # Parse file and extract dictionary of EDS entry
615         eds_dict = ParseEDSFile(filepath)
616         # Extract Profile Number from Device Type entry
617         ProfileNb = eds_dict[0x1000]["DEFAULTVALUE"] & 0x0000ffff
618         # If profile is not DS-301 or DS-302
619         if ProfileNb not in [301, 302]:
620             # Compile Profile name and path to .prf file
621             ProfileName = "DS-%d"%ProfileNb
622             ProfilePath = os.path.join(cwd, "config/%s.prf"%ProfileName)
623             # Verify that profile is available
624             if os.path.isfile(ProfilePath):
625                 try:
626                     # Load Profile
627                     execfile(ProfilePath)
628                     Node.SetProfileName(ProfileName)
629                     Node.SetProfile(Mapping)
630                     Node.SetSpecificMenu(AddMenuEntries)
631                 except:
632                     pass
633         # Read all entries in the EDS dictionary 
634         for entry, values in eds_dict.items():
635             # All sections with a name in keynames are escaped
636             if entry in SECTION_KEYNAMES:
637                 pass
638             else:
639                 # Extract informations for the entry
640                 entry_infos = Node.GetEntryInfos(entry)
641                 
642                 # If no informations are available, then we write them
643                 if not entry_infos:
644                     # First case, entry is a VAR
645                     if values["OBJECTTYPE"] == 7:
646                         # Add mapping for entry
647                         Node.AddMappingEntry(entry, name = values["PARAMETERNAME"], struct = 1)
648                         # Add mapping for first subindex
649                         Node.AddMappingEntry(entry, 0, values = {"name" : values["PARAMETERNAME"], 
650                                                                  "type" : values["DATATYPE"], 
651                                                                  "access" : ACCESS_TRANSLATE[values["ACCESSTYPE"]], 
652                                                                  "pdo" : values["PDOMAPPING"] == 1})
653                     # Second case, entry is an ARRAY
654                     elif values["OBJECTTYPE"] == 8:
655                         # Extract maximum subindex number defined
656                         try:
657                             max_subindex = values["subindexes"][0]["DEFAULTVALUE"]
658                         except:
659                             raise SyntaxError, "Error on entry 0x%4.4X:\nSubindex 0 must be defined for an ARRAY entry"%entry
660                         # Add mapping for entry
661                         Node.AddMappingEntry(entry, name = values["PARAMETERNAME"], struct = 3)
662                         # Add mapping for first subindex
663                         Node.AddMappingEntry(entry, 0, values = {"name" : "Number of Entries", "type" : 0x05, "access" : "ro", "pdo" : False})
664                         # Add mapping for other subindexes
665                         for subindex in xrange(1, int(max_subindex) + 1):
666                             # if subindex is defined
667                             if subindex in values["subindexes"]:
668                                 Node.AddMappingEntry(entry, subindex, values = {"name" : values["subindexes"][subindex]["PARAMETERNAME"], 
669                                                                                 "type" : values["subindexes"][subindex]["DATATYPE"], 
670                                                                                 "access" : ACCESS_TRANSLATE[values["subindexes"][subindex]["ACCESSTYPE"]], 
671                                                                                 "pdo" : values["subindexes"][subindex]["PDOMAPPING"] == 1})
672                             # if not, we add a mapping for compatibility 
673                             else:
674                                 Node.AddMappingEntry(entry, subindex, values = {"name" : "Compatibility Entry", "type" : 0x05, "access" : "rw", "pdo" : False})
675                     # Third case, entry is an RECORD
676                     elif values["OBJECTTYPE"] == 9:
677                         # Verify that the first subindex is defined
678                         if 0 not in values["subindexes"]:
679                             raise SyntaxError, "Error on entry 0x%4.4X:\nSubindex 0 must be defined for a RECORD entry"%entry
680                         # Add mapping for entry
681                         Node.AddMappingEntry(entry, name = values["PARAMETERNAME"], struct = 7)
682                         # Add mapping for first subindex
683                         Node.AddMappingEntry(entry, 0, values = {"name" : "Number of Entries", "type" : 0x05, "access" : "ro", "pdo" : False})
684                         # Verify that second subindex is defined
685                         if 1 in values:
686                             Node.AddMappingEntry(entry, 1, values = {"name" : values["PARAMETERNAME"] + " %d[(sub)]", 
687                                                                      "type" : values["subindexes"][1]["DATATYPE"], 
688                                                                      "access" : ACCESS_TRANSLATE[values["subindexes"][1]["ACCESSTYPE"]], 
689                                                                      "pdo" : values["subindexes"][1]["PDOMAPPING"] == 1})
690                         else:
691                             raise SyntaxError, "Error on entry 0x%4.4X:\nA RECORD entry must have at least 2 subindexes"%entry
692                 
693                 # Define entry for the new node
694                 
695                 # First case, entry is a VAR
696                 if values["OBJECTTYPE"] == 7:
697                     # Take default value if it is defined
698                     if "DEFAULTVALUE" in values:
699                         value = values["DEFAULTVALUE"]
700                     # Find default value for value type of the entry
701                     else:
702                         value = GetDefaultValue(entry)
703                     Node.AddEntry(entry, 0, value)
704                 # Second case, entry is an ARRAY or a RECORD
705                 elif values["OBJECTTYPE"] in (8, 9):
706                     # Verify that "Subnumber" attribute is defined and has a valid value
707                     if "SUBNUMBER" in values and values["SUBNUMBER"] > 0:
708                         # Extract maximum subindex number defined
709                         try:
710                             max_subindex = values["subindexes"][0]["DEFAULTVALUE"]
711                         except:
712                             raise SyntaxError, "Error on entry 0x%4.4X:\nSubindex 0 must be defined for an ARRAY or a RECORD entry"%entry
713                         # Define value for all subindexes except the first 
714                         for subindex in xrange(1, int(max_subindex) + 1):
715                             # Take default value if it is defined and entry is defined
716                             if subindex in values["subindexes"] and "DEFAULTVALUE" in values["subindexes"][subindex]:
717                                 value = values["subindexes"][subindex]["DEFAULTVALUE"]
718                             # Find default value for value type of the subindex
719                             else:
720                                 value = GetDefaultValue(entry, subindex)
721                             Node.AddEntry(entry, subindex, value)
722                     else:
723                         raise SyntaxError, "Array or Record entry 0x%4.4X must have a \"SubNumber\" attribute"%entry
724         return Node
725     except SyntaxError, message:
726         return "Unable to import EDS file\n%s"%message
727
728 #-------------------------------------------------------------------------------
729 #                             Main Function
730 #-------------------------------------------------------------------------------
731
732 if __name__ == '__main__':
733     print ParseEDSFile("examples/PEAK MicroMod.eds")
734