objdictgen/node.py
author etisserant
Fri, 11 Jan 2008 09:39:49 +0100
changeset 352 3347581c3140
parent 327 b904d9a99e28
child 418 64a8c24b61a5
permissions -rwxr-xr-x
Fixed timer problem on late Heartbeat. Thanks to Jorge.
#!/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 cPickle
from types import *
import re

"""
Dictionary of translation between access symbol and their signification
"""
AccessType = {"ro" : "Read Only", "wo" : "Write Only", "rw" : "Read/Write"}

BoolType = {True : "True", False : "False"} 
OptionType = {True : "Yes", False : "No"}

CustomisableTypes = [(0x02, 0), (0x03, 0), (0x04, 0), (0x05, 0), (0x06, 0), (0x07, 0),
    (0x08, 0), (0x09, 1), (0x0A, 1), (0x0B, 1), (0x10, 0), (0x11, 0), (0x12, 0),
    (0x13, 0), (0x14, 0), (0x15, 0), (0x16, 0), (0x18, 0), (0x19, 0), (0x1A, 0),
    (0x1B, 0)]

DefaultParams = {"comment" : "", "save" : False}

#-------------------------------------------------------------------------------
#                      Dictionary Mapping and Organisation
#-------------------------------------------------------------------------------

"""
Properties of entry structure in the Object Dictionary
"""
OD_Subindex = 1             # Entry has at least one subindex
OD_MultipleSubindexes = 2   # Entry has more than one subindex
OD_IdenticalSubindexes = 4  # Subindexes of entry have the same description
OD_IdenticalIndexes = 8     # Entry has the same description on multiple indexes

"""
Structures of entry in the Object Dictionary, sum of the properties described above
for all sorts of entries use in CAN Open specification
"""
nosub = 0 # Entry without subindex (only for type declaration)
var = OD_Subindex
array = OD_Subindex | OD_MultipleSubindexes
rec = OD_Subindex | OD_MultipleSubindexes | OD_IdenticalSubindexes
# Entries identical on multiple indexes
plurivar = OD_Subindex | OD_IdenticalIndexes
pluriarray = OD_Subindex | OD_MultipleSubindexes | OD_IdenticalIndexes # Example : PDO Parameters
plurirec = OD_Subindex | OD_MultipleSubindexes | OD_IdenticalSubindexes |OD_IdenticalIndexes   # Example : PDO Mapping

"""
MappingDictionary is the structure used for writing a good organised Object
Dictionary. It follows the specifications of the CANOpen standard.
Change the informations within it if there is a mistake. But don't modify the
organisation of this object, it will involve in a malfunction of the application.
"""

