objdictgen/nodemanager.py
author fbeaulier
Tue, 16 Aug 2011 14:15:52 +0200
changeset 663 70fc3603e36f
parent 584 e23359f62023
child 762 4b60a0d313b0
permissions -rwxr-xr-x
timers_unix.c : remove sigint and sigterm catch
sdo : Allow multiple servers
The sdo transfer struct is not anymore referenced by server's node id but by
client or server number in the OD. Node id is not relevant in SDO transfert.
#!/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

from gnosis.xml.pickle import *
from gnosis.xml.pickle.util import setParanoia
setParanoia(0)

from node import *
import eds_utils, gen_cfile

from types import *
import os, re

UndoBufferLength = 20

type_model = re.compile('([\_A-Z]*)([0-9]*)')
range_model = re.compile('([\_A-Z]*)([0-9]*)\[([\-0-9]*)-([\-0-9]*)\]')

# ID for the file viewed
CurrentID = 0

# Returns a new id
def GetNewId():
    global CurrentID
    CurrentID += 1
    return CurrentID

"""
Class implementing a buffer of changes made on the current editing Object Dictionary
"""

class UndoBuffer:

    """
    Constructor initialising buffer
    """
    def __init__(self, currentstate, issaved = False):
        self.Buffer = []
        self.CurrentIndex = -1
        self.MinIndex = -1
        self.MaxIndex = -1
        # if current state is defined
        if currentstate:
            self.CurrentIndex = 0
            self.MinIndex = 0
            self.MaxIndex = 0
        # Initialising buffer with currentstate at the first place
        for i in xrange(UndoBufferLength):
            if i == 0:
                self.Buffer.append(currentstate)
            else:
                self.Buffer.append(None)
        # Initialising index of state saved
        if issaved:
            self.LastSave = 0
        else:
            self.LastSave = -1
    
    """
    Add a new state in buffer
    """
    def Buffering(self, currentstate):
        self.CurrentIndex = (self.CurrentIndex + 1) % UndoBufferLength
        self.Buffer[self.CurrentIndex] = currentstate
        # Actualising buffer limits
        self.MaxIndex = self.CurrentIndex
        if self.MinIndex == self.CurrentIndex:
            # If the removed state was the state saved, there is no state saved in the buffer
            if self.LastSave == self.MinIndex:
                self.LastSave = -1
            self.MinIndex = (self.MinIndex + 1) % UndoBufferLength
        self.MinIndex = max(self.MinIndex, 0)
    
    """
    Return current state of buffer
    """
    def Current(self):
        return self.Buffer[self.CurrentIndex]
    
    """
    Change current state to previous in buffer and return new current state
    """
    def Previous(self):
        if self.CurrentIndex != self.MinIndex:
            self.CurrentIndex = (self.CurrentIndex - 1) % UndoBufferLength
            return self.Buffer[self.CurrentIndex]
        return None
    
    """
    Change current state to next in buffer and return new current state
    """
    def Next(self):
        if self.CurrentIndex != self.MaxIndex:
            self.CurrentIndex = (self.CurrentIndex + 1) % UndoBufferLength
            return self.Buffer[self.CurrentIndex]
        return None
    
    """
    Return True if current state is the first in buffer
    """
    def IsFirst(self):
        return self.CurrentIndex == self.MinIndex
    
    """
    Return True if current state is the last in buffer
    """
    def IsLast(self):
        return self.CurrentIndex == self.MaxIndex

    """
    Note that current state is saved
    """
    def CurrentSaved(self):
        self.LastSave = self.CurrentIndex
        
    """
    Return True if current state is saved
    """
    def IsCurrentSaved(self):
        return self.LastSave == self.CurrentIndex



"""
Class which control the operations made on the node and answer to view requests
"""

class NodeManager:

    """
    Constructor
    """
    def __init__(self):
        self.LastNewIndex = 0
        self.FilePaths = {}
        self.FileNames = {}
        self.NodeIndex = None
        self.CurrentNode = None
        self.UndoBuffers = {}

#-------------------------------------------------------------------------------
#                         Type and Map Variable Lists
#-------------------------------------------------------------------------------
    
    """
    Return the list of types defined for the current node
    """
    def GetCurrentTypeList(self):
        if self.CurrentNode:
            return self.CurrentNode.GetTypeList()
        else:
            return ""

    """
    Return the list of variables that can be mapped for the current node
    """
    def GetCurrentMapList(self):
        if self.CurrentNode:
            return self.CurrentNode.GetMapList()
        else:
            return ""

