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