MappingDictionary = {
    0x0001 : {"name" : "BOOLEAN", "struct" : nosub, "size" : 1, "default" : False, "values" : []},
    0x0002 : {"name" : "INTEGER8", "struct" : nosub, "size" : 8, "default" : 0, "values" : []},
    0x0003 : {"name" : "INTEGER16", "struct" : nosub, "size" : 16, "default" : 0, "values" : []},
    0x0004 : {"name" : "INTEGER32", "struct" : nosub, "size" : 32, "default" : 0, "values" : []},
    0x0005 : {"name" : "UNSIGNED8", "struct" : nosub, "size" : 8, "default" : 0, "values" : []},
    0x0006 : {"name" : "UNSIGNED16", "struct" : nosub, "size" : 16, "default" : 0, "values" : []},
    0x0007 : {"name" : "UNSIGNED32", "struct" : nosub, "size" : 32, "default" : 0, "values" : []},
    0x0008 : {"name" : "REAL32", "struct" : nosub, "size" : 32, "default" : 0.0, "values" : []},
    0x0009 : {"name" : "VISIBLE_STRING", "struct" : nosub, "size" : 8, "default" : "", "values" : []},
    0x000A : {"name" : "OCTET_STRING", "struct" : nosub, "size" : 8, "default" : "", "values" : []},
    0x000B : {"name" : "UNICODE_STRING", "struct" : nosub, "size" : 16, "default" : "", "values" : []},
#    0x000C : {"name" : "TIME_OF_DAY", "struct" : nosub, "size" : 48, "default" : 0, "values" : []},
#    0x000D : {"name" : "TIME_DIFFERENCE", "struct" : nosub, "size" : 48, "default" : 0, "values" : []},
    0x000F : {"name" : "DOMAIN", "struct" : nosub, "size" : 0, "default" : "", "values" : []},
    0x0010 : {"name" : "INTEGER24", "struct" : nosub, "size" : 24, "default" : 0, "values" : []},
    0x0011 : {"name" : "REAL64", "struct" : nosub, "size" : 64, "default" : 0.0, "values" : []},
    0x0012 : {"name" : "INTEGER40", "struct" : nosub, "size" : 40, "default" : 0, "values" : []},
    0x0013 : {"name" : "INTEGER48", "struct" : nosub, "size" : 48, "default" : 0, "values" : []},
    0x0014 : {"name" : "INTEGER56", "struct" : nosub, "size" : 56, "default" : 0, "values" : []},
    0x0015 : {"name" : "INTEGER64", "struct" : nosub, "size" : 64, "default" : 0, "values" : []},
    0x0016 : {"name" : "UNSIGNED24", "struct" : nosub, "size" : 24, "default" : 0, "values" : []},
    0x0018 : {"name" : "UNSIGNED40", "struct" : nosub, "size" : 40, "default" : 0, "values" : []},
    0x0019 : {"name" : "UNSIGNED48", "struct" : nosub, "size" : 48, "default" : 0, "values" : []},
    0x001A : {"name" : "UNSIGNED56", "struct" : nosub, "size" : 56, "default" : 0, "values" : []},
    0x001B : {"name" : "UNSIGNED64", "struct" : nosub, "size" : 64, "default" : 0, "values" : []},
    0x1000 : {"name" : "Device Type", "struct" : var, "need" : True, "values" : 
                [{"name" : "Device Type", "type" : 0x07, "access" : 'ro', "pdo" : False}]},
    0x1001 : {"name" : "Error Register", "struct" : var,  "need" : True, "values" : 
                [{"name" : "Error Register", "type" : 0x05, "access": 'ro', "pdo" : True}]},
    0x1002 : {"name" : "Manufacturer Status Register", "struct" : var, "need" : False,  "values" :
                [{"name" : "Manufacturer Status Register", "type" : 0x07, "access" : 'ro', "pdo" : True}]},
    0x1003 : {"name" : "Pre-defined Error Field", "struct" : rec, "need" : False, "callback" : True,  "values" :
                [{"name" : "Number of Errors", "type" : 0x05, "access" : 'rw', "pdo" : False},
                 {"name" : "Standard Error Field", "type" : 0x07, "access" : 'ro', "pdo" : False, "nbmax" : 0xFE}]},
    0x1005 : {"name" : "SYNC COB ID", "struct" : var, "need" : False, "callback" : True, "values" :
                [{"name" : "SYNC COB ID", "type" : 0x07, "access" : 'rw', "pdo" : False}]},
    0x1006 : {"name" : "Communication / Cycle Period", "struct" : var, "need" : False, "callback" : True, "values" :
                [{"name" : "Communication Cycle Period", "type" : 0x07, "access" : 'rw', "pdo" : False}]},
    0x1007 : {"name" : "Synchronous Window Length", "struct" : var, "need" : False, "values" :
                [{"name" : "Synchronous Window Length", "type" : 0x07, "access" : 'rw', "pdo" : False}]},
    0x1008 : {"name" : "Manufacturer Device Name", "struct" : var, "need" : False, "values" :
                [{"name" : "Manufacturer Device Name", "type" : 0x09, "access" : 'ro', "pdo" : False}]},
    0x1009 : {"name" : "Manufacturer Hardware Version", "struct" : var, "need" : False, "values" :
                [{"name" : "Manufacturer Hardware Version", "type" : 0x09, "access" : 'ro', "pdo" : False}]},
    0x100A : {"name" : "Manufacturer Software Version", "struct" : var, "need" : False, "values" :
                [{"name" : "Manufacturer Software Version", "type" : 0x09, "access" : 'ro', "pdo" : False}]},
    0x100C : {"name" : "Guard Time", "struct" : var, "need" : False, "values" :
                [{"name" : "Guard Time", "type" : 0x06, "access" : 'rw', "pdo" : False}]},
    0x100D : {"name" : "Life Time Factor", "struct" : var, "need" : False, "values" :
                [{"name" : "Life Time Factor", "type" : 0x05, "access" : 'rw', "pdo" : False}]},
    0x1010 : {"name" : "Store parameters", "struct" : array, "need" : False, "values" :
                [{"name" : "Number of Entries", "type" : 0x05, "access" : 'ro', "pdo" : False},
                 {"name" : "Save All Parameters", "type" : 0x07, "access" : 'rw', "pdo" : False},
                 {"name" : "Save Communication Parameters", "type" : 0x07, "access" : 'rw', "pdo" : False},
                 {"name" : "Save Application Parameters", "type" : 0x07, "access" : 'rw', "pdo" : False},
                 {"name" : "Save Manufacturer Parameters", "type" : 0x07, "access" : 'rw', "pdo" : False, "nbmax" : 0x7C}]},
    0x1011 : {"name" : "Restore Default Parameters", "struct" : array, "need" : False, "values" :
                [{"name" : "Number of Entries", "type" : 0x05, "access" : 'ro', "pdo" : False},
                 {"name" : "Restore All Default Parameters", "type" : 0x07, "access" : 'rw', "pdo" : False},
                 {"name" : "Restore Communication Default Parameters", "type" : 0x07, "access" : 'rw', "pdo" : False},
                 {"name" : "Restore Application Default Parameters", "type" : 0x07, "access" : 'rw', "pdo" : False},
                 {"name" : "Restore Manufacturer Default Parameters", "type" : 0x07, "access" : 'rw', "pdo" : False, "nbmax" : 0x7C}]},
    0x1012 : {"name" : "TIME COB ID", "struct" : var, "need" : False, "values" :
                [{"name" : "TIME COB ID", "type" : 0x07, "access" : 'rw', "pdo" : False}]},
    0x1013 : {"name" : "High Resolution Timestamp", "struct" : var, "need" : False, "values" :
                [{"name" : "High Resolution Time Stamp", "type" : 0x07, "access" : 'rw', "pdo" : True}]},
    0x1014 : {"name" : "Emergency COB ID", "struct" : var, "need" : False, "values" :
                [{"name" : "Emergency COB ID", "type" : 0x07, "access" : 'rw', "pdo" : False, "default" : "\"$NODEID+0x80\""}]},
    0x1015 : {"name" : "Inhibit Time Emergency", "struct" : var, "need" : False, "values" :
                [{"name" : "Inhibit Time Emergency", "type" : 0x06, "access" : 'rw', "pdo" : False}]},
    0x1016 : {"name" : "Consumer Heartbeat Time", "struct" : rec, "need" : False, "values" :
                [{"name" : "Number of Entries", "type" : 0x05, "access" : 'ro', "pdo" : False},
                 {"name" : "Consumer Heartbeat Time", "type" : 0x07, "access" : 'rw', "pdo" : False, "nbmin" : 1, "nbmax" : 0x7F}]},
    0x1017 : {"name" : "Producer Heartbeat Time", "struct" : var, "need" : False, "callback" : True, "values" :
                [{"name" : "Producer Heartbeat Time", "type" : 0x06, "access" : 'rw', "pdo" : False}]},
    0x1018 : {"name" : "Identity", "struct" : array, "need" : True, "values" :
                [{"name" : "Number of Entries", "type" : 0x05, "access" : 'ro', "pdo" : False},
                 {"name" : "Vendor ID", "type" : 0x07, "access" : 'ro', "pdo" : False},
                 {"name" : "Product Code", "type" : 0x07, "access" : 'ro', "pdo" : False},
                 {"name" : "Revision Number", "type" : 0x07, "access" : 'ro', "pdo" : False},
                 {"name" : "Serial Number", "type" : 0x07, "access" : 'ro', "pdo" : False}]},
    0x1020 : {"name" : "Verify Configuration", "struct" : array, "need" : False, "values" :
                [{"name" : "Number of Entries", "type" : 0x05, "access" : 'ro', "pdo" : False},
                 {"name" : "Configuration Date", "type" : 0x07, "access" : 'ro', "pdo" : False},
                 {"name" : "Configuration Time", "type" : 0x07, "access" : 'ro', "pdo" : False}]},
#    0x1021 : {"name" : "Store EDS", "struct" : var, "need" : False, "values" :
#                [{"name" : "Store EDS", "type" : 0x0F, "access" : 'rw', "pdo" : False}]},
#    0x1022 : {"name" : "Storage Format", "struct" : var, "need" : False, "values" :
#                [{"name" : "Storage Format", "type" : 0x06, "access" : 'rw', "pdo" : False}]},
    0x1023 : {"name" : "OS Command", "struct" : array, "need" : False, "values" :
                [{"name" : "Number of Entries", "type" : 0x05, "access" : 'ro', "pdo" : False},
                 {"name" : "Command", "type" : 0x0A, "access" : 'rw', "pdo" : False},
                 {"name" : "Status", "type" : 0x05, "access" : 'ro', "pdo" : False},
                 {"name" : "Reply", "type" : 0x0A, "access" : 'ro', "pdo" : False}]},
    0x1024 : {"name" : "OS Command Mode", "struct" : var, "need" : False, "values" :
                [{"name" : "OS Command Mode", "type" : 0x05, "access" : 'wo', "pdo" : False}]},
    0x1025 : {"name" : "OS Debugger Interface", "struct" : array, "need" : False, "values" :
                [{"name" : "Number of Entries", "type" : 0x05, "access" : 'ro', "pdo" : False},
                 {"name" : "Command", "type" : 0x0A, "access" : 'rw', "pdo" : False},
                 {"name" : "Status", "type" : 0x07, "access" : 'ro', "pdo" : False},
                 {"name" : "Reply", "type" : 0x0A, "access" : 'ro', "pdo" : False}]},
    0x1026 : {"name" : "OS Prompt", "struct" : array, "need" : False, "values" :
                [{"name" : "Number of Entries", "type" : 0x05, "access" : 'ro', "pdo" : False},
                 {"name" : "StdIn", "type" : 0x05, "access" : 'wo', "pdo" : True},
                 {"name" : "StdOut", "type" : 0x05, "access" : 'ro', "pdo" : True},
                 {"name" : "StdErr", "type" : 0x05, "access" : 'ro', "pdo" : True}]},
    0x1027 : {"name" : "Module List", "struct" : rec, "need" : False, "values" :
                [{"name" : "Number of Connected Modules", "type" : 0x05, "access" : 'ro', "pdo" : False},
                 {"name" : "Module %d[(sub)]", "type" : 0x06, "access" : 'ro', "pdo" : False, "nbmin" : 1, "nbmax" : 0xFE}]},
    0x1028 : {"name" : "Emergency Consumer", "struct" : rec, "need" : False, "values" :
                [{"name" : "Number of Consumed Emergency Objects", "type" : 0x05, "access" : 'ro', "pdo" : False},
                 {"name" : "Emergency Consumer", "type" : 0x07, "access" : 'rw', "pdo" : False, "nbmin" : 1, "nbmax" : 0x7E}]},
    0x1029 : {"name" : "Error Behavior", "struct" : array, "need" : False, "values" :
                [{"name" : "Number of Error Classes", "type" : 0x05, "access" : 'ro', "pdo" : False},
                 {"name" : "Communication Error", "type" : 0x05, "access" : 'rw', "pdo" : False},
                 {"name" : "Device Profile", "type" : 0x05, "access" : 'rw', "pdo" : False, "nbmax" : 0xFE}]},
    0x1200 : {"name" : "Server SDO Parameter", "struct" : array, "need" : False, "values" :
                [{"name" : "Number of Entries", "type" : 0x05, "access" : 'ro', "pdo" : False},
                 {"name" : "COB ID Client to Server (Receive SDO)", "type" : 0x07, "access" : 'ro', "pdo" : False, "default" : "\"$NODEID+0x600\""},
                 {"name" : "COB ID Server to Client (Transmit SDO)", "type" : 0x07, "access" : 'ro', "pdo" : False, "default" : "\"$NODEID+0x580\""}]},
    0x1201 : {"name" : "Additional Server SDO %d Parameter[(idx)]", "struct" : pluriarray, "incr" : 1, "nbmax" : 0x7F, "need" : False, "values" :
                [{"name" : "Number of Entries", "type" : 0x05, "access" : 'ro', "pdo" : False},
                 {"name" : "COB ID Client to Server (Receive SDO)", "type" : 0x07, "access" : 'ro', "pdo" : False},
                 {"name" : "COB ID Server to Client (Transmit SDO)", "type" : 0x07, "access" : 'ro', "pdo" : False},
                 {"name" : "Node ID of the SDO Client", "type" : 0x05, "access" : 'ro', "pdo" : False}]},
    0x1280 : {"name" : "Client SDO %d Parameter[(idx)]", "struct" : pluriarray, "incr" : 1, "nbmax" : 0x100, "need" : False, "values" :
                [{"name" : "Number of Entries", "type" : 0x05, "access" : 'ro', "pdo" : False},
                 {"name" : "COB ID Client to Server (Transmit SDO)", "type" : 0x07, "access" : 'rw', "pdo" : False},
                 {"name" : "COB ID Server to Client (Receive SDO)", "type" : 0x07, "access" : 'rw', "pdo" : False},
                 {"name" : "Node ID of the SDO Server", "type" : 0x05, "access" : 'rw', "pdo" : False}]},
    0x1400 : {"name" : "Receive PDO %d Parameter[(idx)]", "struct" : pluriarray, "incr" : 1, "nbmax" : 0x200, "need" : False, "values" :
                [{"name" : "Highest SubIndex Supported", "type" : 0x05, "access" : 'ro', "pdo" : False},
                 {"name" : "COB ID used by PDO", "type" : 0x07, "access" : 'rw', "pdo" : False, "default" : "{True:\"$NODEID+0x%X00\"%(base+2),False:0x80000000}[base<4]"},
                 {"name" : "Transmission Type", "type" : 0x05, "access" : 'rw', "pdo" : False},
                 {"name" : "Inhibit Time", "type" : 0x06, "access" : 'rw', "pdo" : False},
                 {"name" : "Compatibility Entry", "type" : 0x05, "access" : 'rw', "pdo" : False},
                 {"name" : "Event Timer", "type" : 0x06, "access" : 'rw', "pdo" : False}]},
    0x1600 : {"name" : "Receive PDO %d Mapping[(idx)]", "struct" : plurirec, "incr" : 1, "nbmax" : 0x200, "need" : False, "values" :
                [{"name" : "Number of Entries", "type" : 0x05, "access" : 'rw', "pdo" : False},
                 {"name" : "PDO %d Mapping for an application object %d[(idx,sub)]", "type" : 0x07, "access" : 'rw', "pdo" : False, "nbmin" : 0, "nbmax" : 0x40}]},
    0x1800 : {"name" : "Transmit PDO %d Parameter[(idx)]", "struct" : pluriarray, "incr" : 1, "nbmax" : 0x200, "need" : False, "callback" : True, "values" :
                [{"name" : "Highest SubIndex Supported", "type" : 0x05, "access" : 'ro', "pdo" : False},
                 {"name" : "COB ID used by PDO", "type" : 0x07, "access" : 'rw', "pdo" : False, "default" : "{True:\"$NODEID+0x%X80\"%(base+1),False:0x80000000}[base<4]"},
                 {"name" : "Transmission Type", "type" : 0x05, "access" : 'rw', "pdo" : False},
                 {"name" : "Inhibit Time", "type" : 0x06, "access" : 'rw', "pdo" : False},
                 {"name" : "Compatibility Entry", "type" : 0x05, "access" : 'rw', "pdo" : False},
                 {"name" : "Event Timer", "type" : 0x06, "access" : 'rw', "pdo" : False}]},
    0x1A00 : {"name" : "Transmit PDO %d Mapping[(idx)]", "struct" : plurirec, "incr" : 1, "nbmax" : 0x200, "need" : False, "values" :
                [{"name" : "Number of Entries", "type" : 0x05, "access" : 'rw', "pdo" : False},
                 {"name" : "PDO %d Mapping for a process data variable %d[(idx,sub)]", "type" : 0x07, "access" : 'rw', "pdo" : False, "nbmin" : 0, "nbmax" : 0x40}]},
}