#-------------------------------------------------------------------------------
#                        Create Load and Save Functions
#-------------------------------------------------------------------------------

    """
    Create a new node and add a new buffer for storing it
    """
    def CreateNewNode(self, name, id, type, description, profile, filepath, NMT, options):
        # Create a new node
        node = Node()
        # Try to load profile given
        result = self.LoadProfile(profile, filepath, node)
        if not result:
            # if success, initialising node
            self.CurrentNode = node
            self.CurrentNode.SetNodeName(name)
            self.CurrentNode.SetNodeID(id)
            self.CurrentNode.SetNodeType(type)
            self.CurrentNode.SetNodeDescription(description)
            AddIndexList = self.GetMandatoryIndexes()
            AddSubIndexList = []
            if NMT == "NodeGuarding":
                AddIndexList.extend([0x100C, 0x100D])
            elif NMT == "Heartbeat":
                AddIndexList.append(0x1017)
            for option in options:
                if option == "DS302":
                    DS302Path = os.path.join(os.path.split(__file__)[0], "config/DS-302.prf")
                    # Charging DS-302 profile if choosen by user
                    if os.path.isfile(DS302Path):
                        try:
                            execfile(DS302Path)
                            self.CurrentNode.SetDS302Profile(Mapping)
                            self.CurrentNode.ExtendSpecificMenu(AddMenuEntries)
                        except:
                            return _("Problem with DS-302! Syntax Error.")
                    else:
                        return _("Couldn't find DS-302 in 'config' folder!")
                elif option == "GenSYNC":
                    AddIndexList.extend([0x1005, 0x1006])
                elif option == "Emergency":
                    AddIndexList.append(0x1014)
                elif option == "SaveConfig":
                    AddIndexList.extend([0x1010, 0x1011, 0x1020])
                elif option == "StoreEDS":
                    AddIndexList.extend([0x1021, 0x1022])
            if type == "slave":
                # add default SDO server
                AddIndexList.append(0x1200)
                # add default 4 receive and 4 transmit PDO
                for comm, mapping in [(0x1400, 0x1600),(0x1800, 0x1A00)]:
                    firstparamindex = self.GetLineFromIndex(comm)
                    firstmappingindex = self.GetLineFromIndex(mapping)
                    AddIndexList.extend(range(firstparamindex, firstparamindex + 4))
                    for idx in range(firstmappingindex, firstmappingindex + 4):
                        AddIndexList.append(idx)
                        AddSubIndexList.append((idx, 8))
            # Add a new buffer 
            index = self.AddNodeBuffer(self.CurrentNode.Copy(), False)
            self.SetCurrentFilePath("")
            # Add Mandatory indexes
            self.ManageEntriesOfCurrent(AddIndexList, [])
            for idx, num in AddSubIndexList:
                self.AddSubentriesToCurrent(idx, num)
            return index
        else:
            return result
    
    """
    Load a profile in node
    """
    def LoadProfile(self, profile, filepath, node):
        if profile != "None":
            # Try to charge the profile given
            try:
                execfile(filepath)
                node.SetProfileName(profile)
                node.SetProfile(Mapping)
                node.SetSpecificMenu(AddMenuEntries)
                return None
            except:
                return _("Syntax Error\nBad OD Profile file!")
        else:
            # Default profile
            node.SetProfileName("None")
            node.SetProfile({})
            node.SetSpecificMenu([])
            return None

    """
    Open a file and store it in a new buffer
    """
    def OpenFileInCurrent(self, filepath):
        try:
            # Open and load file
            file = open(filepath, "r")
            node = load(file)
            file.close()
            self.CurrentNode = node
            self.CurrentNode.SetNodeID(0)
            # Add a new buffer and defining current state
            index = self.AddNodeBuffer(self.CurrentNode.Copy(), True)
            self.SetCurrentFilePath(filepath)
            return index
        except:
            return _("Unable to load file \"%s\"!")%filepath

    """
    Save current node in  a file
    """
    def SaveCurrentInFile(self, filepath = None):
        # if no filepath given, verify if current node has a filepath defined
        if not filepath:
            filepath = self.GetCurrentFilePath()
            if filepath == "":
                return False
        # Save node in file
        file = open(filepath, "w")
        dump(self.CurrentNode, file)
        file.close()
        self.SetCurrentFilePath(filepath)
        # Update saved state in buffer
        self.UndoBuffers[self.NodeIndex].CurrentSaved()
        return True

    """
    Close current state
    """
    def CloseCurrent(self, ignore = False):
        # Verify if it's not forced that the current node is saved before closing it
        if self.NodeIndex in self.UndoBuffers and (self.UndoBuffers[self.NodeIndex].IsCurrentSaved() or ignore):
            self.RemoveNodeBuffer(self.NodeIndex)
            if len(self.UndoBuffers) > 0:
                previousindexes = [idx for idx in self.UndoBuffers.keys() if idx < self.NodeIndex]
                nextindexes = [idx for idx in self.UndoBuffers.keys() if idx > self.NodeIndex]
                if len(previousindexes) > 0:
                    previousindexes.sort()
                    self.NodeIndex = previousindexes[-1]
                elif len(nextindexes) > 0:
                    nextindexes.sort()
                    self.NodeIndex = nextindexes[0]
                else:
                    self.NodeIndex = None
            else:
                self.NodeIndex = None
            return True
        return False

    """
    Import an eds file and store it in a new buffer if no node edited
    """
    def ImportCurrentFromEDSFile(self, filepath):
        # Generate node from definition in a xml file
        result = eds_utils.GenerateNode(filepath)
        if isinstance(result, Node):
            self.CurrentNode = result
            index = self.AddNodeBuffer(self.CurrentNode.Copy(), False)
            self.SetCurrentFilePath("")
            return index
        else:
            return result
    
    """
    Export to an eds file and store it in a new buffer if no node edited
    """
    def ExportCurrentToEDSFile(self, filepath):
        return eds_utils.GenerateEDSFile(filepath, self.CurrentNode)
    
    """
    Build the C definition of Object Dictionary for current node 
    """
    def ExportCurrentToCFile(self, filepath):
        if self.CurrentNode:
            return gen_cfile.GenerateFile(filepath, self.CurrentNode)

