objdictgen/eds_utils.py
changeset 182 988f2b302aa6
child 205 dac0f9b4e3f8
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/objdictgen/eds_utils.py	Fri May 04 13:57:54 2007 +0200
@@ -0,0 +1,587 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+
+#This file is part of CanFestival, a library implementing CanOpen Stack. 
+#
+#Copyright (C): Edouard TISSERANT, Francis DUPIN and Laurent BESSARD
+#
+#See COPYING file for copyrights details.
+#
+#This library is free software; you can redistribute it and/or
+#modify it under the terms of the GNU Lesser General Public
+#License as published by the Free Software Foundation; either
+#version 2.1 of the License, or (at your option) any later version.
+#
+#This library is distributed in the hope that it will be useful,
+#but WITHOUT ANY WARRANTY; without even the implied warranty of
+#MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+#Lesser General Public License for more details.
+#
+#You should have received a copy of the GNU Lesser General Public
+#License along with this library; if not, write to the Free Software
+#Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+
+import node
+from sets import *
+from types import *
+from time import *
+import os,re
+
+# Regular expression for finding index section names
+index_model = re.compile('([0-9a-fA-F]{1,4})')
+# Regular expression for finding subindex section names
+subindex_model = re.compile('([0-9a-fA-F]{1,4})sub([0-9a-fA-F]{1,2})')
+
+# Dictionary for quickly translate boolean into integer value
+BOOL_TRANSLATE = {True : "1", False : "0"}
+
+# Function for verifying data values
+is_integer = lambda x: type(x) == IntType
+is_string = lambda x: type(x) == StringType
+
+# Define checking of value for each attribute
+ENTRY_ATTRIBUTES = {"SUBNUMBER" : is_integer, "PARAMETERNAME" : is_string, 
+                    "OBJECTTYPE" : lambda x: x in (7, 8, 9), "DATATYPE" : is_integer, 
+                    "LOWLIMIT" : is_integer, "HIGHLIMIT" : is_integer,
+                    "ACCESSTYPE" : lambda x: x in ["ro","wo", "rw", "rwr", "rww", "const"],
+                    "DEFAULTVALUE" : lambda x: True, "PDOMAPPING" : lambda x: x in (0, 1),
+                    "OBJFLAGS" : is_integer}
+
+# Define entry parameters by entry ObjectType number
+ENTRY_TYPES = {7 : {"name" : " VAR",
+                    "require" : ["PARAMETERNAME", "OBJECTTYPE", "DATATYPE", "ACCESSTYPE", "PDOMAPPING"],
+                    "optional" : ["LOWLIMIT", "HIGHLIMIT", "DEFAULTVALUE", "OBJFLAGS"]},
+               8 : {"name" : "n ARRAY",
+                    "require" : ["SUBNUMBER", "PARAMETERNAME", "OBJECTTYPE"],
+                    "optional" : ["OBJFLAGS"]},
+               9 : {"name" : " RECORD",
+                    "require" : ["SUBNUMBER", "PARAMETERNAME", "OBJECTTYPE"],
+                    "optional" : ["OBJFLAGS"]}}
+
+
+# Function that search into Node Mappings the informations about an index or a subindex
+# and return the default value
+def GetDefaultValue(index, subIndex = None):
+    infos = Manager.GetEntryInfos(index, Node)
+    if infos["struct"] & node.OD_MultipleSubindexes:
+        # First case entry is a record
+        if infos["struct"] & node.OD_IdenticalSubindexes:
+            subentry_infos = Manager.GetSubentryInfos(index, 1, Node)
+        # Second case entry is an array
+        else:
+            subentry_infos = Manager.GetSubentryInfos(index, subIndex, Node)
+        # If a default value is defined for this subindex, returns it
+        if "default" in subentry_infos:
+            return subentry_infos["default"]
+        # If not, returns the default value for the subindex type
+        else:
+            return Manager.GetTypeDefaultValue(subentry_infos["type"], Node)
+    # Third case entry is a var
+    else:
+        subentry_infos = Manager.GetSubentryInfos(index, 0, Node)
+        # If a default value is defined for this subindex, returns it
+        if "default" in subentry_infos:
+            return subentry_infos["default"]
+        # If not, returns the default value for the subindex type
+        else:
+            return Manager.GetTypeDefaultValue(subentry_infos["type"], Node)
+    return None
+
+
+#-------------------------------------------------------------------------------
+#                               Parse file
+#-------------------------------------------------------------------------------
+
+
+# List of section names that are not index and subindex and that we can meet in
+# an EDS file
+SECTION_KEYNAMES = ["FILEINFO", "DEVICEINFO", "DUMMYUSAGE", "COMMENTS", 
+                    "MANDATORYOBJECTS", "OPTIONALOBJECTS", "MANUFACTUREROBJECTS"]
+
+
+# Function that parse an EDS file and returns a dictionary of the informations
+def ParseFile(filepath):
+    eds_dict = {}
+    # Read file text
+    eds_file = open(filepath,'r').read()
+    sections = [(blocktuple[0],                # EntryName : Assignements dict
+                 blocktuple[-1].splitlines())  # all the lines
+                 for blocktuple in [           # Split the eds files into
+                 block.split("]")              # (EntryName,Assignements) tuple
+                 for block in                  # for each blocks staring with '['
+                 eds_file.split("[")]
+                 if blocktuple[0].isalnum()]   # if EntryName exists
+    
+    # Parse assignments for each section
+    for section_name, assignments in sections:
+        # Reset values of entry
+        values = {}
+        
+        # Search if the section name match an index or subindex expression
+        index_result = index_model.match(section_name)
+        subindex_result = subindex_model.match(section_name)
+        
+        # Compilation of the EDS information dictionary
+        
+        is_entry = False
+        # First case, section name is in SECTION_KEYNAMES 
+        if section_name.upper() in SECTION_KEYNAMES:
+            # Verify that entry is not already defined
+            if section_name.upper() not in eds_dict:
+                eds_dict[section_name.upper()] = values
+            else:
+                raise SyntaxError, "\"[%s]\" section is defined two times"%section_name
+        # Second case, section name is a subindex name 
+        elif subindex_result:
+            # Extract index and subindex number
+            index, subindex = [int(value, 16) for value in subindex_result.groups()]
+            # If index hasn't been referenced before, we add an entry into the dictionary
+            # that will be updated later
+            if index not in eds_dict:
+                eds_dict[index] = {"subindexes" : {}}
+            if subindex not in eds_dict[index]["subindexes"]:
+                eds_dict[index]["subindexes"][subindex] = values
+            else:
+                raise SyntaxError, "\"[%s]\" section is defined two times"%section_name
+            is_entry = True
+        # Third case, section name is an index name 
+        elif index_result:
+            # Extract index number
+            index = int(index_result.groups()[0], 16)
+            # If index hasn't been referenced before, we add an entry into the dictionary
+            if index not in eds_dict:
+                eds_dict[index] = values
+                eds_dict[index]["subindexes"] = {}
+            elif eds_dict[index].keys() == ["subindexes"]:
+                values["subindexes"] = eds_dict[index]["subindexes"]
+                eds_dict[index] = values
+            else:
+                raise SyntaxError, "\"[%s]\" section is defined two times"%section_name
+            is_entry = True
+        # In any other case, there is a syntax problem into EDS file
+        else:
+            raise SyntaxError, "Section \"[%s]\" is unrecognized"%section_name
+        
+        for assignment in assignments:
+            # Escape any comment
+            if assignment.startswith(";"):
+                pass
+            # Verify that line is a valid assignment
+            elif assignment.find('=') > 0:
+                # Split assignment into the two values keyname and value
+                try:
+                    keyname, value = assignment.split("=")
+                except:
+                    raise SyntaxError, "\"%s\" is not a valid EDS line"%assignment.strip()
+                # keyname must be immediately followed by the "=" sign, so we
+                # verify that there is no whitespace into keyname
+                if keyname.isalnum():
+                    # value can be preceded and followed by whitespaces, so we escape them
+                    value = value.strip()
+                    # First case, value starts with "$NODEID", then it's a formula
+                    if value.startswith("$NODEID"):
+                        try:
+                            computed_value = int(value.replace("$NODEID+", ""), 16)
+                        except:
+                            raise SyntaxError, "\"%s\" is not a valid formula for attribute \"%s\" of section \"[%s]\""%(value, keyname, section_name)
+                    # Second case, value starts with "0x", then it's an hexadecimal value
+                    elif value.startswith("0x"):
+                        try:
+                            computed_value = int(value, 16)
+                        except:
+                            raise SyntaxError, "\"%s\" is not a valid value for attribute \"%s\" of section \"[%s]\""%(value, keyname, section_name)
+                    elif value.isdigit():
+                        # Third case, value is a number and starts with "0", then it's an octal value
+                        if value.startswith("0"):
+                            computed_value = int(value, 8)
+                        # Forth case, value is a number and don't start with "0", then it's a decimal value
+                        else:
+                            computed_value = int(value)
+                    # In any other case, we keep string value
+                    else:
+                        computed_value = value
+                    
+                    # Add value to values dictionary
+                    if computed_value != "":
+                        # If entry is an index or a subindex
+                        if is_entry:
+                            # Verify that keyname is a possible attribute
+                            if keyname.upper() not in ENTRY_ATTRIBUTES:
+                                raise SyntaxError, "Keyname \"%s\" not recognised for section \"[%s]\""%(keyname, section_name)
+                            # Verify that value is valid
+                            elif not ENTRY_ATTRIBUTES[keyname.upper()](computed_value):
+                                raise SyntaxError, "Invalid value \"%s\" for keyname \"%s\" of section \"[%s]\""%(value, keyname, section_name)
+                            else:
+                                values[keyname.upper()] = computed_value
+                        else:
+                            values[keyname.upper()] = computed_value
+            # All lines that are not empty and are neither a comment neither not a valid assignment
+            elif assignment.strip() != "":
+                raise SyntaxError, "\"%s\" is not a valid EDS line"%assignment.strip()
+        
+        # If entry is an index or a subindex
+        if is_entry:
+            # Verify that entry has an ObjectType
+            if "OBJECTTYPE" in values.keys():
+                # Extract entry ObjectType
+                objecttype = values["OBJECTTYPE"]
+                # Extract parameters defined
+                keys = Set(values.keys())
+                keys.discard("subindexes")
+                # Extract possible parameters and parameters required
+                possible = Set(ENTRY_TYPES[objecttype]["require"] + ENTRY_TYPES[objecttype]["optional"])
+                required = Set(ENTRY_TYPES[objecttype]["require"])
+                # Verify that parameters defined contains all the parameters required
+                if not keys.issuperset(required):
+                    missing = required.difference(keys)._data.keys()
+                    if len(missing) > 1:
+                        attributes = "Attributes %s are"%", ".join(["\"%s\""%attribute for attribute in missing])
+                    else:
+                        attributes = "Attribute \"%s\" is"%missing[0]
+                    raise SyntaxError, "Error on section \"[%s]\":\n%s required for a%s entry"%(section_name, attributes, ENTRY_TYPES[objecttype]["name"])
+                # Verify that parameters defined are all in the possible parameters
+                if not keys.issubset(possible):
+                    unsupported = keys.difference(possible)._data.keys()
+                    if len(unsupported) > 1:
+                        attributes = "Attributes %s are"%", ".join(["\"%s\""%attribute for attribute in unsupported])
+                    else:
+                        attributes = "Attribute \"%s\" is"%unsupported[0]
+                    raise SyntaxError, "Error on section \"[%s]\":\n%s unsupported for a%s entry"%(section_name, attributes, ENTRY_TYPES[objecttype]["name"])
+            else:
+                raise SyntaxError, "Error on section \"[%s]\":\nAttribute OBJECTTYPE is required"%section_name
+        
+    return eds_dict
+
+
+# Function that write an EDS file after generate it's content
+def WriteFile(filepath, content):
+    # Open file in write mode
+    cfile = open(filepath,"w")
+    # Write content
+    cfile.write(content)
+    # Close file
+    cfile.close()
+
+
+# Function that generate the EDS file content for the current node in the manager
+def GenerateFileContent(filepath):
+    # Dictionary of each index contents
+    indexContents = {}
+    
+    # Extract local time
+    current_time = localtime()
+    # Extract node informations
+    nodename, nodeid, nodetype, description = Manager.GetCurrentNodeInfos()
+    
+    # Compiling lists of indexes defined
+    entries = [idx for name, idx in Manager.GetCurrentValidIndexes(0, 0xFFFF)]
+    
+    # Generate FileInfo section
+    fileContent = "[FileInfo]\n"
+    fileContent += "CreatedBy=CANFestival\n"
+    fileContent += "Description=%s\n"%description
+    fileContent += "CreationTime=%s"%strftime("%I:%M", current_time)
+    # %p option of strftime seems not working, then generate AM/PM by hands
+    if strftime("%I", current_time) == strftime("%H", current_time):
+        fileContent += "AM\n"
+    else:
+        fileContent += "PM\n"
+    fileContent += "CreationDate=%s\n"%strftime("%m-%d-%Y", current_time)
+    fileContent += "FileName=%s\n"%os.path.split(filepath)[-1]
+    fileContent += "FileVersion=1\n"
+    fileContent += "FileRevision=1\n"
+    fileContent += "EDSVersion=3.0\n"
+    
+    # Generate DeviceInfo section
+    fileContent += "\n[DeviceInfo]\n"
+    fileContent += "VendorName=CANFestival\n"
+    # Use information typed by user in Identity entry
+    fileContent += "VendorNumber=0x%8.8X\n"%Manager.GetCurrentEntry(0x1018, 1)
+    fileContent += "ProductName=%s\n"%nodename
+    fileContent += "ProductNumber=0x%8.8X\n"%Manager.GetCurrentEntry(0x1018, 2)
+    fileContent += "RevisionNumber=0x%8.8X\n"%Manager.GetCurrentEntry(0x1018, 3)
+    # CANFestival support all baudrates as soon as driver choosen support them
+    fileContent += "BaudRate_10=1\n"
+    fileContent += "BaudRate_20=1\n"
+    fileContent += "BaudRate_50=1\n"
+    fileContent += "BaudRate_125=1\n"
+    fileContent += "BaudRate_250=1\n"
+    fileContent += "BaudRate_500=1\n"
+    fileContent += "BaudRate_800=1\n"
+    fileContent += "BaudRate_1000=1\n"
+    # Select BootUp type from the informations given by user
+    fileContent += "SimpleBootUpMaster=%s\n"%BOOL_TRANSLATE[nodetype == "master"]
+    fileContent += "SimpleBootUpSlave=%s\n"%BOOL_TRANSLATE[nodetype == "slave"]
+    # CANFestival characteristics
+    fileContent += "Granularity=8\n"
+    fileContent += "DynamicChannelsSupported=0\n"
+    fileContent += "CompactPDO=0\n"
+    fileContent += "GroupMessaging=0\n"
+    # Calculate receive and tranmit PDO numbers with the entry available
+    fileContent += "NrOfRXPDO=%d\n"%len([idx for idx in entries if 0x1400 <= idx <= 0x15FF])
+    fileContent += "NrOfTXPDO=%d\n"%len([idx for idx in entries if 0x1400 <= idx <= 0x15FF])
+    # LSS not supported as soon as DS-302 was not fully implemented
+    fileContent += "LSS_Supported=0\n"
+    
+    # Generate Dummy Usage section
+    fileContent += "\n[DummyUsage]\n"
+    fileContent += "Dummy0001=0\n"
+    fileContent += "Dummy0002=1\n"
+    fileContent += "Dummy0003=1\n"
+    fileContent += "Dummy0004=1\n"
+    fileContent += "Dummy0005=1\n"
+    fileContent += "Dummy0006=1\n"
+    fileContent += "Dummy0007=1\n"
+
+    # Generate Comments section
+    fileContent += "\n[Comments]\n"
+    fileContent += "Lines=0\n"
+    
+    # List of entry by type (Mandatory, Optional or Manufacturer
+    mandatories = []
+    optionals = []
+    manufacturers = []
+    
+    # For each entry, we generate the entry section or sections if there is subindexes
+    for entry in entries:
+        # Extract infos and values for the entry
+        entry_infos = Manager.GetEntryInfos(entry)
+        values = Manager.GetCurrentEntry(entry)
+        # Define section name
+        text = "\n[%X]\n"%entry
+        # If there is only one value, it's a VAR entry
+        if type(values) != ListType:
+            # Extract the informations of the first subindex
+            subentry_infos = Manager.GetSubentryInfos(entry, 0)
+            # Generate EDS informations for the entry
+            text += "ParameterName=%s\n"%subentry_infos["name"]
+            text += "ObjectType=0x7\n"
+            text += "DataType=0x%4.4X\n"%subentry_infos["type"]
+            text += "AccessType=%s\n"%subentry_infos["access"]
+            text += "DefaultValue=%s\n"%values
+            text += "PDOMapping=%s\n"%BOOL_TRANSLATE[subentry_infos["pdo"]]
+        else:
+            # Generate EDS informations for the entry
+            text += "ParameterName=%s\n"%entry_infos["name"]
+            if entry_infos["struct"] & node.OD_IdenticalSubindexes:
+                text += "ObjectType=0x9\n"
+            else:
+                text += "ObjectType=0x8\n"
+            
+            # Generate EDS informations for subindexes of the entry in a separate text
+            subtext = ""
+            # Reset number of subindex defined 
+            nb_subentry = 0
+            for subentry, value in enumerate(values):
+                # Extract the informations of each subindex
+                subentry_infos = Manager.GetSubentryInfos(entry, subentry)
+                # If entry is not for the compatibility, generate informations for subindex
+                if subentry_infos["name"] != "Compatibility Entry":
+                    subtext += "\n[%Xsub%X]\n"%(entry, subentry)
+                    subtext += "ParameterName=%s\n"%subentry_infos["name"]
+                    subtext += "ObjectType=0x7\n"
+                    subtext += "DataType=0x%4.4X\n"%subentry_infos["type"]
+                    subtext += "AccessType=%s\n"%subentry_infos["access"]
+                    subtext += "DefaultValue=%s\n"%value
+                    subtext += "PDOMapping=%s\n"%BOOL_TRANSLATE[subentry_infos["pdo"]]
+                    # Increment number of subindex defined 
+                    nb_subentry += 1
+            # Write number of subindex defined for the entry
+            text += "SubNumber=%d\n"%nb_subentry
+            # Write subindex definitions
+            text += subtext
+        
+        # Then we add the entry in the right list
+        
+        # First case, entry is between 0x2000 and 0x5FFF, then it's a manufacturer entry
+        if 0x2000 <= entry <= 0x5FFF:
+            manufacturers.append(entry)
+        # Second case, entry is required, then it's a mandatory entry
+        elif entry_infos["need"]:
+            mandatories.append(entry)
+        # In any other case, it's an optional entry
+        else:
+            optionals.append(entry)
+        # Save text of the entry in the dictiionary of contents
+        indexContents[entry] = text
+    
+    # Before generate File Content we sort the entry list
+    manufacturers.sort()
+    mandatories.sort()
+    optionals.sort()
+    
+    # Generate Definition of mandatory objects
+    fileContent += "\n[MandatoryObjects]\n"
+    fileContent += "SupportedObjects=%d\n"%len(mandatories)
+    for idx, entry in enumerate(mandatories):
+        fileContent += "%d=0x%4.4X\n"%(idx, entry)
+    # Write mandatory entries
+    for entry in mandatories:
+        fileContent += indexContents[entry]
+    
+    # Generate Definition of optional objects
+    fileContent += "\n[OptionalObjects]\n"
+    fileContent += "SupportedObjects=%d\n"%len(optionals)
+    for idx, entry in enumerate(optionals):
+        fileContent += "%d=0x%4.4X\n"%(idx, entry)
+    # Write optional entries
+    for entry in optionals:
+        fileContent += indexContents[entry]
+
+    # Generate Definition of manufacturer objects
+    fileContent += "\n[ManufacturerObjects]\n"
+    fileContent += "SupportedObjects=%d\n"%len(manufacturers)
+    for idx, entry in enumerate(manufacturers):
+        fileContent += "%d=0x%4.4X\n"%(idx, entry)
+    # Write manufacturer entries
+    for entry in manufacturers:
+        fileContent += indexContents[entry]
+    
+    # Return File Content
+    return fileContent
+
+
+# Function that generates EDS file from current node edited
+def GenerateEDSFile(filepath, manager):
+    global Manager
+    Manager = manager
+    try:
+        # Generate file content
+        content = GenerateFileContent(filepath)
+        # Write file
+        WriteFile(filepath, content)
+        return None
+    except ValueError, message:
+        return "Unable to generate EDS file\n%s"%message
+    
+
+# Function that generates Node from an EDS file
+def GenerateNode(filepath, manager, cwd):
+    global Node
+    global Manager
+    Manager = manager
+    # Create a new node
+    Node = node.Node()
+    try:
+        # Parse file and extract dictionary of EDS entry
+        eds_dict = ParseFile(filepath)
+        # Extract Profile Number from Device Type entry
+        ProfileNb = eds_dict[0x1000]["DEFAULTVALUE"] & 0x0000ffff
+        # If profile is not DS-301 or DS-302
+        if ProfileNb not in [301, 302]:
+            # Compile Profile name and path to .prf file
+            ProfileName = "DS-%d"%ProfileNb
+            ProfilePath = os.path.join(cwd, "config/%s.prf"%ProfileName)
+            # Verify that profile is available
+            if os.path.isfile(ProfilePath):
+                try:
+                    # Load Profile
+                    execfile(ProfilePath)
+                    Node.SetProfileName(ProfileName)
+                    Node.SetProfile(Mapping)
+                    Node.SetSpecificMenu(AddMenuEntries)
+                except:
+                    pass
+        # Read all entries in the EDS dictionary 
+        for entry, values in eds_dict.items():
+            # All sections with a name in keynames are escaped
+            if entry in SECTION_KEYNAMES:
+                pass
+            else:
+                # Extract informations for the entry
+                entry_infos = Manager.GetEntryInfos(entry, Node)
+                
+                # If no informations are available, then we write them
+                if not entry_infos:
+                    # First case, entry is a VAR
+                    if values["OBJECTTYPE"] == 7:
+                        # Add mapping for entry
+                        Node.AddMappingEntry(entry, name = values["PARAMETERNAME"], struct = 1)
+                        # Add mapping for first subindex
+                        Node.AddMappingEntry(entry, 0, values = {"name" : values["PARAMETERNAME"], 
+                                                                 "type" : values["DATATYPE"], 
+                                                                 "access" : values["ACCESSTYPE"], 
+                                                                 "pdo" : values["PDOMAPPING"] == 1})
+                    # Second case, entry is an ARRAY
+                    elif values["OBJECTTYPE"] == 8:
+                        # Extract maximum subindex number defined
+                        try:
+                            max_subindex = values["subindexes"][0]["DEFAULTVALUE"]
+                        except:
+                            raise SyntaxError, "Error on entry 0x%4.4X:\nSubindex 0 must be defined for an ARRAY entry"%entry
+                        # Add mapping for entry
+                        Node.AddMappingEntry(entry, name = values["PARAMETERNAME"], struct = 3)
+                        # Add mapping for first subindex
+                        Node.AddMappingEntry(entry, 0, values = {"name" : "Number of Entries", "type" : 0x05, "access" : "ro", "pdo" : False})
+                        # Add mapping for other subindexes
+                        for subindex in xrange(1, int(max_subindex) + 1):
+                            # if subindex is defined
+                            if subindex in values["subindexes"]:
+                                Node.AddMappingEntry(entry, subindex, values = {"name" : values["subindexes"][subindex]["PARAMETERNAME"], 
+                                                                                "type" : values["subindexes"][subindex]["DATATYPE"], 
+                                                                                "access" : values["subindexes"][subindex]["ACCESSTYPE"], 
+                                                                                "pdo" : values["subindexes"][subindex]["PDOMAPPING"] == 1})
+                            # if not, we add a mapping for compatibility 
+                            else:
+                                Node.AddMappingEntry(entry, subindex, values = {"name" : "Compatibility Entry", "type" : 0x05, "access" : "rw", "pdo" : False})
+                    # Third case, entry is an RECORD
+                    elif values["OBJECTTYPE"] == 9:
+                        # Verify that the first subindex is defined
+                        if 0 not in values["subindexes"]:
+                            raise SyntaxError, "Error on entry 0x%4.4X:\nSubindex 0 must be defined for a RECORD entry"%entry
+                        # Add mapping for entry
+                        Node.AddMappingEntry(entry, name = values["PARAMETERNAME"], struct = 7)
+                        # Add mapping for first subindex
+                        Node.AddMappingEntry(entry, 0, values = {"name" : "Number of Entries", "type" : 0x05, "access" : "ro", "pdo" : False})
+                        # Verify that second subindex is defined
+                        if 1 in values:
+                            Node.AddMappingEntry(entry, 1, values = {"name" : values["PARAMETERNAME"] + " %d[(sub)]", 
+                                                                     "type" : values["subindexes"][1]["DATATYPE"], 
+                                                                     "access" : values["subindexes"][1]["ACCESSTYPE"], 
+                                                                     "pdo" : values["subindexes"][1]["PDOMAPPING"] == 1})
+                        else:
+                            raise SyntaxError, "Error on entry 0x%4.4X:\nA RECORD entry must have at least 2 subindexes"%entry
+                
+                # Define entry for the new node
+                
+                # First case, entry is a VAR
+                if values["OBJECTTYPE"] == 7:
+                    # Take default value if it is defined
+                    if "DEFAULTVALUE" in values:
+                        value = values["DEFAULTVALUE"]
+                    # Find default value for value type of the entry
+                    else:
+                        value = GetDefaultValue(entry)
+                    Node.AddEntry(entry, 0, value)
+                # Second case, entry is an ARRAY or a RECORD
+                elif values["OBJECTTYPE"] in (8, 9):
+                    # Verify that "Subnumber" attribute is defined and has a valid value
+                    if "SUBNUMBER" in values and values["SUBNUMBER"] > 0:
+                        # Extract maximum subindex number defined
+                        try:
+                            max_subindex = values["subindexes"][0]["DEFAULTVALUE"]
+                        except:
+                            raise SyntaxError, "Error on entry 0x%4.4X:\nSubindex 0 must be defined for an ARRAY or a RECORD entry"%entry
+                        # Define value for all subindexes except the first 
+                        for subindex in xrange(1, int(max_subindex) + 1):
+                            # Take default value if it is defined and entry is defined
+                            if subindex in values["subindexes"] and "DEFAULTVALUE" in values["subindexes"][subindex]:
+                                value = values["subindexes"][subindex]["DEFAULTVALUE"]
+                            # Find default value for value type of the subindex
+                            else:
+                                value = GetDefaultValue(entry, subindex)
+                            Node.AddEntry(entry, subindex, value)
+                    else:
+                        raise SyntaxError, "Array or Record entry 0x%4.4X must have a \"SubNumber\" attribute"%entry
+        return Node
+    except SyntaxError, message:
+        return "Unable to import EDS File\n%s"%message
+
+#-------------------------------------------------------------------------------
+#                             Main Function
+#-------------------------------------------------------------------------------
+
+if __name__ == '__main__':
+    print ParseFile("examples/PEAK MicroMod.eds")
+