#-------------------------------------------------------------------------------
#                         Search in a Mapping Dictionary
#-------------------------------------------------------------------------------

"""
Return the index of the typename given by searching in mappingdictionary 
"""
def FindTypeIndex(typename, mappingdictionary):
    testdic = {}
    for index, values in mappingdictionary.iteritems():
        if index < 0x1000:
            testdic[values["name"]] = index
    if typename in testdic:
        return testdic[typename]
    return None

"""
Return the name of the type by searching in mappingdictionary 
"""
def FindTypeName(typeindex, mappingdictionary):
    if typeindex < 0x1000 and typeindex in mappingdictionary:
        return mappingdictionary[typeindex]["name"]
    return None

"""
Return the default value of the type by searching in mappingdictionary 
"""
def FindTypeDefaultValue(typeindex, mappingdictionary):
    if typeindex < 0x1000 and typeindex in mappingdictionary:
        return mappingdictionary[typeindex]["default"]
    return None

"""
Return the list of types defined in mappingdictionary 
"""
def FindTypeList(mappingdictionary):
    list = []
    for index in mappingdictionary.keys():
        if index < 0x1000:
            list.append(mappingdictionary[index]["name"])
    return list

"""
Return the name of an entry by searching in mappingdictionary 
"""
def FindEntryName(index, mappingdictionary):
    base_index = FindIndex(index, mappingdictionary)
    if base_index:
        infos = mappingdictionary[base_index]
        if infos["struct"] & OD_IdenticalIndexes:
            return StringFormat(infos["name"], (index - base_index) / infos["incr"] + 1, 0)
        else:
            return infos["name"]
    return None