#-------------------------------------------------------------------------------
#                        Add Entries to Current Functions
#-------------------------------------------------------------------------------

    """
    Add the specified number of subentry for the given entry. Verify that total
    number of subentry (except 0) doesn't exceed nbmax defined
    """
    def AddSubentriesToCurrent(self, index, number, node = None):
        disable_buffer = node != None
        if node == None:
            node = self.CurrentNode
        # Informations about entry
        length = node.GetEntry(index, 0)
        infos = node.GetEntryInfos(index)
        subentry_infos = node.GetSubentryInfos(index, 1)
        # Get default value for subindex
        if "default" in subentry_infos:
            default = subentry_infos["default"]
        else:
            default = self.GetTypeDefaultValue(subentry_infos["type"])   
        # First case entry is record
        if infos["struct"] & OD_IdenticalSubindexes: 
            for i in xrange(1, min(number,subentry_infos["nbmax"]-length) + 1):
                node.AddEntry(index, length + i, default)
            if not disable_buffer:
                self.BufferCurrentNode()
            return None
        # Second case entry is array, only possible for manufacturer specific
        elif infos["struct"] & OD_MultipleSubindexes and 0x2000 <= index <= 0x5FFF:
            values = {"name" : "Undefined", "type" : 5, "access" : "rw", "pdo" : True}
            for i in xrange(1, min(number,0xFE-length) + 1):
                node.AddMappingEntry(index, length + i, values = values.copy())
                node.AddEntry(index, length + i, 0)
            if not disable_buffer:
                self.BufferCurrentNode()
            return None
            

    """
    Remove the specified number of subentry for the given entry. Verify that total
    number of subentry (except 0) isn't less than 1
    """
    def RemoveSubentriesFromCurrent(self, index, number):
        # Informations about entry
        infos = self.GetEntryInfos(index)
        length = self.CurrentNode.GetEntry(index, 0)
        if "nbmin" in infos:
            nbmin = infos["nbmin"]
        else:
            nbmin = 1
        # Entry is a record, or is an array of manufacturer specific
        if infos["struct"] & OD_IdenticalSubindexes or 0x2000 <= index <= 0x5FFF and infos["struct"] & OD_IdenticalSubindexes:
            for i in xrange(min(number, length - nbmin)):
                self.RemoveCurrentVariable(index, length - i)
            self.BufferCurrentNode()

    """
    Add a SDO Server to current node
    """
    def AddSDOServerToCurrent(self):
        # An SDO Server is already defined at index 0x1200
        if self.CurrentNode.IsEntry(0x1200):
            indexlist = [self.GetLineFromIndex(0x1201)]
            if None not in indexlist:
                self.ManageEntriesOfCurrent(indexlist, [])
        # Add an SDO Server at index 0x1200
        else:
            self.ManageEntriesOfCurrent([0x1200], [])
        
    """
    Add a SDO Server to current node
    """
    def AddSDOClientToCurrent(self):
        indexlist = [self.GetLineFromIndex(0x1280)]
        if None not in indexlist:
            self.ManageEntriesOfCurrent(indexlist, [])

    """
    Add a Transmit PDO to current node
    """
    def AddPDOTransmitToCurrent(self):
        indexlist = [self.GetLineFromIndex(0x1800),self.GetLineFromIndex(0x1A00)]
        if None not in indexlist:
            self.ManageEntriesOfCurrent(indexlist, [])
        
    """
    Add a Receive PDO to current node
    """
    def AddPDOReceiveToCurrent(self):
        indexlist = [self.GetLineFromIndex(0x1400),self.GetLineFromIndex(0x1600)]
        if None not in indexlist:
            self.ManageEntriesOfCurrent(indexlist, [])

    """
    Add a list of entries defined in profile for menu item selected to current node
    """
    def AddSpecificEntryToCurrent(self, menuitem):
        indexlist = []
        for menu, indexes in self.CurrentNode.GetSpecificMenu():
            if menuitem == menu:
                for index in indexes:
                    indexlist.append(self.GetLineFromIndex(index))
        if None not in indexlist:
            self.ManageEntriesOfCurrent(indexlist, [])

    """
    Search the first index available for a pluri entry from base_index
    """
    def GetLineFromIndex(self, base_index):
        found = False
        index = base_index
        infos = self.GetEntryInfos(base_index)
        while index < base_index + infos["incr"]*infos["nbmax"] and not found:
            if not self.CurrentNode.IsEntry(index):
                found = True
            else:
                index += infos["incr"]
        if found:
            return index
        return None
    
    """
    Add entries specified in addinglist and remove entries specified in removinglist
    """
    def ManageEntriesOfCurrent(self, addinglist, removinglist, node = None):
        disable_buffer = node != None
        if node == None:
            node = self.CurrentNode
        # Add all the entries in addinglist
        for index in addinglist:
            infos = self.GetEntryInfos(index)
            if infos["struct"] & OD_MultipleSubindexes:
                # First case entry is a record
                if infos["struct"] & OD_IdenticalSubindexes:
                    subentry_infos = self.GetSubentryInfos(index, 1)
                    if "default" in subentry_infos:
                        default = subentry_infos["default"]
                    else:
                        default = self.GetTypeDefaultValue(subentry_infos["type"])
                    node.AddEntry(index, value = [])
                    if "nbmin" in subentry_infos:
                        for i in xrange(subentry_infos["nbmin"]):
                            node.AddEntry(index, i + 1, default)
                    else:
                        node.AddEntry(index, 1, default)
                # Second case entry is a record
                else:
                    i = 1
                    subentry_infos = self.GetSubentryInfos(index, i)
                    while subentry_infos:
                        if "default" in subentry_infos:
                            default = subentry_infos["default"]
                        else:
                            default = self.GetTypeDefaultValue(subentry_infos["type"])
                        node.AddEntry(index, i, default)
                        i += 1
                        subentry_infos = self.GetSubentryInfos(index, i)
            # Third case entry is a record
            else:
                subentry_infos = self.GetSubentryInfos(index, 0)
                if "default" in subentry_infos:
                    default = subentry_infos["default"]
                else:
                    default = self.GetTypeDefaultValue(subentry_infos["type"])
                node.AddEntry(index, 0, default)
        # Remove all the entries in removinglist
        for index in removinglist:
            self.RemoveCurrentVariable(index)
        if not disable_buffer:
            self.BufferCurrentNode()
        return None


    """
    Reset an subentry from current node to its default value
    """
    def SetCurrentEntryToDefault(self, index, subindex, node = None):
        disable_buffer = node != None
        if node == None:
            node = self.CurrentNode
        if node.IsEntry(index, subindex):
            subentry_infos = self.GetSubentryInfos(index, subindex)
            if "default" in subentry_infos:
                default = subentry_infos["default"]
            else:
                default = self.GetTypeDefaultValue(subentry_infos["type"])
            node.SetEntry(index, subindex, default)
            if not disable_buffer:
                self.BufferCurrentNode()

    """
    Remove an entry from current node. Analize the index to perform the correct
    method
    """
    def RemoveCurrentVariable(self, index, subIndex = None):
        Mappings = self.CurrentNode.GetMappings()
        if index < 0x1000 and subIndex == None:
            type = self.CurrentNode.GetEntry(index, 1)
            for i in Mappings[-1]:
                for value in Mappings[-1][i]["values"]:
                    if value["type"] == index:
                        value["type"] = type
            self.CurrentNode.RemoveMappingEntry(index)
            self.CurrentNode.RemoveEntry(index)
        elif index == 0x1200 and subIndex == None:
            self.CurrentNode.RemoveEntry(0x1200)
        elif 0x1201 <= index <= 0x127F and subIndex == None:
            self.CurrentNode.RemoveLine(index, 0x127F)
        elif 0x1280 <= index <= 0x12FF and subIndex == None:
            self.CurrentNode.RemoveLine(index, 0x12FF)
        elif 0x1400 <= index <= 0x15FF or 0x1600 <= index <= 0x17FF and subIndex == None:
            if 0x1600 <= index <= 0x17FF and subIndex == None:
                index -= 0x200
            self.CurrentNode.RemoveLine(index, 0x15FF)
            self.CurrentNode.RemoveLine(index + 0x200, 0x17FF)
        elif 0x1800 <= index <= 0x19FF or 0x1A00 <= index <= 0x1BFF and subIndex == None:
            if 0x1A00 <= index <= 0x1BFF:
                index -= 0x200
            self.CurrentNode.RemoveLine(index, 0x19FF)
            self.CurrentNode.RemoveLine(index + 0x200, 0x1BFF)
        else:
            found = False
            for menu,list in self.CurrentNode.GetSpecificMenu():
                for i in list:
                    iinfos = self.GetEntryInfos(i)
                    indexes = [i + incr * iinfos["incr"] for incr in xrange(iinfos["nbmax"])] 
                    if index in indexes:
                        found = True
                        diff = index - i
                        for j in list:
                            jinfos = self.GetEntryInfos(j)
                            self.CurrentNode.RemoveLine(j + diff, j + jinfos["incr"]*jinfos["nbmax"], jinfos["incr"])
            self.CurrentNode.RemoveMapVariable(index, subIndex)
            if not found:
                infos = self.GetEntryInfos(index)
                if not infos["need"]:
                    self.CurrentNode.RemoveEntry(index, subIndex)
            if index in Mappings[-1]:
                self.CurrentNode.RemoveMappingEntry(index, subIndex)

    def AddMapVariableToCurrent(self, index, name, struct, number, node = None):
        if 0x2000 <= index <= 0x5FFF:
            disable_buffer = node != None
            if node == None:
                node = self.CurrentNode
            if not node.IsEntry(index):
                node.AddMappingEntry(index, name = name, struct = struct)
                if struct == var:
                    values = {"name" : name, "type" : 0x05, "access" : "rw", "pdo" : True}
                    node.AddMappingEntry(index, 0, values = values)
                    node.AddEntry(index, 0, 0)
                else:
                    values = {"name" : "Number of Entries", "type" : 0x05, "access" : "ro", "pdo" : False}
                    node.AddMappingEntry(index, 0, values = values)
                    if struct == rec:
                        values = {"name" : name + " %d[(sub)]", "type" : 0x05, "access" : "rw", "pdo" : True, "nbmax" : 0xFE}
                        node.AddMappingEntry(index, 1, values = values)
                        for i in xrange(number):
                            node.AddEntry(index, i + 1, 0)
                    else:
                        for i in xrange(number):
                            values = {"name" : "Undefined", "type" : 0x05, "access" : "rw", "pdo" : True}
                            node.AddMappingEntry(index, i + 1, values = values)
                            node.AddEntry(index, i + 1, 0)
                if not disable_buffer:
                    self.BufferCurrentNode()
                return None
            else:
                return _("Index 0x%04X already defined!")%index
        else:
            return _("Index 0x%04X isn't a valid index for Map Variable!")%index

    def AddUserTypeToCurrent(self, type, min, max, length):
        index = 0xA0
        while index < 0x100 and self.CurrentNode.IsEntry(index):
            index += 1
        if index < 0x100:
            customisabletypes = self.GetCustomisableTypes()
            name, valuetype = customisabletypes[type]
            size = self.GetEntryInfos(type)["size"]
            default = self.GetTypeDefaultValue(type)
            if valuetype == 0:
                self.CurrentNode.AddMappingEntry(index, name = "%s[%d-%d]"%(name, min, max), struct = 3, size = size, default = default)
                self.CurrentNode.AddMappingEntry(index, 0, values = {"name" : "Number of Entries", "type" : 0x05, "access" : "ro", "pdo" : False})
                self.CurrentNode.AddMappingEntry(index, 1, values = {"name" : "Type", "type" : 0x05, "access" : "ro", "pdo" : False})
                self.CurrentNode.AddMappingEntry(index, 2, values = {"name" : "Minimum Value", "type" : type, "access" : "ro", "pdo" : False})
                self.CurrentNode.AddMappingEntry(index, 3, values = {"name" : "Maximum Value", "type" : type, "access" : "ro", "pdo" : False})
                self.CurrentNode.AddEntry(index, 1, type)
                self.CurrentNode.AddEntry(index, 2, min)
                self.CurrentNode.AddEntry(index, 3, max)
            elif valuetype == 1:
                self.CurrentNode.AddMappingEntry(index, name = "%s%d"%(name, length), struct = 3, size = length * size, default = default)
                self.CurrentNode.AddMappingEntry(index, 0, values = {"name" : "Number of Entries", "type" : 0x05, "access" : "ro", "pdo" : False})
                self.CurrentNode.AddMappingEntry(index, 1, values = {"name" : "Type", "type" : 0x05, "access" : "ro", "pdo" : False})
                self.CurrentNode.AddMappingEntry(index, 2, values = {"name" : "Length", "type" : 0x05, "access" : "ro", "pdo" : False})
                self.CurrentNode.AddEntry(index, 1, type)
                self.CurrentNode.AddEntry(index, 2, length)
            self.BufferCurrentNode()
            return None
        else:
            return _("Too many User Types have already been defined!")

