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