"""
Return the informations of one entry by searching in mappingdictionary 
"""
def FindEntryInfos(index, mappingdictionary):
    base_index = FindIndex(index, mappingdictionary)
    if base_index:
        copy = mappingdictionary[base_index].copy()
        if copy["struct"] & OD_IdenticalIndexes:
            copy["name"] = StringFormat(copy["name"], (index - base_index) / copy["incr"] + 1, 0)
        copy.pop("values")
        return copy
    return None

"""
Return the informations of one subentry of an entry by searching in mappingdictionary 
"""
def FindSubentryInfos(index, subIndex, mappingdictionary):
    base_index = FindIndex(index, mappingdictionary)
    if base_index:
        struct = mappingdictionary[base_index]["struct"]
        if struct & OD_Subindex:
            if struct & OD_IdenticalSubindexes:
                if struct & OD_IdenticalIndexes:
                    incr = mappingdictionary[base_index]["incr"]
                else:
                    incr = 1
                if subIndex == 0:
                    return mappingdictionary[base_index]["values"][0].copy()
                elif 0 < subIndex <= mappingdictionary[base_index]["values"][1]["nbmax"]:
                    copy = mappingdictionary[base_index]["values"][1].copy()
                    copy["name"] = StringFormat(copy["name"], (index - base_index) / incr + 1, subIndex)
                    return copy
            elif struct & OD_MultipleSubindexes and 0 <= subIndex < len(mappingdictionary[base_index]["values"]):
                return mappingdictionary[base_index]["values"][subIndex].copy()
            elif subIndex == 0:
                return mappingdictionary[base_index]["values"][0].copy()
    return None