#-------------------------------------------------------------------------------
#                      Modify Entry and Mapping Functions
#-------------------------------------------------------------------------------

    def SetCurrentEntryCallbacks(self, index, value):
        if self.CurrentNode and self.CurrentNode.IsEntry(index):
            entry_infos = self.GetEntryInfos(index)
            if "callback" not in entry_infos:
                self.CurrentNode.SetParamsEntry(index, None, callback = value)
                self.BufferCurrentNode()

    def SetCurrentEntry(self, index, subIndex, value, name, editor, node = None):
        disable_buffer = node != None
        if node == None:
            node = self.CurrentNode
        if node and node.IsEntry(index):
            if name == "value":
                if editor == "map":
                    value = node.GetMapValue(value)
                    if value is not None:
                        node.SetEntry(index, subIndex, value)
                elif editor == "bool":
                    value = value == "True"
                    node.SetEntry(index, subIndex, value)
                elif editor == "time":
                    node.SetEntry(index, subIndex, value)
                elif editor == "number":
                    try:
                        node.SetEntry(index, subIndex, int(value))
                    except:
                        pass
                elif editor == "float":
                    try:
                        node.SetEntry(index, subIndex, float(value))
                    except:
                        pass
                elif editor == "domain":
                    try:
                        if len(value) % 2 != 0:
                            value = "0" + value
                        value = value.decode('hex_codec')
                        node.SetEntry(index, subIndex, value)
                    except:
                        pass
                elif editor == "dcf":
                    node.SetEntry(index, subIndex, value)
                else:
                    subentry_infos = self.GetSubentryInfos(index, subIndex)
                    type = subentry_infos["type"]
                    dic = {}
                    for typeindex, typevalue in CustomisableTypes:
                        dic[typeindex] = typevalue
                    if type not in dic:
                        type = node.GetEntry(type)[1]
                    if dic[type] == 0:
                        try:
                            if value.startswith("$NODEID"):
                                value = "\"%s\""%value
                            elif value.startswith("0x"):
                                value = int(value, 16)
                            else:
                                value = int(value)
                            node.SetEntry(index, subIndex, value)
                        except:
                            pass
                    else:
                        node.SetEntry(index, subIndex, value)
            elif name in ["comment", "save"]:
                if editor == "option":
                    value = value == "Yes"
                if name == "save":
                    node.SetParamsEntry(index, subIndex, save = value)
                elif name == "comment":
                    node.SetParamsEntry(index, subIndex, comment = value)
            else:
                if editor == "type":
                    value = self.GetTypeIndex(value)
                    size = self.GetEntryInfos(value)["size"]
                    node.UpdateMapVariable(index, subIndex, size)
                elif editor in ["access","raccess"]:
                    dic = {}
                    for abbrev,access in AccessType.iteritems():
                        dic[access] = abbrev
                    value = dic[value]
                    if editor == "raccess" and not node.IsMappingEntry(index):
                        entry_infos = self.GetEntryInfos(index)
                        subIndex0_infos = self.GetSubentryInfos(index, 0, False).copy()
                        subIndex1_infos = self.GetSubentryInfos(index, 1, False).copy()
                        node.AddMappingEntry(index, name = entry_infos["name"], struct = 7)
                        node.AddMappingEntry(index, 0, values = subIndex0_infos)
                        node.AddMappingEntry(index, 1, values = subIndex1_infos)
                node.SetMappingEntry(index, subIndex, values = {name : value})
            if not disable_buffer:
                self.BufferCurrentNode()
            return None

    def SetCurrentEntryName(self, index, name):
        self.CurrentNode.SetMappingEntry(index, name=name)
        self.BufferCurrentNode()

    def SetCurrentUserType(self, index, type, min, max, length):
        customisabletypes = self.GetCustomisableTypes()
        values, valuetype = self.GetCustomisedTypeValues(index)
        name, new_valuetype = customisabletypes[type]
        size = self.GetEntryInfos(type)["size"]
        default = self.GetTypeDefaultValue(type)
        if new_valuetype == 0:
            self.CurrentNode.SetMappingEntry(index, name = "%s[%d-%d]"%(name, min, max), struct = 3, size = size, default = default) 
            if valuetype == 1:
                self.CurrentNode.SetMappingEntry(index, 2, values = {"name" : "Minimum Value", "type" : type, "access" : "ro", "pdo" : False})
                self.CurrentNode.AddMappingEntry(index, 3, values = {"name" : "Maximum Value", "type" : type, "access" : "ro", "pdo" : False})
            self.CurrentNode.SetEntry(index, 1, type)
            self.CurrentNode.SetEntry(index, 2, min)
            if valuetype == 1:
                self.CurrentNode.AddEntry(index, 3, max)
            else:
                self.CurrentNode.SetEntry(index, 3, max)
        elif new_valuetype == 1:
            self.CurrentNode.SetMappingEntry(index, name = "%s%d"%(name, length), struct = 3, size = size, default = default)
            if valuetype == 0:
                self.CurrentNode.SetMappingEntry(index, 2, values = {"name" : "Length", "type" : 0x02, "access" : "ro", "pdo" : False})
                self.CurrentNode.RemoveMappingEntry(index, 3)
            self.CurrentNode.SetEntry(index, 1, type)
            self.CurrentNode.SetEntry(index, 2, length)
            if valuetype == 0:
                self.CurrentNode.RemoveEntry(index, 3)
        self.BufferCurrentNode()

