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