"""
Return the list of variables that can be mapped defined in mappingdictionary 
"""
def FindMapVariableList(mappingdictionary, Node):
    list = []
    for index in mappingdictionary.iterkeys():
        if Node.IsEntry(index):
            for subIndex, values in enumerate(mappingdictionary[index]["values"]):
                if mappingdictionary[index]["values"][subIndex]["pdo"]:
                    infos = Node.GetEntryInfos(mappingdictionary[index]["values"][subIndex]["type"])
                    if mappingdictionary[index]["struct"] & OD_IdenticalSubindexes:
                        values = Node.GetEntry(index)
                        for i in xrange(len(values) - 1):
                            list.append((index, i + 1, infos["size"], StringFormat(mappingdictionary[index]["values"][subIndex]["name"],1,i+1)))
                    else:
                        list.append((index, subIndex, infos["size"], mappingdictionary[index]["values"][subIndex]["name"]))
    return list

"""
Return the list of mandatory indexes defined in mappingdictionary 
"""
def FindMandatoryIndexes(mappingdictionary):
    list = []
    for index in mappingdictionary.iterkeys():
        if index >= 0x1000 and mappingdictionary[index]["need"]:
            list.append(index)
    return list

"""
Return the index of the informations in the Object Dictionary in case of identical
indexes
"""
def FindIndex(index, mappingdictionary):
    if index in mappingdictionary:
        return index
    else:
        listpluri = [idx for idx in mappingdictionary.keys() if mappingdictionary[idx]["struct"] & OD_IdenticalIndexes]
        listpluri.sort()
        for idx in listpluri:
            nb_max = mappingdictionary[idx]["nbmax"]
            incr = mappingdictionary[idx]["incr"]
            if idx < index < idx + incr * nb_max and (index - idx)%incr == 0:
                return idx
    return None

#-------------------------------------------------------------------------------
#                           Formating Name of an Entry
#-------------------------------------------------------------------------------

name_model = re.compile('(.*)\[(.*)\]')

"""
Format the text given with the index and subindex defined
"""
def StringFormat(text, idx, sub):
    result = name_model.match(text)
    if result:
        format = result.groups()
        return format[0]%eval(format[1])
    else:
        return text

#-------------------------------------------------------------------------------
#                          Definition of Node Object
#-------------------------------------------------------------------------------

"""
Class recording the Object Dictionary entries. It checks at each modification
that the structure of the Object Dictionary stay coherent
"""