#-------------------------------------------------------------------------------
#                      Current Buffering Management Functions
#-------------------------------------------------------------------------------

    def BufferCurrentNode(self):
        self.UndoBuffers[self.NodeIndex].Buffering(self.CurrentNode.Copy())

    def CurrentIsSaved(self):
        return self.UndoBuffers[self.NodeIndex].IsCurrentSaved()

    def OneFileHasChanged(self):
        result = False
        for buffer in self.UndoBuffers.values():
            result |= not buffer.IsCurrentSaved()
        return result

    def GetBufferNumber(self):
        return len(self.UndoBuffers)

    def GetBufferIndexes(self):
        return self.UndoBuffers.keys()

    def LoadCurrentPrevious(self):
        self.CurrentNode = self.UndoBuffers[self.NodeIndex].Previous().Copy()
    
    def LoadCurrentNext(self):
        self.CurrentNode = self.UndoBuffers[self.NodeIndex].Next().Copy()

    def AddNodeBuffer(self, currentstate = None, issaved = False):
        self.NodeIndex = GetNewId()
        self.UndoBuffers[self.NodeIndex] = UndoBuffer(currentstate, issaved)
        self.FilePaths[self.NodeIndex] = ""
        self.FileNames[self.NodeIndex] = ""
        return self.NodeIndex

    def ChangeCurrentNode(self, index):
        if index in self.UndoBuffers.keys():
            self.NodeIndex = index
            self.CurrentNode = self.UndoBuffers[self.NodeIndex].Current().Copy()
    
    def RemoveNodeBuffer(self, index):
        self.UndoBuffers.pop(index)
        self.FilePaths.pop(index)
        self.FileNames.pop(index)
    
    def GetCurrentNodeIndex(self):
        return self.NodeIndex
    
    def GetCurrentFilename(self):
        return self.GetFilename(self.NodeIndex)
    
    def GetAllFilenames(self):
        indexes = self.UndoBuffers.keys()
        indexes.sort()
        return [self.GetFilename(idx) for idx in indexes]
    
    def GetFilename(self, index):
        if self.UndoBuffers[index].IsCurrentSaved():
            return self.FileNames[index]
        else:
            return "~%s~"%self.FileNames[index]
    
    def SetCurrentFilePath(self, filepath):
        self.FilePaths[self.NodeIndex] = filepath
        if filepath == "":
            self.LastNewIndex += 1
            self.FileNames[self.NodeIndex] = _("Unnamed%d")%self.LastNewIndex
        else:
            self.FileNames[self.NodeIndex] = os.path.splitext(os.path.basename(filepath))[0]
                
    def GetCurrentFilePath(self):
        if len(self.FilePaths) > 0:
            return self.FilePaths[self.NodeIndex]
        else:
            return ""
    
    def GetCurrentBufferState(self):
        first = self.UndoBuffers[self.NodeIndex].IsFirst()
        last = self.UndoBuffers[self.NodeIndex].IsLast()
        return not first, not last