class Node:
    
    def __init__(self, name = "", type = "slave", id = 0, description = "", profilename = "DS-301", profile = {}, specificmenu = []):
        self.Name = name
        self.Type = type
        self.ID = id
        self.Description = description
        self.ProfileName = profilename
        self.Profile = profile
        self.SpecificMenu = specificmenu
        self.Dictionary = {}
        self.ParamsDictionary = {}
        self.DS302 = {}
        self.UserMapping = {}
    
    """
    Return the node name
    """
    def GetNodeName(self):
        return self.Name
    
    """
    Define the node name
    """
    def SetNodeName(self, name):
        self.Name = name

    """
    Return the node type ("master" or "slave")
    """
    def GetNodeType(self):
        return self.Type
    
    """
    Define the node type ("master" or "slave")
    """
    def SetNodeType(self, type):
        self.Type = type

    """
    Return the node ID
    """
    def GetNodeID(self):
        return self.ID
    
    """
    Define the node ID
    """
    def SetNodeID(self, id):
        self.ID = id

    """
    Return the node description
    """
    def GetNodeDescription(self):
        if getattr(self, "Description", False):
            return self.Description
        else:
            return ""
    
    """
    Define the node description
    """
    def SetNodeDescription(self, description):
        self.Description = description

    """
    Return the Specific Profile Name
    """
    def GetProfileName(self):
        return self.ProfileName
    
    """
    Define the Specific Profile Name
    """
    def SetProfileName(self, profilename):
        self.ProfileName = profilename

    """
    Return the Specific Profile
    """
    def GetProfile(self):
        return self.Profile
    
    """
    Define the Specific Profile
    """
    def SetProfile(self, profile):
        self.Profile = profile
    
    """
    Define the DS-302 Profile
    """
    def SetDS302Profile(self, profile):
        self.DS302 = profile
    
    """
    Define the DS-302 Profile
    """
    def GetDS302Profile(self):
        return self.DS302
    
    """
    Return the Specific Menu Entries
    """
    def GetSpecificMenu(self):
        return self.SpecificMenu
    
    """
    Define the Specific Menu Entries
    """
    def SetSpecificMenu(self, specificmenu):
        self.SpecificMenu = specificmenu
    
    """
    Extend the Specific Menu Entries
    """
    
    def ExtendSpecificMenu(self, specificmenu):
        self.SpecificMenu.extend(specificmenu)
    
    """
    Function which return the different Mappings available for this node
    """
    def GetMappings(self, userdefinedtoo = True):
        if userdefinedtoo:
            return [self.Profile, self.DS302, self.UserMapping]
        else:
            return [self.Profile, self.DS302]
    
    """
    Add a new entry in the Object Dictionary
    """
    def AddEntry(self, index, subIndex = None, value = None):
        if index not in self.Dictionary:
            if not subIndex:
                self.Dictionary[index] = value
                return True
            elif subIndex == 1:
                self.Dictionary[index] = [value]
                return True
        elif subIndex > 0 and type(self.Dictionary[index]) == ListType and subIndex == len(self.Dictionary[index]) + 1:
            self.Dictionary[index].append(value)
            return True
        return False

    """
    Warning ! Modifies an existing entry in the Object Dictionary. Can't add a new one.
    """
    def SetEntry(self, index, subIndex = None, value = None):
        if index in self.Dictionary:
            if not subIndex:
                if value != None:
                    self.Dictionary[index] = value
                return True
            elif type(self.Dictionary[index]) == ListType and 0 < subIndex <= len(self.Dictionary[index]):
                if value != None:
                    self.Dictionary[index][subIndex - 1] = value
                return True
        return False
    
    def SetParamsEntry(self, index, subIndex = None, comment = None, save = None, callback = None):
        if not getattr(self, "ParamsDictionary", False):
            self.ParamsDictionary = {}
        if index in self.Dictionary:
            if (comment != None or save != None or callback != None) and index not in self.ParamsDictionary:
                self.ParamsDictionary[index] = {}
            if subIndex == None or type(self.Dictionary[index]) != ListType and subIndex == 0:
                if comment != None:
                    self.ParamsDictionary[index]["comment"] = comment
                if save != None:
                    self.ParamsDictionary[index]["save"] = save
                if callback != None:
                    self.ParamsDictionary[index]["callback"] = callback
                return True
            elif type(self.Dictionary[index]) == ListType and 0 <= subIndex <= len(self.Dictionary[index]):
                if (comment != None or save != None or callback != None) and subIndex not in self.ParamsDictionary[index]:
                    self.ParamsDictionary[index][subIndex] = {}
                if comment != None:
                    self.ParamsDictionary[index][subIndex]["comment"] = comment
                if save != None:
                    self.ParamsDictionary[index][subIndex]["save"] = save
                return True
        return False
    
    """
    Removes an existing entry in the Object Dictionary. If a subIndex is specified
    it will remove this subIndex only if it's the last of the index. If no subIndex
    is specified it removes the whole index and subIndexes from the Object Dictionary.
    """
    def RemoveEntry(self, index, subIndex = None):
        if not getattr(self, "ParamsDictionary", False):
            self.ParamsDictionary = {}
        if index in self.Dictionary:
            if not subIndex:
                self.Dictionary.pop(index)
                if index in self.ParamsDictionary:
                    self.ParamsDictionary.pop(index)
                return True
            elif type(self.Dictionary[index]) == ListType and subIndex == len(self.Dictionary[index]):
                self.Dictionary[index].pop(subIndex - 1)
                if index in self.ParamsDictionary:
                    if subIndex in self.ParamsDictionary[index]:
                        self.ParamsDictionary[index].pop(subIndex)
                    if len(self.ParamsDictionary[index]) == 0:
                        self.ParamsDictionary.pop(index)
                if len(self.Dictionary[index]) == 0:
                    self.Dictionary.pop(index)
                    if index in self.ParamsDictionary:
                        self.ParamsDictionary.pop(index)
                return True
        return False
    
    """
    Check if an entry exists in the Object Dictionary and returns the answer.
    """
    def IsEntry(self, index, subIndex = None):
        if index in self.Dictionary:
            if not subIndex:
                return True
            return subIndex <= len(self.Dictionary[index])
        return False
    
    """
    Returns the value of the entry asked. If the entry has the value "count", it
    returns the number of subIndex in the entry except the first.
    """
    def GetEntry(self, index, subIndex = None, compute = True):
        if index in self.Dictionary:
            if subIndex == None:
                if type(self.Dictionary[index]) == ListType:
                    values = [len(self.Dictionary[index])]
                    for value in self.Dictionary[index]:
                        values.append(self.CompileValue(value, index, compute))
                    return values
                else:
                    return self.CompileValue(self.Dictionary[index], index, compute)
            elif subIndex == 0:
                if type(self.Dictionary[index]) == ListType:
                    return len(self.Dictionary[index])
                else:
                    return self.CompileValue(self.Dictionary[index], index, compute)
            elif type(self.Dictionary[index]) == ListType and 0 < subIndex <= len(self.Dictionary[index]):
                return self.CompileValue(self.Dictionary[index][subIndex - 1], index, compute)
        return None

    """
    Returns the value of the entry asked. If the entry has the value "count", it
    returns the number of subIndex in the entry except the first.
    """
    def GetParamsEntry(self, index, subIndex = None):
        if not getattr(self, "ParamsDictionary", False):
            self.ParamsDictionary = {}
        if index in self.Dictionary:
            if subIndex == None:
                if type(self.Dictionary[index]) == ListType:
                    if index in self.ParamsDictionary:
                        result = []
                        for i in xrange(len(self.Dictionary[index]) + 1):
                            line = DefaultParams.copy()
                            if i in self.ParamsDictionary[index]:
                                line.update(self.ParamsDictionary[index][i])
                            result.append(line)
                        return result
                    else:
                        return [DefaultParams.copy() for i in xrange(len(self.Dictionary[index]) + 1)]
                else:
                    result = DefaultParams.copy()
                    if index in self.ParamsDictionary:
                        result.update(self.ParamsDictionary[index])
                    return result
            elif subIndex == 0 and type(self.Dictionary[index]) != ListType:
                result = DefaultParams.copy()
                if index in self.ParamsDictionary:
                    result.update(self.ParamsDictionary[index])
                return result
            elif type(self.Dictionary[index]) == ListType and 0 <= subIndex <= len(self.Dictionary[index]):
                result = DefaultParams.copy()
                if index in self.ParamsDictionary and subIndex in self.ParamsDictionary[index]:
                    result.update(self.ParamsDictionary[index][subIndex])
                return result
        return None

    def HasEntryCallbacks(self, index):
        entry_infos = self.GetEntryInfos(index)
        if entry_infos and "callback" in entry_infos:
            return entry_infos["callback"]
        else:
            if not getattr(self, "ParamsDictionary", False):
                self.ParamsDictionary = {}
            if index in self.Dictionary and index in self.ParamsDictionary and "callback" in self.ParamsDictionary[index]:
                return self.ParamsDictionary[index]["callback"]
        return False

    """
    Check if an entry exists in the User Mapping Dictionary and returns the answer.
    """
    def IsMappingEntry(self, index):
        if index in self.UserMapping:
            return True
        return False

    """
    Add a new entry in the User Mapping Dictionary
    """
    def AddMappingEntry(self, index, subIndex = None, name = "Undefined", struct = 0, size = None, nbmax = None, default = None, values = None):
        if index not in self.UserMapping:
            if values == None:
                values = []
            if subIndex == None:
                self.UserMapping[index] = {"name" : name, "struct" : struct, "need" : False, "values" : values}
                if size != None:
                    self.UserMapping[index]["size"] = size
                if nbmax != None:
                    self.UserMapping[index]["nbmax"] = nbmax
                if default != None:
                    self.UserMapping[index]["default"] = default
                return True
        elif subIndex != None and subIndex == len(self.UserMapping[index]["values"]):
            if values == None:
                values = {}
            self.UserMapping[index]["values"].append(values)
            return True
        return False

    """
    Warning ! Modifies an existing entry in the User Mapping Dictionary. Can't add a new one.
    """
    def SetMappingEntry(self, index, subIndex = None, name = None, struct = None, size = None, nbmax = None, default = None, values = None):
        if index in self.UserMapping:
            if subIndex == None:
                if name != None:
                    self.UserMapping[index]["name"] = name
                    if self.UserMapping[index]["struct"] & OD_IdenticalSubindexes:
                        self.UserMapping[index]["values"][1]["name"] = name + " %d[(sub)]"
                    elif not self.UserMapping[index]["struct"] & OD_MultipleSubindexes:
                        self.UserMapping[index]["values"][0]["name"] = name
                if struct != None:
                    self.UserMapping[index]["struct"] = struct
                if size != None:
                    self.UserMapping[index]["size"] = size
                if nbmax != None:
                    self.UserMapping[index]["nbmax"] = nbmax
                if default != None:
                    self.UserMapping[index]["default"] = default
                if values != None:
                    self.UserMapping[index]["values"] = values
                return True
            elif 0 <= subIndex < len(self.UserMapping[index]["values"]) and values != None:
                if "type" in values:
                    if self.IsStringType(values["type"]) and not self.IsStringType(self.UserMapping[index]["values"][subIndex]["type"]):
                        self.SetEntry(index, subIndex, "")
                    elif not self.IsStringType(values["type"]) and self.IsStringType(self.UserMapping[index]["values"][subIndex]["type"]):
                        self.SetEntry(index, subIndex, 0)
                self.UserMapping[index]["values"][subIndex].update(values)
                return True
        return False
    
    """
    Removes an existing entry in the User Mapping Dictionary. If a subIndex is specified
    it will remove this subIndex only if it's the last of the index. If no subIndex
    is specified it removes the whole index and subIndexes from the User Mapping Dictionary.
    """
    def RemoveMappingEntry(self, index, subIndex = None):
        if index in self.UserMapping:
            if subIndex == None:
                self.UserMapping.pop(index)
                return True
            elif subIndex == len(self.UserMapping[index]["values"]) - 1:
                self.UserMapping[index]["values"].pop(subIndex)
                return True
        return False

    def RemoveMapVariable(self, index, subIndex = None):
        model = index << 16
        mask = 0xFFFF << 16
        if subIndex:
            model += subIndex << 8
            mask += 0xFF << 8
        for i in self.Dictionary.iterkeys():
            if 0x1600 <= i <= 0x17FF or 0x1A00 <= i <= 0x1BFF:
                for j,value in enumerate(self.Dictionary[i]):
                    if (value & mask) == model:
                        self.Dictionary[i][j] = 0
    
    def UpdateMapVariable(self, index, subIndex, size):
        model = index << 16
        mask = 0xFFFF << 16
        if subIndex:
            model += subIndex << 8
            mask = 0xFF << 8
        for i in self.Dictionary.iterkeys():
            if 0x1600 <= i <= 0x17FF or 0x1A00 <= i <= 0x1BFF:
                for j,value in enumerate(self.Dictionary[i]):
                    if (value & mask) == model:
                        self.Dictionary[i][j] = model + size
    
    def RemoveLine(self, index, max, incr = 1):
        i = index
        while i < max and self.IsEntry(i + incr):
            self.Dictionary[i] = self.Dictionary[i + incr]
            i += incr
        self.Dictionary.pop(i)

    def RemoveUserType(self, index):
        type = self.GetEntry(index, 1)
        for i in self.UserMapping:
            for value in self.UserMapping[i]["values"]:
                if value["type"] == index:
                    value["type"] = type
        self.RemoveMappingEntry(index)
        self.RemoveEntry(index)

    """
    Return a copy of the node
    """
    def Copy(self):
        return cPickle.loads(cPickle.dumps(self))

    """
    Return a sorted list of indexes in Object Dictionary
    """
    def GetIndexes(self):
        listindex = self.Dictionary.keys()
        listindex.sort()
        return listindex

    """
    Print the Dictionary values
    """
    def Print(self):
        print self.PrintString()
    
    def PrintString(self):
        result = ""
        listindex = self.Dictionary.keys()
        listindex.sort()
        for index in listindex:
            name = self.GetEntryName(index)
            values = self.Dictionary[index]
            if isinstance(values, ListType):
                result += "%04X (%s):\n"%(index, name)
                for subidx, value in enumerate(values):
                    subentry_infos = self.GetSubentryInfos(index, subidx + 1)
                    if index == 0x1F22 and value:
                        nb_params = BE_to_LE(value[:4])
                        data = value[4:]
                        value = "%d arg defined"%nb_params
                        i = 0
                        count = 1
                        while i < len(data):
                            value += "\n%04X %02X, arg %d: "%(index, subidx+1, count)
                            value += "%04X"%BE_to_LE(data[i:i+2])
                            value += " %02X"%BE_to_LE(data[i+2:i+3])
                            size = BE_to_LE(data[i+3:i+7])
                            value += " %08X"%size
                            value += (" %0"+"%d"%(size * 2)+"X")%BE_to_LE(data[i+7:i+7+size])
                            i += 7 + size
                            count += 1
                    elif isinstance(value, IntType):
                        value = "%X"%value
                    result += "%04X %02X (%s): %s\n"%(index, subidx+1, subentry_infos["name"], value)
            else:
                if isinstance(values, IntType):
                    values = "%X"%values
                result += "%04X (%s): %s\n"%(index, name, values)
        return result
            
    def CompileValue(self, value, index, compute = True):
        if type(value) == StringType and value.find("$NODEID") != -1:
            base = self.GetBaseIndex(index)
            try:
                raw = eval(value)
                if compute:
                    return eval(raw.replace("$NODEID","self.ID"))
                return raw
            except:
                return 0
        else:
            return value

#-------------------------------------------------------------------------------
#                         Node Informations Functions
#-------------------------------------------------------------------------------

    def GetBaseIndex(self, index):
        for mapping in self.GetMappings():
            result = FindIndex(index, mapping)
            if result != None:
                return (index - result) / mapping[result].get("incr", 1)
        result = FindIndex(index, MappingDictionary)
        if result != None:
            return (index - result) / MappingDictionary[result].get("incr", 1)
        return 0

    def GetCustomisedTypeValues(self, index):
        values = self.GetEntry(index)
        customisabletypes = self.GetCustomisableTypes()
        return values, customisabletypes[values[1]][1]

    def GetEntryName(self, index):
        result = None
        mappings = self.GetMappings()
        i = 0
        while not result and i < len(mappings):
            result = FindEntryName(index, mappings[i])
            i += 1
        if result == None:
            result = FindEntryName(index, MappingDictionary)
        return result
    
    def GetEntryInfos(self, index):
        result = None
        mappings = self.GetMappings()
        i = 0
        while not result and i < len(mappings):
            result = FindEntryInfos(index, mappings[i])
            i += 1
        if result == None:
            result = FindEntryInfos(index, MappingDictionary)
        return result
    
    def GetSubentryInfos(self, index, subIndex):
        result = None
        mappings = self.GetMappings()
        i = 0
        while not result and i < len(mappings):
            result = FindSubentryInfos(index, subIndex, mappings[i])
            if result:
                result["user_defined"] = i == len(mappings) - 1 and index >= 0x1000
            i += 1
        if result == None:
            result = FindSubentryInfos(index, subIndex, MappingDictionary)
            if result:
                result["user_defined"] = False
        return result
    
    def GetTypeIndex(self, typename):
        result = None
        mappings = self.GetMappings()
        i = 0
        while not result and i < len(mappings):
            result = FindTypeIndex(typename, mappings[i])
            i += 1
        if result == None:
            result = FindTypeIndex(typename, MappingDictionary)
        return result
    
    def GetTypeName(self, typeindex):
        result = None
        mappings = self.GetMappings()
        i = 0
        while not result and i < len(mappings):
            result = FindTypeName(typeindex, mappings[i])
            i += 1
        if result == None:
            result = FindTypeName(typeindex, MappingDictionary)
        return result
    
    def GetTypeDefaultValue(self, typeindex):
        result = None
        mappings = self.GetMappings()
        i = 0
        while not result and i < len(mappings):
            result = FindTypeDefaultValue(typeindex, mappings[i])
            i += 1
        if result == None:
            result = FindTypeDefaultValue(typeindex, MappingDictionary)
        return result
    
    def GetMapVariableList(self):
        list = FindMapVariableList(MappingDictionary, self)
        for mapping in self.GetMappings():
            list.extend(FindMapVariableList(mapping, self))
        list.sort()
        return list
    
    def GetMandatoryIndexes(self, node = None):
        list = FindMandatoryIndexes(MappingDictionary)
        for mapping in self.GetMappings():
            list.extend(FindMandatoryIndexes(mapping))
        return list
    
    def GetCustomisableTypes(self):
        dic = {}
        for index, valuetype in CustomisableTypes:
            name = self.GetTypeName(index)
            dic[index] = [name, valuetype]
        return dic