#-------------------------------------------------------------------------------
#                         Profiles Management Functions
#-------------------------------------------------------------------------------

    def GetCurrentCommunicationLists(self):
        list = []
        for index in MappingDictionary.iterkeys():
            if 0x1000 <= index < 0x1200:
                list.append(index)
        return self.GetProfileLists(MappingDictionary, list)
    
    def GetCurrentDS302Lists(self):
        return self.GetSpecificProfileLists(self.CurrentNode.GetDS302Profile())
    
    def GetCurrentProfileLists(self):
        return self.GetSpecificProfileLists(self.CurrentNode.GetProfile())
    
    def GetSpecificProfileLists(self, mappingdictionary):
        validlist = []
        exclusionlist = []
        for name, list in self.CurrentNode.GetSpecificMenu():
            exclusionlist.extend(list)
        for index in mappingdictionary.iterkeys():
            if index not in exclusionlist:
                validlist.append(index)
        return self.GetProfileLists(mappingdictionary, validlist)
    
    def GetProfileLists(self, mappingdictionary, list):
        dictionary = {}
        current = []
        for index in list:
            dictionary[index] = (mappingdictionary[index]["name"], mappingdictionary[index]["need"])
            if self.CurrentNode.IsEntry(index):
                current.append(index)
        return dictionary, current

    def GetCurrentNextMapIndex(self):
        if self.CurrentNode:
            index = 0x2000
            while self.CurrentNode.IsEntry(index) and index < 0x5FFF:
                index += 1
            if index < 0x6000:
                return index
            else:
                return None

    def CurrentDS302Defined(self):
        if self.CurrentNode:
            return len(self.CurrentNode.GetDS302Profile()) > 0
        return False