#-------------------------------------------------------------------------------
#                            Type helper functions
#-------------------------------------------------------------------------------

    def IsStringType(self, index):
        if index in (0x9, 0xA, 0xB):
            return True
        elif 0xA0 <= index < 0x100:
            result = self.GetEntry(index, 1)
            if result is not None and result in (0x9, 0xA, 0xB):
                return True
        return False

#-------------------------------------------------------------------------------
#                            Type and Map Variable Lists
#-------------------------------------------------------------------------------
    
    def GetTypeList(self):
        list = FindTypeList(MappingDictionary)
        for mapping in self.GetMappings():
            list.extend(FindTypeList(mapping))
        list.sort()
        return ",".join(list)

    """
    Generate the list of variables that can be mapped for the current node
    """
    def GenerateMapList(self):
        self.MapList = "None"
        self.NameTranslation = {"None" : "00000000"}
        self.MapTranslation = {"00000000" : "None"}
        list = self.GetMapVariableList()
        for index, subIndex, size, name in list:
            self.MapList += ",%s"%name
            map = "%04X%02X%02X"%(index,subIndex,size)
            self.NameTranslation[name] = map
            self.MapTranslation[map] = name

    def GetMapValue(self, mapname):
        if mapname == "None":
            return 0
        else:
            list = self.GetMapVariableList()
            for index, subIndex, size, name in list:
                if mapname == name:
                    return (index << 16) + (subIndex << 8) + size
            return None
    
    def GetMapName(self, value):
        if value != 0:
            index = value >> 16
            subindex = (value >> 8) % (1 << 8)
            result = self.GetSubentryInfos(index, subindex)
            if result:
                return result["name"]
        return "None"
    
    """
    Return the list of variables that can be mapped for the current node
    """
    def GetMapList(self):
        list = ["None"] + [name for index, subIndex, size, name in self.GetMapVariableList()]
        return ",".join(list)

def BE_to_LE(value):
    """
    Convert Big Endian to Little Endian 
    @param value: value expressed in Big Endian
    @param size: number of bytes generated
    @return: a string containing the value converted
    """
    
    data = [char for char in value]
    data.reverse()
    return int("".join(["%2.2X"%ord(char) for char in data]), 16)

def LE_to_BE(value, size):
    """
    Convert Little Endian to Big Endian
    @param value: value expressed in integer
    @param size: number of bytes generated
    @return: a string containing the value converted
    """
    
    data = ("%" + str(size * 2) + "." + str(size * 2) + "X") % value
    list_car = [data[i:i+2] for i in xrange(0, len(data), 2)]
    list_car.reverse()
    return "".join([chr(int(car, 16)) for car in list_car])