#-------------------------------------------------------------------------------
#                         Node State and Values Functions
#-------------------------------------------------------------------------------
    
    def GetCurrentNodeName(self):
        if self.CurrentNode:
            return self.CurrentNode.GetNodeName()
        else:
            return ""

    def GetCurrentNodeCopy(self):
        if self.CurrentNode:
            return self.CurrentNode.Copy()
        else:
            return None
    
    def GetCurrentNodeID(self, node = None):
        if self.CurrentNode:
            return self.CurrentNode.GetNodeID()
        else:
            return None

    def GetCurrentNodeInfos(self):
        name = self.CurrentNode.GetNodeName()
        id = self.CurrentNode.GetNodeID()
        type = self.CurrentNode.GetNodeType()
        description = self.CurrentNode.GetNodeDescription()
        return name, id, type, description
    
    def SetCurrentNodeInfos(self, name, id, type, description):
        self.CurrentNode.SetNodeName(name)
        self.CurrentNode.SetNodeID(id)
        self.CurrentNode.SetNodeType(type)
        self.CurrentNode.SetNodeDescription(description)
        self.BufferCurrentNode()

    def GetCurrentNodeDefaultStringSize(self):
        if self.CurrentNode:
            return self.CurrentNode.GetDefaultStringSize()
        else:
            return Node.DefaultStringSize
    
    def SetCurrentNodeDefaultStringSize(self, size):
        if self.CurrentNode:
            self.CurrentNode.SetDefaultStringSize(size)
        else:
            Node.DefaultStringSize = size

    def GetCurrentProfileName(self):
        if self.CurrentNode:
            return self.CurrentNode.GetProfileName()
        return ""

    def IsCurrentEntry(self, index):
        if self.CurrentNode:
            return self.CurrentNode.IsEntry(index)
        return False
    
    def GetCurrentEntry(self, index, subIndex = None, compute = True):
        if self.CurrentNode:
            return self.CurrentNode.GetEntry(index, subIndex, compute)
        return None
    
    def GetCurrentParamsEntry(self, index, subIndex = None):
        if self.CurrentNode:
            return self.CurrentNode.GetParamsEntry(index, subIndex)
        return None
    
    def GetCurrentValidIndexes(self, min, max):
        validindexes = []
        for index in self.CurrentNode.GetIndexes():
            if min <= index <= max:
                validindexes.append((self.GetEntryName(index), index))
        return validindexes
        
    def GetCurrentValidChoices(self, min, max):
        validchoices = []
        exclusionlist = []
        for menu, indexes in self.CurrentNode.GetSpecificMenu():
            exclusionlist.extend(indexes)
            good = True
            for index in indexes:
                good &= min <= index <= max
            if good:
                validchoices.append((menu, None))
        list = [index for index in MappingDictionary.keys() if index >= 0x1000]
        profiles = self.CurrentNode.GetMappings(False)
        for profile in profiles:
            list.extend(profile.keys())
        list.sort()
        for index in list:
            if min <= index <= max and not self.CurrentNode.IsEntry(index) and index not in exclusionlist:
                validchoices.append((self.GetEntryName(index), index))
        return validchoices
    
    def HasCurrentEntryCallbacks(self, index):
        if self.CurrentNode:
            return self.CurrentNode.HasEntryCallbacks(index)
        return False
    
    def GetCurrentEntryValues(self, index):
        if self.CurrentNode:
            return self.GetNodeEntryValues(self.CurrentNode, index)
    
    def GetNodeEntryValues(self, node, index):
        if node and node.IsEntry(index):
            entry_infos = node.GetEntryInfos(index)
            data = []
            editors = []
            values = node.GetEntry(index, compute = False)
            params = node.GetParamsEntry(index)
            if isinstance(values, ListType):
                for i, value in enumerate(values):
                    data.append({"value" : value})
                    data[-1].update(params[i])      
            else:
                data.append({"value" : values})
                data[-1].update(params)
            for i, dic in enumerate(data):
                infos = node.GetSubentryInfos(index, i)
                dic["subindex"] = "0x%02X"%i
                dic["name"] = infos["name"]
                dic["type"] = node.GetTypeName(infos["type"])
                if dic["type"] is None:
                    dic["type"] = "Unknown"
                dic["access"] = AccessType[infos["access"]]
                dic["save"] = OptionType[dic["save"]]
                editor = {"subindex" : None, "name" : None, 
                          "type" : None, "value" : None,
                          "access" : None, "save" : "option", 
                          "callback" : "option", "comment" : "string"}
                if isinstance(values, ListType) and i == 0:
                    if 0x1600 <= index <= 0x17FF or 0x1A00 <= index <= 0x1C00:
                        editor["access"] = "raccess"
                else:
                    if infos["user_defined"]:
                        if entry_infos["struct"] & OD_IdenticalSubindexes:
                            if i == 0:
                                editor["type"] = "type"
                                editor["access"] = "access"
                        else:
                            if entry_infos["struct"] & OD_MultipleSubindexes:
                                editor["name"] = "string"
                            editor["type"] = "type"
                            editor["access"] = "access"
                    if index < 0x260:
                        if i == 1:
                            dic["value"] = node.GetTypeName(dic["value"])
                    elif 0x1600 <= index <= 0x17FF or 0x1A00 <= index <= 0x1C00:
                        editor["value"] = "map"
                        dic["value"] = node.GetMapName(dic["value"])
                    else:
                        if dic["type"].startswith("VISIBLE_STRING") or dic["type"].startswith("OCTET_STRING"):
                            editor["value"] = "string"
                        elif dic["type"] in ["TIME_OF_DAY","TIME_DIFFERENCE"]:
                            editor["value"] = "time"
                        elif dic["type"] == "DOMAIN":
                            if index == 0x1F22:
                                editor["value"] = "dcf"
                            else:
                                editor["value"] = "domain"
                            dic["value"] = dic["value"].encode('hex_codec')
                        elif dic["type"] == "BOOLEAN":
                            editor["value"] = "bool"
                            dic["value"] = BoolType[dic["value"]]
                        result = type_model.match(dic["type"])
                        if result:
                            values = result.groups()
                            if values[0] == "UNSIGNED":
                                try:
                                    format = "0x%0" + str(int(values[1])/4) + "X"
                                    dic["value"] = format%dic["value"]
                                except:
                                    pass
                                editor["value"] = "string"
                            if values[0] == "INTEGER":
                                editor["value"] = "number"
                            elif values[0] == "REAL":
                                editor["value"] = "float"
                            elif values[0] in ["VISIBLE_STRING", "OCTET_STRING"]:
                                editor["length"] = values[0]
                        result = range_model.match(dic["type"])
                        if result:
                            values = result.groups()
                            if values[0] in ["UNSIGNED", "INTEGER", "REAL"]:
                                editor["min"] = values[2]
                                editor["max"] = values[3]
                editors.append(editor)
            return data, editors
        else:
            return None

    def AddToDCF(self, node_id, index, subindex, size, value):
        if self.CurrentNode.IsEntry(0x1F22, node_id):
            dcf_value = self.CurrentNode.GetEntry(0x1F22, node_id)
            if dcf_value != "":
                nbparams = BE_to_LE(dcf_value[:4])
            else:
                nbparams = 0
            new_value = LE_to_BE(nbparams + 1, 4) + dcf_value[4:]
            new_value += LE_to_BE(index, 2) + LE_to_BE(subindex, 1) + LE_to_BE(size, 4) + LE_to_BE(value, size)
            self.CurrentNode.SetEntry(0x1F22, node_id, new_value)

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

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

    def GetEntryName(self, index, compute=True):
        if self.CurrentNode:
            return self.CurrentNode.GetEntryName(index, compute)
        else:
            return FindEntryName(index, MappingDictionary, compute)
    
    def GetEntryInfos(self, index, compute=True):
        if self.CurrentNode:
            return self.CurrentNode.GetEntryInfos(index, compute)
        else:
            return FindEntryInfos(index, MappingDictionary, compute)
    
    def GetSubentryInfos(self, index, subindex, compute=True):
        if self.CurrentNode:
            return self.CurrentNode.GetSubentryInfos(index, subindex, compute)
        else:
            result = FindSubentryInfos(index, subindex, MappingDictionary, compute)
            if result:
                result["user_defined"] = False
            return result
    
    def GetTypeIndex(self, typename):
        if self.CurrentNode:
            return self.CurrentNode.GetTypeIndex(typename)
        else:
            return FindTypeIndex(typename, MappingDictionary)
    
    def GetTypeName(self, typeindex):
        if self.CurrentNode:
            return self.CurrentNode.GetTypeName(typeindex)
        else:
            return FindTypeName(typeindex, MappingDictionary)
    
    def GetTypeDefaultValue(self, typeindex):
        if self.CurrentNode:
            return self.CurrentNode.GetTypeDefaultValue(typeindex)
        else:
            return FindTypeDefaultValue(typeindex, MappingDictionary)
    
    def GetMapVariableList(self, compute=True):
        if self.CurrentNode:
            return self.CurrentNode.GetMapVariableList(compute)
        else:
            return []

    def GetMandatoryIndexes(self):
        if self.CurrentNode:
            return self.CurrentNode.GetMandatoryIndexes()
        else:
            return FindMandatoryIndexes(MappingDictionary)
    
    def GetCustomisableTypes(self):
        dic = {}
        for index, valuetype in CustomisableTypes:
            name = self.GetTypeName(index)
            dic[index] = [name, valuetype]
        return dic
    
    def GetCurrentSpecificMenu(self):
        if self.CurrentNode:
            return self.CurrentNode.GetSpecificMenu()
        return []