PLCControler.py
author Laurent Bessard
Thu, 29 Aug 2013 00:28:39 +0200
changeset 1293 40117d02601b
parent 1291 42ea51d083ce
child 1294 f02ba5b83811
permissions -rw-r--r--
Fixed diagram editing in xmlclass refactoring
#!/usr/bin/env python
# -*- coding: utf-8 -*-

#This file is part of PLCOpenEditor, a library implementing an IEC 61131-3 editor
#based on the plcopen standard. 
#
#Copyright (C) 2007: Edouard TISSERANT 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 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
#General Public License for more details.
#
#You should have received a copy of the GNU 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 xml.dom import minidom
from types import StringType, UnicodeType, TupleType
import cPickle
import os,sys,re
import datetime
from time import localtime

from plcopen import PLCOpenParser, LoadProject, SaveProject
from plcopen.structures import *
from graphics.GraphicCommons import *
from PLCGenerator import *

duration_model = re.compile("(?:([0-9]{1,2})h)?(?:([0-9]{1,2})m(?!s))?(?:([0-9]{1,2})s)?(?:([0-9]{1,3}(?:\.[0-9]*)?)ms)?")

ITEMS_EDITABLE = [ITEM_PROJECT,
                  ITEM_POU,
                  ITEM_VARIABLE,
                  ITEM_TRANSITION,
                  ITEM_ACTION,
                  ITEM_CONFIGURATION,
                  ITEM_RESOURCE,
                  ITEM_DATATYPE
                 ] = range(8)

ITEMS_UNEDITABLE = [ITEM_DATATYPES,
                    ITEM_FUNCTION,
                    ITEM_FUNCTIONBLOCK,
                    ITEM_PROGRAM,
                    ITEM_TRANSITIONS,
                    ITEM_ACTIONS,
                    ITEM_CONFIGURATIONS,
                    ITEM_RESOURCES,
                    ITEM_PROPERTIES
                   ] = range(8, 17)
 
ITEMS_VARIABLE = [ITEM_VAR_LOCAL,
                  ITEM_VAR_GLOBAL,
                  ITEM_VAR_EXTERNAL,
                  ITEM_VAR_TEMP,
                  ITEM_VAR_INPUT,
                  ITEM_VAR_OUTPUT,
                  ITEM_VAR_INOUT
                 ] = range(17, 24)

VAR_CLASS_INFOS = {
    "Local":    ("localVars",    ITEM_VAR_LOCAL),
    "Global":   ("globalVars",   ITEM_VAR_GLOBAL),
    "External": ("externalVars", ITEM_VAR_EXTERNAL),
    "Temp":     ("tempVars",     ITEM_VAR_TEMP),
    "Input":    ("inputVars",    ITEM_VAR_INPUT),
    "Output":   ("outputVars",   ITEM_VAR_OUTPUT),
    "InOut":    ("inOutVars",    ITEM_VAR_INOUT)}

POU_TYPES = {"program": ITEM_PROGRAM,
             "functionBlock": ITEM_FUNCTIONBLOCK,
             "function": ITEM_FUNCTION,
            }

LOCATIONS_ITEMS = [LOCATION_CONFNODE,
                   LOCATION_MODULE,
                   LOCATION_GROUP,
                   LOCATION_VAR_INPUT,
                   LOCATION_VAR_OUTPUT,
                   LOCATION_VAR_MEMORY] = range(6)

ScriptDirectory = os.path.split(os.path.realpath(__file__))[0]

def GetUneditableNames():
    _ = lambda x:x
    return [_("User-defined POUs"), _("Functions"), _("Function Blocks"), 
            _("Programs"), _("Data Types"), _("Transitions"), _("Actions"), 
            _("Configurations"), _("Resources"), _("Properties")]
UNEDITABLE_NAMES = GetUneditableNames()
[USER_DEFINED_POUS, FUNCTIONS, FUNCTION_BLOCKS, PROGRAMS, 
 DATA_TYPES, TRANSITIONS, ACTIONS, CONFIGURATIONS, 
 RESOURCES, PROPERTIES] = UNEDITABLE_NAMES

#-------------------------------------------------------------------------------
#                         Undo Buffer for PLCOpenEditor
#-------------------------------------------------------------------------------

# Length of the buffer
UNDO_BUFFER_LENGTH = 20

"""
Class implementing a buffer of changes made on the current editing model
"""
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(UNDO_BUFFER_LENGTH):
            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) % UNDO_BUFFER_LENGTH
        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) % UNDO_BUFFER_LENGTH
        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) % UNDO_BUFFER_LENGTH
            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) % UNDO_BUFFER_LENGTH
            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


#-------------------------------------------------------------------------------
#                           Controler for PLCOpenEditor
#-------------------------------------------------------------------------------

"""
Class which controls the operations made on the plcopen model and answers to view requests
"""
class PLCControler:
    
    # Create a new PLCControler
    def __init__(self):
        self.LastNewIndex = 0
        self.Reset()
    
    # Reset PLCControler internal variables
    def Reset(self):
        self.Project = None
        self.ProjectBufferEnabled = True
        self.ProjectBuffer = None
        self.ProjectSaved = True
        self.Buffering = False
        self.FilePath = ""
        self.FileName = ""
        self.ProgramChunks = []
        self.ProgramOffset = 0
        self.NextCompiledProject = None
        self.CurrentCompiledProject = None
        self.ConfNodeTypes = []
        self.TotalTypesDict = StdBlckDct.copy()
        self.TotalTypes = StdBlckLst[:]
        self.ProgramFilePath = ""
            
    def GetQualifierTypes(self):
        return plcopen.QualifierList

    def GetProject(self, debug = False):
        if debug and self.CurrentCompiledProject is not None:
            return self.CurrentCompiledProject
        else:
            return self.Project

#-------------------------------------------------------------------------------
#                         Project management functions
#-------------------------------------------------------------------------------

    # Return if a project is opened
    def HasOpenedProject(self):
        return self.Project is not None

    # Create a new project by replacing the current one
    def CreateNewProject(self, properties):
        # Create the project
        self.Project = PLCOpenParser.CreateRoot()
        properties["creationDateTime"] = datetime.datetime(*localtime()[:6])
        self.Project.setfileHeader(properties)
        self.Project.setcontentHeader(properties)
        self.SetFilePath("")
        
        ## To remove when project buffering ready
        self.ProjectBufferEnabled = False
        
        # Initialize the project buffer
        self.CreateProjectBuffer(False)
        self.ProgramChunks = []
        self.ProgramOffset = 0
        self.NextCompiledProject = self.Project #self.Copy(self.Project)
        self.CurrentCompiledProject = None
        self.Buffering = False
    
    # Return project data type names
    def GetProjectDataTypeNames(self, debug = False):
        project = self.GetProject(debug)
        if project is not None:
            return [datatype.getname() for datatype in project.getdataTypes()]
        return []
    
    # Return project pou names
    def GetProjectPouNames(self, debug = False):
        project = self.GetProject(debug)
        if project is not None:
            return [pou.getname() for pou in project.getpous()]
        return []
    
    # Return project pou names
    def GetProjectConfigNames(self, debug = False):
        project = self.GetProject(debug)
        if project is not None:
            return [config.getname() for config in project.getconfigurations()]
        return []
    
    # Return project pou variable names
    def GetProjectPouVariableNames(self, pou_name = None, debug = False):
        variables = []
        project = self.GetProject(debug)
        if project is not None:
            for pou in project.getpous():
                if pou_name is None or pou_name == pou.getname():
                    variables.extend([var["Name"] for var in self.GetPouInterfaceVars(pou, debug)])
                    for transition in pou.gettransitionList():
                        variables.append(transition.getname())
                    for action in pou.getactionList():
                        variables.append(action.getname())
        return variables
    
    # Return file path if project is an open file
    def GetFilePath(self):
        return self.FilePath
    
    # Return file path if project is an open file
    def GetProgramFilePath(self):
        return self.ProgramFilePath
    
    # Return file name and point out if file is up to date
    def GetFilename(self):
        if self.Project is not None:
            if self.ProjectIsSaved():
                return self.FileName
            else:
                return "~%s~"%self.FileName
        return ""
    
    # Change file path and save file name or create a default one if file path not defined
    def SetFilePath(self, filepath):
        self.FilePath = filepath
        if filepath == "":
            self.LastNewIndex += 1
            self.FileName = _("Unnamed%d")%self.LastNewIndex
        else:
            self.FileName = os.path.splitext(os.path.basename(filepath))[0]
    
    # Change project properties
    def SetProjectProperties(self, name = None, properties = None, buffer = True):
        if self.Project is not None:
            if name is not None:
                self.Project.setname(name)
            if properties is not None:
                self.Project.setfileHeader(properties)
                self.Project.setcontentHeader(properties)
            if buffer and (name is not None or properties is not None):
                self.BufferProject()
    
    # Return project name
    def GetProjectName(self, debug=False):
        project = self.GetProject(debug)
        if project is not None:
            return project.getname()
        return None
    
    # Return project properties
    def GetProjectProperties(self, debug = False):
        project = self.GetProject(debug)
        if project is not None:
            properties = project.getfileHeader()
            properties.update(project.getcontentHeader())
            return properties
        return None
    
    # Return project informations
    def GetProjectInfos(self, debug = False):
        project = self.GetProject(debug)
        if project is not None:
            infos = {"name": project.getname(), "type": ITEM_PROJECT}
            datatypes = {"name": DATA_TYPES, "type": ITEM_DATATYPES, "values":[]}
            for datatype in project.getdataTypes():
                datatypes["values"].append({"name": datatype.getname(), "type": ITEM_DATATYPE, 
                    "tagname": self.ComputeDataTypeName(datatype.getname()), "values": []})
            pou_types = {"function": {"name": FUNCTIONS, "type": ITEM_FUNCTION, "values":[]},
                         "functionBlock": {"name": FUNCTION_BLOCKS, "type": ITEM_FUNCTIONBLOCK, "values":[]},
                         "program": {"name": PROGRAMS, "type": ITEM_PROGRAM, "values":[]}}
            for pou in project.getpous():
                pou_type = pou.getpouType()
                pou_infos = {"name": pou.getname(), "type": ITEM_POU,
                             "tagname": self.ComputePouName(pou.getname())}
                pou_values = []
                if pou.getbodyType() == "SFC":
                    transitions = []
                    for transition in pou.gettransitionList():
                        transitions.append({"name": transition.getname(), "type": ITEM_TRANSITION, 
                            "tagname": self.ComputePouTransitionName(pou.getname(), transition.getname()), 
                            "values": []})
                    pou_values.append({"name": TRANSITIONS, "type": ITEM_TRANSITIONS, "values": transitions})
                    actions = []
                    for action in pou.getactionList():
                        actions.append({"name": action.getname(), "type": ITEM_ACTION, 
                            "tagname": self.ComputePouActionName(pou.getname(), action.getname()), 
                            "values": []})
                    pou_values.append({"name": ACTIONS, "type": ITEM_ACTIONS, "values": actions})
                if pou_type in pou_types:
                    pou_infos["values"] = pou_values
                    pou_types[pou_type]["values"].append(pou_infos)
            configurations = {"name": CONFIGURATIONS, "type": ITEM_CONFIGURATIONS, "values": []}
            for config in project.getconfigurations():
                config_name = config.getname()
                config_infos = {"name": config_name, "type": ITEM_CONFIGURATION, 
                    "tagname": self.ComputeConfigurationName(config.getname()), 
                    "values": []}
                resources = {"name": RESOURCES, "type": ITEM_RESOURCES, "values": []}
                for resource in config.getresource():
                    resource_name = resource.getname()
                    resource_infos = {"name": resource_name, "type": ITEM_RESOURCE, 
                        "tagname": self.ComputeConfigurationResourceName(config.getname(), resource.getname()), 
                        "values": []}
                    resources["values"].append(resource_infos)
                config_infos["values"] = [resources]
                configurations["values"].append(config_infos)
            infos["values"] = [datatypes, pou_types["function"], pou_types["functionBlock"], 
                               pou_types["program"], configurations]
            return infos
        return None

    def GetPouVariableInfos(self, project, variable, var_class, debug=False):
        vartype_content = variable.gettype().getcontent()
        vartype_content_type = vartype_content.getLocalTag()
        if vartype_content_type == "derived":
            var_type = vartype_content.getname()
            pou_type = None
            pou = project.getpou(var_type)
            if pou is not None:
                pou_type = pou.getpouType()
            edit = debug = pou_type is not None
            if pou_type is None:
                block_infos = self.GetBlockType(var_type, debug = debug)
                if block_infos is not None:
                    pou_type = block_infos["type"]
            if pou_type is not None:
                var_class = None
                if pou_type == "program":
                    var_class = ITEM_PROGRAM
                elif pou_type != "function":
                    var_class = ITEM_FUNCTIONBLOCK
                if var_class is not None:
                    return {"name": variable.getname(), 
                            "type": var_type, 
                            "class": var_class,
                            "edit": edit,
                            "debug": debug}
            elif var_type in self.GetDataTypes(debug = debug):
                return {"name": variable.getname(), 
                        "type": var_type, 
                        "class": var_class,
                        "edit": False,
                        "debug": False}
        elif vartype_content_type in ["string", "wstring"]:
            return {"name": variable.getname(), 
                    "type": vartype_content_type.upper(), 
                    "class": var_class,
                    "edit": False,
                    "debug": True}
        else:
            return {"name": variable.getname(),
                    "type": vartype_content_type, 
                    "class": var_class,
                    "edit": False,
                    "debug": True}
        return None

    def GetPouVariables(self, tagname, debug = False):
        vars = []
        pou_type = None
        project = self.GetProject(debug)
        if project is not None:
            words = tagname.split("::")
            if words[0] == "P":
                pou = project.getpou(words[1])
                if pou is not None:
                    pou_type = pou.getpouType()
                    if (pou_type in ["program", "functionBlock"] and 
                        pou.interface is not None):
                        # Extract variables from every varLists
                        for varlist_type, varlist in pou.getvars():
                            var_infos = VAR_CLASS_INFOS.get(varlist_type, None)
                            if var_infos is not None:
                                var_class = var_infos[1]
                            else:
                                var_class = ITEM_VAR_LOCAL
                            for variable in varlist.getvariable():
                                var_infos = self.GetPouVariableInfos(project, variable, var_class, debug)
                                if var_infos is not None:
                                    vars.append(var_infos)
                        if pou.getbodyType() == "SFC":
                            for transition in pou.gettransitionList():
                                vars.append({
                                    "name": transition.getname(),
                                    "type": None, 
                                    "class": ITEM_TRANSITION,
                                    "edit": True,
                                    "debug": True})
                            for action in pou.getactionList():
                                vars.append({
                                    "name": action.getname(),
                                    "type": None, 
                                    "class": ITEM_ACTION,
                                    "edit": True,
                                    "debug": True})
                        return {"class": POU_TYPES[pou_type],
                                "type": words[1],
                                "variables": vars,
                                "edit": True,
                                "debug": True}
                else:
                    block_infos = self.GetBlockType(words[1], debug = debug)
                    if (block_infos is not None and 
                        block_infos["type"] in ["program", "functionBlock"]):
                        for varname, vartype, varmodifier in block_infos["inputs"]:
                            vars.append({"name" : varname, 
                                         "type" : vartype, 
                                         "class" : ITEM_VAR_INPUT,
                                         "edit": False,
                                         "debug": True})
                        for varname, vartype, varmodifier in block_infos["outputs"]:
                            vars.append({"name" : varname, 
                                         "type" : vartype, 
                                         "class" : ITEM_VAR_OUTPUT,
                                         "edit": False,
                                         "debug": True})
                        return {"class": POU_TYPES[block_infos["type"]],
                                "type": None,
                                "variables": vars,
                                "edit": False,
                                "debug": False}
            elif words[0] in ['A', 'T']:
                pou_vars = self.GetPouVariables(self.ComputePouName(words[1]), debug)
                if pou_vars is not None:
                    if words[0] == 'A':
                        element_type = ITEM_ACTION
                    elif words[0] == 'T':
                        element_type = ITEM_TRANSITION
                    return {"class": element_type,
                            "type": None,
                            "variables": [var for var in pou_vars["variables"] 
                                          if var["class"] not in [ITEM_ACTION, ITEM_TRANSITION]],
                            "edit": True,
                            "debug": True}
            elif words[0] in ['C', 'R']:
                if words[0] == 'C':
                    element_type = ITEM_CONFIGURATION
                    element = project.getconfiguration(words[1])
                    if element is not None:
                        for resource in element.getresource():
                            vars.append({"name": resource.getname(),
                                         "type": None,
                                         "class": ITEM_RESOURCE,
                                         "edit": True,
                                         "debug": False})
                elif words[0] == 'R':
                    element_type = ITEM_RESOURCE
                    element = project.getconfigurationResource(words[1], words[2])
                    if element is not None:
                        for task in element.gettask():
                            for pou in task.getpouInstance():
                                vars.append({"name": pou.getname(),
                                             "type": pou.gettypeName(),
                                             "class": ITEM_PROGRAM,
                                             "edit": True,
                                             "debug": True})
                        for pou in element.getpouInstance():
                            vars.append({"name": pou.getname(),
                                         "type": pou.gettypeName(),
                                         "class": ITEM_PROGRAM,
                                         "edit": True,
                                         "debug": True})
                if element is not None:
                    for varlist in element.getglobalVars():
                        for variable in varlist.getvariable():
                            var_infos = self.GetPouVariableInfos(project, variable, ITEM_VAR_GLOBAL, debug)
                            if var_infos is not None:
                                vars.append(var_infos)
                    return {"class": element_type,
                            "type": None,
                            "variables": vars,
                            "edit": True,
                            "debug": False}
        return None

    def RecursiveSearchPouInstances(self, project, pou_type, parent_path, varlists, debug = False):
        instances = []
        for varlist in varlists:
            for variable in varlist.getvariable():
                vartype_content = variable.gettype().getcontent()
                if vartype_content.getLocalTag() == "derived":
                    var_path = "%s.%s" % (parent_path, variable.getname())
                    var_type = vartype_content.getname()
                    if var_type == pou_type:
                        instances.append(var_path)
                    else:
                        pou = project.getpou(var_type)
                        if pou is not None and project.ElementIsUsedBy(pou_type, var_type):
                            instances.extend(
                                self.RecursiveSearchPouInstances(
                                    project, pou_type, var_path, 
                                    [varlist for type, varlist in pou.getvars()], 
                                    debug))
        return instances
                        
    def SearchPouInstances(self, tagname, debug = False):
        project = self.GetProject(debug)
        if project is not None:
            words = tagname.split("::")
            if words[0] == "P":
                instances = []
                for config in project.getconfigurations():
                    config_name = config.getname()
                    instances.extend(
                        self.RecursiveSearchPouInstances(
                            project, words[1], config_name, 
                            config.getglobalVars(), debug))
                    for resource in config.getresource():
                        res_path = "%s.%s" % (config_name, resource.getname())
                        instances.extend(
                            self.RecursiveSearchPouInstances(
                                project, words[1], res_path, 
                                resource.getglobalVars(), debug))
                        pou_instances = resource.getpouInstance()[:]
                        for task in resource.gettask():
                            pou_instances.extend(task.getpouInstance())
                        for pou_instance in pou_instances:
                            pou_path = "%s.%s" % (res_path, pou_instance.getname())
                            pou_type = pou_instance.gettypeName()
                            if pou_type == words[1]:
                                instances.append(pou_path)
                            pou = project.getpou(pou_type)
                            if pou is not None and project.ElementIsUsedBy(words[1], pou_type):
                                instances.extend(
                                    self.RecursiveSearchPouInstances(
                                        project, words[1], pou_path, 
                                        [varlist for type, varlist in pou.getvars()], 
                                        debug))
                return instances
            elif words[0] == 'C':
                return [words[1]]
            elif words[0] == 'R':
                return ["%s.%s" % (words[1], words[2])]
            elif words[0] in ['T', 'A']:
                return ["%s.%s" % (instance, words[2])
                        for instance in self.SearchPouInstances(
                            self.ComputePouName(words[1]), debug)]
        return []
    
    def RecursiveGetPouInstanceTagName(self, project, pou_type, parts, debug = False):
        pou = project.getpou(pou_type)
        if pou is not None:
            if len(parts) == 0:
                return self.ComputePouName(pou_type)
            
            for varlist_type, varlist in pou.getvars():
                for variable in varlist.getvariable():
                    if variable.getname() == parts[0]:
                        vartype_content = variable.gettype().getcontent()
                        if vartype_content.getLocalTag() == "derived":
                            return self.RecursiveGetPouInstanceTagName(
                                            project, 
                                            vartype_content["value"].getname(),
                                            parts[1:], debug)
            
            if pou.getbodyType() == "SFC" and len(parts) == 1:
                for action in pou.getactionList():
                    if action.getname() == parts[0]:
                        return self.ComputePouActionName(pou_type, parts[0])
                for transition in pou.gettransitionList():
                    if transition.getname() == parts[0]:
                        return self.ComputePouTransitionName(pou_type, parts[0])
        else:
            block_infos = self.GetBlockType(pou_type, debug=debug)
            if (block_infos is not None and 
                block_infos["type"] in ["program", "functionBlock"]):
                
                if len(parts) == 0:
                    return self.ComputePouName(pou_type)
                
                for varname, vartype, varmodifier in block_infos["inputs"] + block_infos["outputs"]:
                    if varname == parts[0]:
                        return self.RecursiveGetPouInstanceTagName(project, vartype, parts[1:], debug)
        return None
    
    def GetGlobalInstanceTagName(self, project, element, parts, debug = False):
        for varlist in element.getglobalVars():
            for variable in varlist.getvariable():
                if variable.getname() == parts[0]:
                    vartype_content = variable.gettype().getcontent()
                    if vartype_content.getLocalTag() == "derived":                        
                        if len(parts) == 1:
                            return self.ComputePouName(
                                        vartype_content.getname())
                        else:
                            return self.RecursiveGetPouInstanceTagName(
                                        project, 
                                        vartype_content.getname(),
                                        parts[1:], debug)
        return None
    
    def GetPouInstanceTagName(self, instance_path, debug = False):
        project = self.GetProject(debug)
        parts = instance_path.split(".")
        if len(parts) == 1:
            return self.ComputeConfigurationName(parts[0])
        elif len(parts) == 2:
            for config in project.getconfigurations():
                if config.getname() == parts[0]:
                    result = self.GetGlobalInstanceTagName(project, 
                                                           config, 
                                                           parts[1:],
                                                           debug)
                    if result is not None:
                        return result
            return self.ComputeConfigurationResourceName(parts[0], parts[1])
        else:
            for config in project.getconfigurations():
                if config.getname() == parts[0]:
                    for resource in config.getresource():
                        if resource.getname() == parts[1]:
                            pou_instances = resource.getpouInstance()[:]
                            for task in resource.gettask():
                                pou_instances.extend(task.getpouInstance())
                            for pou_instance in pou_instances:
                                if pou_instance.getname() == parts[2]:
                                    if len(parts) == 3:
                                        return self.ComputePouName(
                                                    pou_instance.gettypeName())
                                    else:
                                        return self.RecursiveGetPouInstanceTagName(
                                                    project,
                                                    pou_instance.gettypeName(),
                                                    parts[3:], debug)
                            return self.GetGlobalInstanceTagName(project, 
                                                                 resource, 
                                                                 parts[2:], 
                                                                 debug)
                    return self.GetGlobalInstanceTagName(project, 
                                                         config, 
                                                         parts[1:],
                                                         debug)
        return None
    
    def GetInstanceInfos(self, instance_path, debug = False):
        tagname = self.GetPouInstanceTagName(instance_path)
        if tagname is not None:
            infos = self.GetPouVariables(tagname, debug)
            infos["type"] = tagname
            return infos
        else:
            pou_path, var_name = instance_path.rsplit(".", 1)
            tagname = self.GetPouInstanceTagName(pou_path)
            if tagname is not None:
                pou_infos = self.GetPouVariables(tagname, debug)
                for var_infos in pou_infos["variables"]:
                    if var_infos["name"] == var_name:
                        return var_infos
        return None
    
    # Return if data type given by name is used by another data type or pou
    def DataTypeIsUsed(self, name, debug = False):
        project = self.GetProject(debug)
        if project is not None:
            return project.ElementIsUsed(name) or project.DataTypeIsDerived(name)
        return False

    # Return if pou given by name is used by another pou
    def PouIsUsed(self, name, debug = False):
        project = self.GetProject(debug)
        if project is not None:
            return project.ElementIsUsed(name)
        return False

    # Return if pou given by name is directly or undirectly used by the reference pou
    def PouIsUsedBy(self, name, reference, debug = False):
        project = self.GetProject(debug)
        if project is not None:
            return project.ElementIsUsedBy(name, reference)
        return False

    def GenerateProgram(self, filepath=None):
        errors = []
        warnings = []
        if self.Project is not None:
            try:
                self.ProgramChunks = GenerateCurrentProgram(self, self.Project, errors, warnings)
                self.NextCompiledProject = self.Copy(self.Project)
                program_text = "".join([item[0] for item in self.ProgramChunks])
                if filepath is not None:
                    programfile = open(filepath, "w")
                    programfile.write(program_text.encode("utf-8"))
                    programfile.close()
                    self.ProgramFilePath = filepath
                return program_text, errors, warnings
            except PLCGenException, e:
                errors.append(e.message)
        else:
            errors.append("No project opened")
        return "", errors, warnings

    def DebugAvailable(self):
        return self.CurrentCompiledProject is not None

    def ProgramTransferred(self):
        if self.NextCompiledProject is None:
            self.CurrentCompiledProject = self.NextCompiledProject
        else:
            self.CurrentCompiledProject = self.Copy(self.Project)

    def GetChunkInfos(self, from_location, to_location):
        row = self.ProgramOffset + 1
        col = 1
        infos = []
        for chunk, chunk_infos in self.ProgramChunks:
            lines = chunk.split("\n")
            if len(lines) > 1:
                next_row = row + len(lines) - 1
                next_col = len(lines[-1]) + 1
            else:
                next_row = row
                next_col = col + len(chunk)
            if (next_row > from_location[0] or next_row == from_location[0] and next_col >= from_location[1]) and len(chunk_infos) > 0:
                infos.append((chunk_infos, (row, col)))
            if next_row == to_location[0] and next_col > to_location[1] or next_row > to_location[0]:
                return infos
            row, col = next_row, next_col
        return infos
        
#-------------------------------------------------------------------------------
#                        Project Pous management functions
#-------------------------------------------------------------------------------
    
    # Add a Data Type to Project
    def ProjectAddDataType(self, datatype_name=None):
        if self.Project is not None:
            if datatype_name is None:
                datatype_name = self.GenerateNewName(None, None, "datatype%d")
            # Add the datatype to project
            self.Project.appenddataType(datatype_name)
            self.BufferProject()
            return self.ComputeDataTypeName(datatype_name)
        return None
        
    # Remove a Data Type from project
    def ProjectRemoveDataType(self, datatype_name):
        if self.Project is not None:
            self.Project.removedataType(datatype_name)
            self.BufferProject()
    
    # Add a Pou to Project
    def ProjectAddPou(self, pou_name, pou_type, body_type):
        if self.Project is not None:
            # Add the pou to project
            self.Project.appendpou(pou_name, pou_type, body_type)
            if pou_type == "function":
                self.SetPouInterfaceReturnType(pou_name, "BOOL")
            self.BufferProject()
            return self.ComputePouName(pou_name)
        return None
    
    def ProjectChangePouType(self, name, pou_type):
        if self.Project is not None:
            pou = self.Project.getpou(name)
            if pou is not None:
                pou.setpouType(pou_type)
                self.Project.RefreshCustomBlockTypes()
                self.BufferProject()
                
    def GetPouXml(self, pou_name):
        if self.Project is not None:
            pou = self.Project.getpou(pou_name)
            if pou is not None:
                return pou.generateXMLText('pou', 0)
        return None
    
    def PastePou(self, pou_type, pou_xml):
        '''
        Adds the POU defined by 'pou_xml' to the current project with type 'pou_type'
        '''
        try:
            tree = minidom.parseString(pou_xml.encode("utf-8"))
            root = tree.childNodes[0]
        except:
            return _("Couldn't paste non-POU object.")

        if root.nodeName == "pou":
            new_pou = plcopen.pous_pou()
            new_pou.loadXMLTree(root)

            name = new_pou.getname()
            
            idx = 0
            new_name = name
            while self.Project.getpou(new_name):
                # a POU with that name already exists.
                # make a new name and test if a POU with that name exists.
                # append an incrementing numeric suffix to the POU name.
                idx += 1
                new_name = "%s%d" % (name, idx)
                
            # we've found a name that does not already exist, use it
            new_pou.setname(new_name)
            
            if pou_type is not None:
                orig_type = new_pou.getpouType()
    
                # prevent violations of POU content restrictions:
                # function blocks cannot be pasted as functions,
                # programs cannot be pasted as functions or function blocks
                if orig_type == 'functionBlock' and pou_type == 'function' or \
                   orig_type == 'program' and pou_type in ['function', 'functionBlock']:
                    return _('''%s "%s" can't be pasted as a %s.''') % (orig_type, name, pou_type)
                
                new_pou.setpouType(pou_type)

            self.Project.insertpou(-1, new_pou)
            self.BufferProject()
            
            return self.ComputePouName(new_name),
        else:
            return _("Couldn't paste non-POU object.")

    # Remove a Pou from project
    def ProjectRemovePou(self, pou_name):
        if self.Project is not None:
            self.Project.removepou(pou_name)
            self.BufferProject()
    
    # Return the name of the configuration if only one exist
    def GetProjectMainConfigurationName(self):
        if self.Project is not None:
            # Found the configuration corresponding to old name and change its name to new name
            configurations = self.Project.getconfigurations()
            if len(configurations) == 1:
                return configurations[0].getname()
        return None
                
    # Add a configuration to Project
    def ProjectAddConfiguration(self, config_name=None):
        if self.Project is not None:
            if config_name is None:
                config_name = self.GenerateNewName(None, None, "configuration%d")
            self.Project.addconfiguration(config_name)
            self.BufferProject()
            return self.ComputeConfigurationName(config_name)
        return None
    
    # Remove a configuration from project
    def ProjectRemoveConfiguration(self, config_name):
        if self.Project is not None:
            self.Project.removeconfiguration(config_name)
            self.BufferProject()
    
    # Add a resource to a configuration of the Project
    def ProjectAddConfigurationResource(self, config_name, resource_name=None):
        if self.Project is not None:
            if resource_name is None:
                resource_name = self.GenerateNewName(None, None, "resource%d")
            self.Project.addconfigurationResource(config_name, resource_name)
            self.BufferProject()
            return self.ComputeConfigurationResourceName(config_name, resource_name)
        return None
    
    # Remove a resource from a configuration of the project
    def ProjectRemoveConfigurationResource(self, config_name, resource_name):
        if self.Project is not None:
            self.Project.removeconfigurationResource(config_name, resource_name)
            self.BufferProject()
    
    # Add a Transition to a Project Pou
    def ProjectAddPouTransition(self, pou_name, transition_name, transition_type):
        if self.Project is not None:
            pou = self.Project.getpou(pou_name)
            if pou is not None:
                pou.addtransition(transition_name, transition_type)
                self.BufferProject()
                return self.ComputePouTransitionName(pou_name, transition_name)
        return None
    
    # Remove a Transition from a Project Pou
    def ProjectRemovePouTransition(self, pou_name, transition_name):
        # Search if the pou removed is currently opened
        if self.Project is not None:
            pou = self.Project.getpou(pou_name)
            if pou is not None:
                pou.removetransition(transition_name)
                self.BufferProject()
    
    # Add an Action to a Project Pou
    def ProjectAddPouAction(self, pou_name, action_name, action_type):
        if self.Project is not None:
            pou = self.Project.getpou(pou_name)
            if pou is not None:
                pou.addaction(action_name, action_type)
                self.BufferProject()
                return self.ComputePouActionName(pou_name, action_name)
        return None
    
    # Remove an Action from a Project Pou
    def ProjectRemovePouAction(self, pou_name, action_name):
        # Search if the pou removed is currently opened
        if self.Project is not None:
            pou = self.Project.getpou(pou_name)
            if pou is not None:
                pou.removeaction(action_name)
                self.BufferProject()
    
    # Change the name of a pou
    def ChangeDataTypeName(self, old_name, new_name):
        if self.Project is not None:
            # Found the pou corresponding to old name and change its name to new name
            datatype = self.Project.getdataType(old_name)
            if datatype is not None:
                datatype.setname(new_name)
                self.Project.updateElementName(old_name, new_name)
                self.Project.RefreshElementUsingTree()
                self.Project.RefreshDataTypeHierarchy()
                self.BufferProject()
    
    # Change the name of a pou
    def ChangePouName(self, old_name, new_name):
        if self.Project is not None:
            # Found the pou corresponding to old name and change its name to new name
            pou = self.Project.getpou(old_name)
            if pou is not None:
                pou.setname(new_name)
                self.Project.updateElementName(old_name, new_name)
                self.Project.RefreshElementUsingTree()
                self.Project.RefreshCustomBlockTypes()
                self.BufferProject()
    
    # Change the name of a pou transition
    def ChangePouTransitionName(self, pou_name, old_name, new_name):
        if self.Project is not None:
            # Found the pou transition corresponding to old name and change its name to new name
            pou = self.Project.getpou(pou_name)
            if pou is not None:
                transition = pou.gettransition(old_name)
                if transition is not None:
                    transition.setname(new_name)
                    pou.updateElementName(old_name, new_name)
                    self.BufferProject()
    
    # Change the name of a pou action
    def ChangePouActionName(self, pou_name, old_name, new_name):
        if self.Project is not None:
            # Found the pou action corresponding to old name and change its name to new name
            pou = self.Project.getpou(pou_name)
            if pou is not None:
                action = pou.getaction(old_name)
                if action is not None:
                    action.setname(new_name)
                    pou.updateElementName(old_name, new_name)
                    self.BufferProject()
    
    # Change the name of a pou variable
    def ChangePouVariableName(self, pou_name, old_name, new_name):
        if self.Project is not None:
            # Found the pou action corresponding to old name and change its name to new name
            pou = self.Project.getpou(pou_name)
            if pou is not None:
                for type, varlist in pou.getvars():
                    for var in varlist.getvariable():
                        if var.getname() == old_name:
                            var.setname(new_name)
                self.Project.RefreshCustomBlockTypes()
                self.BufferProject()
        
    # Change the name of a configuration
    def ChangeConfigurationName(self, old_name, new_name):
        if self.Project is not None:
            # Found the configuration corresponding to old name and change its name to new name
            configuration = self.Project.getconfiguration(old_name)
            if configuration is not None:
                configuration.setname(new_name)
                self.BufferProject()
    
    # Change the name of a configuration resource
    def ChangeConfigurationResourceName(self, config_name, old_name, new_name):
        if self.Project is not None:
            # Found the resource corresponding to old name and change its name to new name
            resource = self.Project.getconfigurationResource(config_name, old_name)
            if resource is not None:
                resource.setname(new_name)
                self.BufferProject()
    
    # Return the description of the pou given by its name
    def GetPouDescription(self, name, debug = False):
        project = self.GetProject(debug)
        if project is not None:
            # Found the pou correponding to name and return its type
            pou = project.getpou(name)
            if pou is not None:
                return pou.getdescription()
        return ""
    
    # Return the description of the pou given by its name
    def SetPouDescription(self, name, description, debug = False):
        project = self.GetProject(debug)
        if project is not None:
            # Found the pou correponding to name and return its type
            pou = project.getpou(name)
            if pou is not None:
                pou.setdescription(description)
                project.RefreshCustomBlockTypes()
                self.BufferProject()
    
    # Return the type of the pou given by its name
    def GetPouType(self, name, debug = False):
        project = self.GetProject(debug)
        if project is not None:
            # Found the pou correponding to name and return its type
            pou = project.getpou(name)
            if pou is not None:
                return pou.getpouType()
        return None
    
    # Return pous with SFC language
    def GetSFCPous(self, debug = False):
        list = []
        project = self.GetProject(debug)
        if project is not None:
            for pou in project.getpous():
                if pou.getBodyType() == "SFC":
                    list.append(pou.getname())
        return list
    
    # Return the body language of the pou given by its name
    def GetPouBodyType(self, name, debug = False):
        project = self.GetProject(debug)
        if project is not None:
            # Found the pou correponding to name and return its body language
            pou = project.getpou(name)
            if pou is not None:
                return pou.getbodyType()
        return None
    
    # Return the actions of a pou
    def GetPouTransitions(self, pou_name, debug = False):
        transitions = []
        project = self.GetProject(debug)
        if project is not None:
            # Found the pou correponding to name and return its transitions if SFC
            pou = project.getpou(pou_name)
            if pou is not None and pou.getbodyType() == "SFC":
                for transition in pou.gettransitionList():
                    transitions.append(transition.getname())
        return transitions
    
    # Return the body language of the transition given by its name
    def GetTransitionBodyType(self, pou_name, pou_transition, debug = False):
        project = self.GetProject(debug)
        if project is not None:
            # Found the pou correponding to name
            pou = project.getpou(pou_name)
            if pou is not None:
                # Found the pou transition correponding to name and return its body language
                transition = pou.gettransition(pou_transition)
                if transition is not None:
                    return transition.getbodyType()
        return None
    
    # Return the actions of a pou
    def GetPouActions(self, pou_name, debug = False):
        actions = []
        project = self.GetProject(debug)
        if project is not None:
            # Found the pou correponding to name and return its actions if SFC
            pou = project.getpou(pou_name)
            if pou.getbodyType() == "SFC":
                for action in pou.getactionList():
                    actions.append(action.getname())
        return actions
    
    # Return the body language of the pou given by its name
    def GetActionBodyType(self, pou_name, pou_action, debug = False):
        project = self.GetProject(debug)
        if project is not None:
            # Found the pou correponding to name and return its body language
            pou = project.getpou(pou_name)
            if pou is not None:
                action = pou.getaction(pou_action)
                if action is not None:
                    return action.getbodyType()
        return None
    
    # Extract varlists from a list of vars
    def ExtractVarLists(self, vars):
        varlist_list = []
        current_varlist = None
        current_type = None
        for var in vars:
            next_type = (var["Class"], 
                         var["Option"], 
                         var["Location"] in ["", None] or 
                         # When declaring globals, located 
                         # and not located variables are 
                         # in the same declaration block
                         var["Class"] == "Global")
            if current_type != next_type:
                current_type = next_type
                infos = VAR_CLASS_INFOS.get(var["Class"], None)
                if infos is not None:
                    current_varlist = PLCOpenParser.CreateElement(infos[0], "interface")
                else:
                    current_varlist = PLCOpenParser.CreateElement("varList")
                varlist_list.append((var["Class"], current_varlist))
                if var["Option"] == "Constant":
                    current_varlist.setconstant(True)
                elif var["Option"] == "Retain":
                    current_varlist.setretain(True)
                elif var["Option"] == "Non-Retain":
                    current_varlist.setnonretain(True)
            # Create variable and change its properties
            tempvar = PLCOpenParser.CreateElement("variable", "varListPlain")
            tempvar.setname(var["Name"])
            
            var_type = PLCOpenParser.CreateElement("type", "variable")
            if isinstance(var["Type"], TupleType):
                if var["Type"][0] == "array":
                    array_type, base_type_name, dimensions = var["Type"]
                    array = PLCOpenParser.CreateElement("array", "dataType")
                    for i, dimension in enumerate(dimensions):
                        dimension_range = plcopen.rangeSigned()
                        dimension_range.setlower(dimension[0])
                        dimension_range.setupper(dimension[1])
                        if i == 0:
                            array.setdimension([dimension_range])
                        else:
                            array.appenddimension(dimension_range)
                    if base_type_name in self.GetBaseTypes():
                        array.baseType.setcontent(PLCOpenParser.CreateElement(
                            base_type_name.lower()
                            if base_type_name in ["STRING", "WSTRING"]
                            else base_type_name, "dataType"))
                    else:
                        derived_datatype = PLCOpenParser.CreateElement("derived", "dataType")
                        derived_datatype.setname(base_type_name)
                        array.baseType.setcontent(derived_datatype)
                    var_type.setcontent(array)
            elif var["Type"] in self.GetBaseTypes():
                var_type.setcontent(PLCOpenParser.CreateElement(
                    var["Type"].lower()
                    if var["Type"] in ["STRING", "WSTRING"]
                    else var["Type"], "dataType"))
            else:
                derived_type = PLCOpenParser.CreateElement("derived", "dataType")
                derived_type.setname(var["Type"])
                var_type.setcontent(derived_type)
            tempvar.settype(var_type)

            if var["Initial Value"] != "":
                value = PLCOpenParser.CreateElement("initialValue", "variable")
                value.setvalue(var["Initial Value"])
                tempvar.setinitialValue(value)
            if var["Location"] != "":
                tempvar.setaddress(var["Location"])
            else:
                tempvar.setaddress(None)
            if var['Documentation'] != "":
                ft = plcopen.formattedText()
                ft.setanyText(var['Documentation'])
                tempvar.setdocumentation(ft)

            # Add variable to varList
            current_varlist.appendvariable(tempvar)
        return varlist_list
    
    def GetVariableDictionary(self, varlist, var):
        '''
        convert a PLC variable to the dictionary representation
        returned by Get*Vars)
        '''

        tempvar = {"Name": var.getname()}

        vartype_content = var.gettype().getcontent()
        vartype_content_type = vartype_content.getLocalTag()
        if vartype_content_type == "derived":
            tempvar["Type"] = vartype_content.getname()
        elif vartype_content_type == "array":
            dimensions = []
            for dimension in vartype_content.getdimension():
                dimensions.append((dimension.getlower(), dimension.getupper()))
            base_type = vartype_content.baseType.getcontent()
            base_type_type = base_type.getLocalTag()
            if base_type_type == "derived":
                base_type_name = base_type.getname()
            else:
                base_type_name = base_type_type.upper()
            tempvar["Type"] = ("array", base_type_name, dimensions)
        else:
            tempvar["Type"] = vartype_content_type.upper()
        
        tempvar["Edit"] = True
        
        initial = var.getinitialValue()
        if initial is not None:
            tempvar["Initial Value"] = initial.getvalue()
        else:
            tempvar["Initial Value"] = ""

        address = var.getaddress()
        if address:
            tempvar["Location"] = address
        else:
            tempvar["Location"] = ""

        if varlist.getconstant():
            tempvar["Option"] = "Constant"
        elif varlist.getretain():
            tempvar["Option"] = "Retain"
        elif varlist.getnonretain():
            tempvar["Option"] = "Non-Retain"
        else:
            tempvar["Option"] = ""

        doc = var.getdocumentation()
        if doc is not None:
            tempvar["Documentation"] = doc.getanyText()
        else:
            tempvar["Documentation"] = ""

        return tempvar
    
    # Add a global var to configuration to configuration
    def AddConfigurationGlobalVar(self, config_name, type, var_name, 
                                           location="", description=""):
        if self.Project is not None:
            # Found the configuration corresponding to name
            configuration = self.Project.getconfiguration(config_name)
            if configuration is not None:
                # Set configuration global vars
                configuration.addglobalVar(type, var_name, location, description)

    # Replace the configuration globalvars by those given
    def SetConfigurationGlobalVars(self, name, vars):
        if self.Project is not None:
            # Found the configuration corresponding to name
            configuration = self.Project.getconfiguration(name)
            if configuration is not None:
                # Set configuration global vars
                configuration.setglobalVars([
                    varlist for vartype, varlist
                    in self.ExtractVarLists(vars)])
    
    # Return the configuration globalvars
    def GetConfigurationGlobalVars(self, name, debug = False):
        vars = []
        project = self.GetProject(debug)
        if project is not None:
            # Found the configuration corresponding to name
            configuration = project.getconfiguration(name)
            if configuration is not None:
                # Extract variables from every varLists
                for varlist in configuration.getglobalVars():
                    for var in varlist.getvariable():
                        tempvar = self.GetVariableDictionary(varlist, var)
                        tempvar["Class"] = "Global"
                        vars.append(tempvar)
        return vars

    # Return configuration variable names
    def GetConfigurationVariableNames(self, config_name = None, debug = False):
        variables = []
        project = self.GetProject(debug)
        if project is not None:
            for configuration in self.Project.getconfigurations():
                if config_name is None or config_name == configuration.getname():
                    variables.extend(
                        [var.getname() for var in reduce(
                            lambda x, y: x + y, [varlist.getvariable() 
                                for varlist in configuration.globalVars],
                            [])])
        return variables

    # Replace the resource globalvars by those given
    def SetConfigurationResourceGlobalVars(self, config_name, name, vars):
        if self.Project is not None:
            # Found the resource corresponding to name
            resource = self.Project.getconfigurationResource(config_name, name)
            # Set resource global vars
            if resource is not None:
                resource.setglobalVars([
                    varlist for vartype, varlist
                    in self.ExtractVarLists(vars)])
    
    # Return the resource globalvars
    def GetConfigurationResourceGlobalVars(self, config_name, name, debug = False):
        vars = []
        project = self.GetProject(debug)
        if project is not None:
            # Found the resource corresponding to name
            resource = project.getconfigurationResource(config_name, name)
            if resource:
                # Extract variables from every varLists
                for varlist in resource.getglobalVars():
                    for var in varlist.getvariable():
                        tempvar = self.GetVariableDictionary(varlist, var)
                        tempvar["Class"] = "Global"
                        vars.append(tempvar)
        return vars
    
    # Return resource variable names
    def GetConfigurationResourceVariableNames(self, 
                config_name = None, resource_name = None, debug = False):
        variables = []
        project = self.GetProject(debug)
        if project is not None:
            for configuration in self.Project.getconfigurations():
                if config_name is None or config_name == configuration.getname():
                    for resource in configuration.getresource():
                        if resource_name is None or resource.getname() == resource_name:
                            variables.extend(
                                [var.getname() for var in reduce(
                                    lambda x, y: x + y, [varlist.getvariable() 
                                        for varlist in resource.globalVars],
                                    [])])
        return variables
    
    # Recursively generate element name tree for a structured variable
    def GenerateVarTree(self, typename, debug = False):
        project = self.GetProject(debug)
        if project is not None:
            blocktype = self.GetBlockType(typename, debug = debug)
            if blocktype is not None:
                tree = []
                en = False
                eno = False
                for var_name, var_type, var_modifier in blocktype["inputs"] + blocktype["outputs"]:
                    en |= var_name.upper() == "EN"
                    eno |= var_name.upper() == "ENO"    
                    tree.append((var_name, var_type, self.GenerateVarTree(var_type, debug)))
                if not eno:
                    tree.insert(0, ("ENO", "BOOL", ([], [])))
                if not en:
                    tree.insert(0, ("EN", "BOOL", ([], [])))
                return tree, []
            datatype = project.getdataType(typename)
            if datatype is None:
                datatype = self.GetConfNodeDataType(typename)
            if datatype is not None:
                tree = []
                basetype_content = datatype.baseType.getcontent()
                basetype_content_type = basetype_content.getLocalTag()
                if basetype_content_type == "derived":
                    return self.GenerateVarTree(basetype_content.getname())
                elif basetype_content_type == "array":
                    dimensions = []
                    base_type = basetype_content.baseType.getcontent()
                    if base_type.getLocalTag() == "derived":
                        tree = self.GenerateVarTree(base_type.getname())
                        if len(tree[1]) == 0:
                            tree = tree[0]
                        for dimension in basetype_content.getdimension():
                            dimensions.append((dimension.getlower(), dimension.getupper()))
                    return tree, dimensions
                elif basetype_content_type == "struct":
                    for element in basetype_content.getvariable():
                        element_type = element.type.getcontent()
                        element_type_type = element_type.getLocalTag()
                        if element_type_type == "derived":
                            tree.append((element.getname(), element_type.getname(), self.GenerateVarTree(element_type.getname())))
                        else:
                            tree.append((element.getname(), element_type_type, ([], [])))
                    return tree, []
        return [], []

    # Return the interface for the given pou
    def GetPouInterfaceVars(self, pou, debug = False):
        vars = []
        # Verify that the pou has an interface
        if pou.interface is not None:
            # Extract variables from every varLists
            for type, varlist in pou.getvars():
                for var in varlist.getvariable():
                    tempvar = self.GetVariableDictionary(varlist, var)

                    tempvar["Class"] = type
                    tempvar["Tree"] = ([], [])

                    vartype_content = var.gettype().getcontent()
                    if vartype_content.getLocalTag() == "derived":
                        tempvar["Edit"] = not pou.hasblock(tempvar["Name"])
                        tempvar["Tree"] = self.GenerateVarTree(tempvar["Type"], debug)

                    vars.append(tempvar)
        return vars

    # Replace the Pou interface by the one given
    def SetPouInterfaceVars(self, name, vars):
        if self.Project is not None:
            # Found the pou corresponding to name and add interface if there isn't one yet
            pou = self.Project.getpou(name)
            if pou is not None:
                if pou.interface is None:
                    pou.interface = PLCOpenParser.CreateElement("interface", "pou")
                # Set Pou interface
                pou.setvars([varlist for varlist_type, varlist in self.ExtractVarLists(vars)])
                self.Project.RefreshElementUsingTree()
                self.Project.RefreshCustomBlockTypes()
    
    # Replace the return type of the pou given by its name (only for functions)
    def SetPouInterfaceReturnType(self, name, return_type):
        if self.Project is not None:
            pou = self.Project.getpou(name)
            if pou is not None:
                if pou.interface is None:
                    pou.interface = PLCOpenParser.CreateElement("interface", "pou")
                # If there isn't any return type yet, add it
                return_type_obj = pou.interface.getreturnType()
                if not return_type_obj:
                    return_type_obj = PLCOpenParser.CreateElement("returnType", "interface")
                    pou.interface.setreturnType(return_type_obj)
                # Change return type
                if return_type in self.GetBaseTypes():
                    return_type_obj.setcontent(PLCOpenParser.CreateElement(
                        return_type.lower()
                        if return_type in ["STRING", "WSTRING"]
                        else return_type, "dataType"))
                else:
                    derived_type = PLCOpenParser.CreateElement("derived", "dataType")
                    derived_type.setname(return_type)
                    return_type.setcontent(derived_type)
                self.Project.RefreshElementUsingTree()
                self.Project.RefreshCustomBlockTypes()
    
    def UpdateProjectUsedPous(self, old_name, new_name):
        if self.Project:
            self.Project.updateElementName(old_name, new_name)
    
    def UpdateEditedElementUsedVariable(self, tagname, old_name, new_name):
        pou = self.GetEditedElement(tagname)
        if pou:
            pou.updateElementName(old_name, new_name)
    
    # Return the return type of the pou given by its name
    def GetPouInterfaceReturnTypeByName(self, name):
        project = self.GetProject(debug)
        if project is not None:
            # Found the pou correponding to name and return the return type
            pou = project.getpou(name)
            if pou is not None:
                return self.GetPouInterfaceReturnType(pou)
        return False
    
    # Return the return type of the given pou
    def GetPouInterfaceReturnType(self, pou):
        # Verify that the pou has an interface
        if pou.interface is not None:
            # Return the return type if there is one
            return_type = pou.interface.getreturnType()
            if return_type is not None:
                returntype_content = return_type.getcontent()
                returntype_content_type = returntype_content.getLocalTag()
                if returntype_content_type == "derived":
                    return returntype_content.getname()
                else:
                    return returntype_content_type.upper()
        return None

    # Function that add a new confnode to the confnode list
    def AddConfNodeTypesList(self, typeslist):
        self.ConfNodeTypes.extend(typeslist)
        addedcat = [{"name": _("%s POUs") % confnodetypes["name"],
                     "list": confnodetypes["types"].GetCustomBlockTypes()}
                     for confnodetypes in typeslist]
        self.TotalTypes.extend(addedcat)
        for cat in addedcat:
            for desc in cat["list"]:
                BlkLst = self.TotalTypesDict.setdefault(desc["name"],[])
                BlkLst.append((section["name"], desc))
        
    # Function that clear the confnode list
    def ClearConfNodeTypes(self):
        self.ConfNodeTypes = []
        self.TotalTypesDict = StdBlckDct.copy()
        self.TotalTypes = StdBlckLst[:]

    def GetConfNodeBlockTypes(self):
        return [{"name": _("%s POUs") % confnodetypes["name"],
                 "list": confnodetypes["types"].GetCustomBlockTypes()}
                for confnodetypes in self.ConfNodeTypes]
        
    def GetConfNodeDataTypes(self, exclude = None, only_locatables = False):
        return [{"name": _("%s Data Types") % confnodetypes["name"],
                 "list": [datatype["name"] for datatype in confnodetypes["types"].GetCustomDataTypes(exclude, only_locatables)]}
                for confnodetypes in self.ConfNodeTypes]
    
    def GetConfNodeDataType(self, typename):
        for confnodetype in self.ConfNodeTypes:
            datatype = confnodetype["types"].getdataType(typename)
            if datatype is not None:
                return datatype
        return None
    
    def GetVariableLocationTree(self):
        return []

    def GetConfNodeGlobalInstances(self):
        return []

    def GetConfigurationExtraVariables(self):
        global_vars = []
        for var_name, var_type, var_initial in self.GetConfNodeGlobalInstances():
            tempvar = PLCOpenParser.CreateElement("variable", "globalVars")
            tempvar.setname(var_name)
            
            tempvartype = PLCOpenParser.CreateElement("dataType", "variable")
            if var_type in self.GetBaseTypes():
                tempvar.setcontent(PLCOpenParser.CreateElement(
                    var_type.lower()
                    if var_type in ["STRING", "WSTRING"]
                    else var_type, "dataType"))
            else:
                tempderivedtype = PLCOpenParser.CreateElement("derived", "dataType")
                tempderivedtype.setname(var_type)
                tempvartype.setcontent(tempderivedtype)
            tempvar.settype(tempvartype)
            
            if var_initial != "":
                value = PLCOpenParser.CreateElement("initialValue", "variable")
                value.setvalue(var_initial)
                tempvar.setinitialValue(value)
            
            global_vars.append(tempvar)
        return global_vars

    # Function that returns the block definition associated to the block type given
    def GetBlockType(self, typename, inputs = None, debug = False):
        result_blocktype = None
        for sectioname, blocktype in self.TotalTypesDict.get(typename,[]):
            if inputs is not None and inputs != "undefined":
                block_inputs = tuple([var_type for name, var_type, modifier in blocktype["inputs"]])
                if reduce(lambda x, y: x and y, map(lambda x: x[0] == "ANY" or self.IsOfType(*x), zip(inputs, block_inputs)), True):
                    return blocktype
            else:
                if result_blocktype is not None:
                    if inputs == "undefined":
                        return None
                    else:
                        result_blocktype["inputs"] = [(i[0], "ANY", i[2]) for i in result_blocktype["inputs"]]
                        result_blocktype["outputs"] = [(o[0], "ANY", o[2]) for o in result_blocktype["outputs"]]
                        return result_blocktype
                result_blocktype = blocktype.copy()
        if result_blocktype is not None:
            return result_blocktype
        project = self.GetProject(debug)
        if project is not None:
            return project.GetCustomBlockType(typename, inputs)
        return None

    # Return Block types checking for recursion
    def GetBlockTypes(self, tagname = "", debug = False):
        typename = None
        words = tagname.split("::")
        name = None
        project = self.GetProject(debug)
        if project is not None:
            pou_type = None
            if words[0] in ["P","T","A"]:
                blocktypes = []
                name = words[1]
                pou_type = self.GetPouType(name, debug)
            if pou_type == "function":
                for category in self.TotalTypes:
                    cat = {"name" : category["name"], "list" : []}
                    for block in category["list"]:
                        if block["type"] == "function":
                            cat["list"].append(block)
                    if len(cat["list"]) > 0:
                        blocktypes.append(cat)
            else:
                blocktypes = [category for category in self.TotalTypes]
            blocktypes.append({"name" : USER_DEFINED_POUS, 
                "list": project.GetCustomBlockTypes(name, 
                    pou_type == "function" or words[0] == "T")})
            return blocktypes
        return self.TotalTypes

    # Return Function Block types checking for recursion
    def GetFunctionBlockTypes(self, tagname = "", debug = False):
        blocktypes = []
        for blocks in self.TotalTypesDict.itervalues():
            for sectioname,block in blocks:
                if block["type"] == "functionBlock":
                    blocktypes.append(block["name"])
        project = self.GetProject(debug)
        if project is not None:
            name = ""
            words = tagname.split("::")
            if words[0] in ["P","T","A"]:
                name = words[1]
            blocktypes.extend(project.GetCustomFunctionBlockTypes(name))
        return blocktypes

    # Return Block types checking for recursion
    def GetBlockResource(self, debug = False):
        blocktypes = []
        for category in StdBlckLst[:-1]:
            for blocktype in category["list"]:
                if blocktype["type"] == "program":
                    blocktypes.append(blocktype["name"])
        project = self.GetProject(debug)
        if project is not None:
            blocktypes.extend(project.GetCustomBlockResource())
        return blocktypes

    # Return Data Types checking for recursion
    def GetDataTypes(self, tagname = "", basetypes = True, confnodetypes = True, only_locatables = False, debug = False):
        if basetypes:
            datatypes = self.GetBaseTypes()
        else:
            datatypes = []
        project = self.GetProject(debug)
        name = None
        if project is not None:
            words = tagname.split("::")
            if words[0] in ["D"]:
                name = words[1]
            datatypes.extend([datatype["name"] for datatype in project.GetCustomDataTypes(name, only_locatables)])
        if confnodetypes:
            for category in self.GetConfNodeDataTypes(name, only_locatables):
                datatypes.extend(category["list"])
        return datatypes

    # Return Base Type of given possible derived type
    def GetBaseType(self, type, debug = False):
        project = self.GetProject(debug)
        if project is not None:
            result = project.GetBaseType(type)
            if result is not None:
                return result
        for confnodetype in self.ConfNodeTypes:
            result = confnodetype["types"].GetBaseType(type)
            if result is not None:
                return result
        return None

    def GetBaseTypes(self):
        '''
        return the list of datatypes defined in IEC 61131-3.
        TypeHierarchy_list has a rough order to it (e.g. SINT, INT, DINT, ...),
        which makes it easy for a user to find a type in a menu.
        '''
        return [x for x,y in TypeHierarchy_list if not x.startswith("ANY")]

    def IsOfType(self, type, reference, debug = False):
        if reference is None:
            return True
        elif type == reference:
            return True
        elif type in TypeHierarchy:
            return self.IsOfType(TypeHierarchy[type], reference)
        else:
            project = self.GetProject(debug)
            if project is not None and project.IsOfType(type, reference):
                return True
            for confnodetype in self.ConfNodeTypes:
                if confnodetype["types"].IsOfType(type, reference):
                    return True
        return False
    
    def IsEndType(self, type):
        if type is not None:
            return not type.startswith("ANY")
        return True

    def IsLocatableType(self, type, debug = False):
        if isinstance(type, TupleType):
            return False 
        if self.GetBlockType(type) is not None:
            return False
        project = self.GetProject(debug)
        if project is not None:
            datatype = project.getdataType(type)
            if datatype is None:
                datatype = self.GetConfNodeDataType(type)
            if datatype is not None:
                return project.IsLocatableType(datatype)
        return True
    
    def IsEnumeratedType(self, type, debug = False):
        project = self.GetProject(debug)
        if project is not None:
            datatype = project.getdataType(type)
            if datatype is None:
                datatype = self.GetConfNodeDataType(type)
            if datatype is not None:
                basetype_content = datatype.baseType.getcontent()
                return basetype_content.getLocalTag() == "enum"
        return False

    def IsNumType(self, type, debug = False):
        return self.IsOfType(type, "ANY_NUM", debug) or\
               self.IsOfType(type, "ANY_BIT", debug)
            
    def GetDataTypeRange(self, type, debug = False):
        if type in DataTypeRange:
            return DataTypeRange[type]
        else:
            project = self.GetProject(debug)
            if project is not None:
                result = project.GetDataTypeRange(type)
                if result is not None:
                    return result
            for confnodetype in self.ConfNodeTypes:
                result = confnodetype["types"].GetDataTypeRange(type)
                if result is not None:
                    return result
        return None
    
    # Return Subrange types
    def GetSubrangeBaseTypes(self, exclude, debug = False):
        subrange_basetypes = []
        project = self.GetProject(debug)
        if project is not None:
            subrange_basetypes.extend(project.GetSubrangeBaseTypes(exclude))
        for confnodetype in self.ConfNodeTypes:
            subrange_basetypes.extend(confnodetype["types"].GetSubrangeBaseTypes(exclude))
        return DataTypeRange.keys() + subrange_basetypes
    
    # Return Enumerated Values
    def GetEnumeratedDataValues(self, type = None, debug = False):
        values = []
        project = self.GetProject(debug)
        if project is not None:
            values.extend(project.GetEnumeratedDataTypeValues(type))
            if type is None and len(values) > 0:
                return values
        for confnodetype in self.ConfNodeTypes:
            values.extend(confnodetype["types"].GetEnumeratedDataTypeValues(type))
            if type is None and len(values) > 0:
                return values
        return values

#-------------------------------------------------------------------------------
#                   Project Element tag name computation functions
#-------------------------------------------------------------------------------
    
    # Compute a data type name
    def ComputeDataTypeName(self, datatype):
        return "D::%s" % datatype
    
    # Compute a pou name
    def ComputePouName(self, pou):
        return "P::%s" % pou
    
    # Compute a pou transition name
    def ComputePouTransitionName(self, pou, transition):
        return "T::%s::%s" % (pou, transition)
    
    # Compute a pou action name
    def ComputePouActionName(self, pou, action):
        return "A::%s::%s" % (pou, action)

    # Compute a pou  name
    def ComputeConfigurationName(self, config):
        return "C::%s" % config

    # Compute a pou  name
    def ComputeConfigurationResourceName(self, config, resource):
        return "R::%s::%s" % (config, resource)

    def GetElementType(self, tagname):
        words = tagname.split("::")
        return {"D" : ITEM_DATATYPE, "P" : ITEM_POU, 
                "T" : ITEM_TRANSITION, "A" : ITEM_ACTION,
                "C" : ITEM_CONFIGURATION, "R" : ITEM_RESOURCE}[words[0]]

#-------------------------------------------------------------------------------
#                    Project opened Data types management functions
#-------------------------------------------------------------------------------

    # Return the data type informations
    def GetDataTypeInfos(self, tagname, debug = False):
        project = self.GetProject(debug)
        if project is not None:
            words = tagname.split("::")
            if words[0] == "D":
                infos = {}
                datatype = project.getdataType(words[1])
                if datatype is None:
                    return None
                basetype_content = datatype.baseType.getcontent()
                basetype_content_type = basetype_content.getLocalTag()
                if basetype_content_type in ["subrangeSigned", "subrangeUnsigned"]:
                    infos["type"] = "Subrange"
                    infos["min"] = basetype_content.range.getlower()
                    infos["max"] = basetype_content.range.getupper()
                    base_type = basetype_content.baseType.getcontent()
                    base_type_type = base_type.getLocalTag()
                    infos["base_type"] = (base_type.getname()
                        if base_type_type == "derived"
                        else base_type_type)
                elif basetype_content_type == "enum":
                    infos["type"] = "Enumerated"
                    infos["values"] = []
                    for value in basetype_content.xpath("ppx:values/ppx:value", namespaces=PLCOpenParser.NSMAP):
                        infos["values"].append(value.getname())
                elif basetype_content_type == "array":
                    infos["type"] = "Array"
                    infos["dimensions"] = []
                    for dimension in basetype_content.getdimension():
                        infos["dimensions"].append((dimension.getlower(), dimension.getupper()))
                    base_type = basetype_content.baseType.getcontent()
                    base_type_type = base_type.getLocalTag()
                    infos["base_type"] = (base_type.getname()
                        if base_type_type == "derived"
                        else base_type_type.upper())
                elif basetype_content_type == "struct":
                    infos["type"] = "Structure"
                    infos["elements"] = []
                    for element in basetype_content.getvariable():
                        element_infos = {}
                        element_infos["Name"] = element.getname()
                        element_type = element.type.getcontent()
                        element_type_type = element_type.getLocalTag()
                        if element_type_type == "array":
                            dimensions = []
                            for dimension in element_type.getdimension():
                                dimensions.append((dimension.getlower(), dimension.getupper()))
                            base_type = element_type.baseType.getcontent()
                            base_type_type = element_type.getLocalTag()
                            element_infos["Type"] = ("array", 
                                base_type.getname()
                                if base_type_type == "derived"
                                else base_type_type.upper(), dimensions)
                        elif element_type_type == "derived":
                            element_infos["Type"] = element_type.getname()
                        else:
                            element_infos["Type"] = element_type_type.upper()
                        if element.initialValue is not None:
                            element_infos["Initial Value"] = str(element.initialValue.getvalue())
                        else:
                            element_infos["Initial Value"] = ""
                        infos["elements"].append(element_infos)
                else:
                    infos["type"] = "Directly"
                    infos["base_type"] = (basetype_content.getname()
                        if basetype_content_type == "derived"
                        else basetype_content_type.upper())
                
                if datatype.initialValue is not None:
                    infos["initial"] = str(datatype.initialValue.getvalue())
                else:
                    infos["initial"] = ""
                return infos
        return None
    
    # Change the data type informations
    def SetDataTypeInfos(self, tagname, infos):
        words = tagname.split("::")
        if self.Project is not None and words[0] == "D":
            datatype = self.Project.getdataType(words[1])
            if infos["type"] == "Directly":
                if infos["base_type"] in self.GetBaseTypes():
                    datatype.baseType.setcontent(PLCOpenParser.CreateElement(
                        infos["base_type"].lower()
                        if infos["base_type"] in ["STRING", "WSTRING"]
                        else infos["base_type"], "dataType"))
                else:
                    derived_datatype = PLCOpenParser.CreateElement("derived", "dataType")
                    derived_datatype.setname(infos["base_type"])
                    datatype.baseType.setcontent(derived_datatype)
            elif infos["type"] == "Subrange":
                datatype.baseType.setcontent(PLCOpenParser.CreateElement(
                    "subrangeUnsigned" 
                    if infos["base_type"] in GetSubTypes("ANY_UINT")
                    else "subrangeSigned", "dataType"))
                subrange.range.setlower(infos["min"])
                subrange.range.setupper(infos["max"])
                if infos["base_type"] in self.GetBaseTypes():
                    subrange.baseType.setcontent(
                        PLCOpenParser.CreateElement(infos["base_type"]))
                else:
                    derived_datatype = PLCOpenParser.CreateElement("derived", "dataType")
                    derived_datatype.setname(infos["base_type"])
                    subrange.baseType.setcontent(derived_datatype)
            elif infos["type"] == "Enumerated":
                enumerated = PLCOpenParser.CreateElement("enum", "dataType")
                values = PLCOpenParser.CreateElement("values", "enum")
                enumerated.setvalues(values)
                for i, enum_value in enumerate(infos["values"]):
                    value = PLCOpenParser.CreateElement("value", "values")
                    value.setname(enum_value)
                    if i == 0:
                        values.setvalue([value])
                    else:
                        values.appendvalue(value)
                datatype.baseType.setcontent(enumerated)
            elif infos["type"] == "Array":
                array = PLCOpenParser.CreateElement("array", "dataType")
                for i, dimension in enumerate(infos["dimensions"]):
                    dimension_range = PLCOpenParser.CreateElement("dimension", "array")
                    dimension_range.setlower(dimension[0])
                    dimension_range.setupper(dimension[1])
                    if i == 0:
                        array.setdimension([dimension_range])
                    else:
                        array.appenddimension(dimension_range)
                if infos["base_type"] in self.GetBaseTypes():
                    array.baseType.setcontent(PLCOpenParser.CreateElement(
                        infos["base_type"].lower()
                        if infos["base_type"] in ["STRING", "WSTRING"]
                        else infos["base_type"], "dataType"))
                else:
                    derived_datatype = PLCOpenParser.CreateElement("derived", "dataType")
                    derived_datatype.setname(infos["base_type"])
                    array.baseType.setcontent(derived_datatype)
                datatype.baseType.setcontent(array)
            elif infos["type"] == "Structure":
                struct = PLCOpenParser.CreateElement("struct", "dataType")
                for i, element_infos in enumerate(infos["elements"]):
                    element = PLCOpenParser.CreateElement("variable", "struct")
                    element.setname(element_infos["Name"])
                    element_type = PLCOpenParser.CreateElement("type", "variable")
                    if isinstance(element_infos["Type"], TupleType):
                        if element_infos["Type"][0] == "array":
                            array_type, base_type_name, dimensions = element_infos["Type"]
                            array = PLCOpenParser.CreateElement("array", "dataType")
                            for j, dimension in enumerate(dimensions):
                                dimension_range = PLCOpenParser.CreateElement("dimension", "array")
                                dimension_range.setlower(dimension[0])
                                dimension_range.setupper(dimension[1])
                                if j == 0:
                                    array.setdimension([dimension_range])
                                else:
                                    array.appenddimension(dimension_range)
                            if base_type_name in self.GetBaseTypes():
                                array.baseType.setcontent(PLCOpenParser.CreateElement(
                                    base_type_name.lower()
                                    if base_type_name in ["STRING", "WSTRING"]
                                    else base_type_name, "dataType"))
                            else:
                                derived_datatype = PLCOpenParser.CreateElement("derived", "dataType")
                                derived_datatype.setname(base_type_name)
                                array.baseType.setcontent(derived_datatype)
                            element_type.setcontent(array)
                    elif element_infos["Type"] in self.GetBaseTypes():
                        element_type.setcontent(
                            PLCOpenParser.CreateElement(
                                element_infos["Type"].lower()
                                if element_infos["Type"] in ["STRING", "WSTRING"]
                                else element_infos["Type"], "dataType"))
                    else:
                        derived_datatype = PLCOpenParser.CreateElement("derived", "dataType")
                        derived_datatype.setname(element_infos["Type"])
                        element_type.setcontent(derived_datatype)
                    element.settype(element_type)
                    if element_infos["Initial Value"] != "":
                        value = PLCOpenParser.CreateElement("initialValue", "variable")
                        value.setvalue(element_infos["Initial Value"])
                        element.setinitialValue(value)
                    if i == 0:
                        struct.setvariable([element])
                    else:
                        struct.appendvariable(element)
                datatype.baseType.setcontent(struct)
            if infos["initial"] != "":
                if datatype.initialValue is None:
                    datatype.initialValue = PLCOpenParser.CreateElement("initialValue", "dataType")
                datatype.initialValue.setvalue(infos["initial"])
            else:
                datatype.initialValue = None
            self.Project.RefreshDataTypeHierarchy()
            self.Project.RefreshElementUsingTree()
            self.BufferProject()
    
#-------------------------------------------------------------------------------
#                       Project opened Pous management functions
#-------------------------------------------------------------------------------

    # Return edited element
    def GetEditedElement(self, tagname, debug = False):
        project = self.GetProject(debug)
        if project is not None:
            words = tagname.split("::")
            if words[0] == "D":
                return project.getdataType(words[1])
            elif words[0] == "P":
                return project.getpou(words[1])
            elif words[0] in ['T', 'A']:
                pou = project.getpou(words[1])
                if pou is not None:
                    if words[0] == 'T':
                        return pou.gettransition(words[2])
                    elif words[0] == 'A':
                        return pou.getaction(words[2])
            elif words[0] == 'C':
                return project.getconfiguration(words[1])
            elif words[0] == 'R':
                return project.getconfigurationResource(words[1], words[2])
        return None
    
    # Return edited element name
    def GetEditedElementName(self, tagname):
        words = tagname.split("::")
        if words[0] in ["P","C","D"]:
            return words[1]
        else:
            return words[2]
        return None
    
    # Return edited element name and type
    def GetEditedElementType(self, tagname, debug = False):
        words = tagname.split("::")
        if words[0] in ["P","T","A"]:
            return words[1], self.GetPouType(words[1], debug)
        return None, None

    # Return language in which edited element is written
    def GetEditedElementBodyType(self, tagname, debug = False):
        words = tagname.split("::")
        if words[0] == "P":
            return self.GetPouBodyType(words[1], debug)
        elif words[0] == 'T':
            return self.GetTransitionBodyType(words[1], words[2], debug)
        elif words[0] == 'A':
            return self.GetActionBodyType(words[1], words[2], debug)
        return None

    # Return the edited element variables
    def GetEditedElementInterfaceVars(self, tagname, debug = False):
        words = tagname.split("::")
        if words[0] in ["P","T","A"]:
            project = self.GetProject(debug)
            if project is not None:
                pou = project.getpou(words[1])
                if pou is not None:
                    return self.GetPouInterfaceVars(pou, debug)
        return []

    # Return the edited element return type
    def GetEditedElementInterfaceReturnType(self, tagname, debug = False):
        words = tagname.split("::")
        if words[0] == "P":
            project = self.GetProject(debug)
            if project is not None:
                pou = self.Project.getpou(words[1])
                if pou is not None:
                    return self.GetPouInterfaceReturnType(pou)
        elif words[0] == 'T':
            return "BOOL"
        return None
    
    # Change the edited element text
    def SetEditedElementText(self, tagname, text):
        if self.Project is not None:
            element = self.GetEditedElement(tagname)
            if element is not None:
                element.settext(text)
                self.Project.RefreshElementUsingTree()
    
    # Return the edited element text
    def GetEditedElementText(self, tagname, debug = False):
        element = self.GetEditedElement(tagname, debug)
        if element is not None:
            return element.gettext()
        return ""

    # Return the edited element transitions
    def GetEditedElementTransitions(self, tagname, debug = False):
        pou = self.GetEditedElement(tagname, debug)
        if pou is not None and pou.getbodyType() == "SFC":
            transitions = []
            for transition in pou.gettransitionList():
                transitions.append(transition.getname())
            return transitions
        return []

    # Return edited element transitions
    def GetEditedElementActions(self, tagname, debug = False):
        pou = self.GetEditedElement(tagname, debug)
        if pou is not None and pou.getbodyType() == "SFC":
            actions = []
            for action in pou.getactionList():
                actions.append(action.getname())
            return actions
        return []

    # Return the names of the pou elements
    def GetEditedElementVariables(self, tagname, debug = False):
        words = tagname.split("::")
        if words[0] in ["P","T","A"]:
            return self.GetProjectPouVariableNames(words[1], debug)
        elif words[0] in ["C", "R"]:
            names = self.GetConfigurationVariableNames(words[1], debug)
            if words[0] == "R":
                names.extend(self.GetConfigurationResourceVariableNames(
                    words[1], words[2], debug))
            return names
        return []

    def GetEditedElementCopy(self, tagname, debug = False):
        element = self.GetEditedElement(tagname, debug)
        if element is not None:
            name = element.__class__.__name__
            return element.generateXMLText(name.split("_")[-1], 0)
        return ""
        
    def GetEditedElementInstancesCopy(self, tagname, blocks_id = None, wires = None, debug = False):
        element = self.GetEditedElement(tagname, debug)
        text = ""
        if element is not None:
            wires = dict([(wire, True) for wire in wires if wire[0] in blocks_id and wire[1] in blocks_id])
            for id in blocks_id:
                instance = element.getinstance(id)
                if instance is not None:
                    instance_copy = self.Copy(instance)
                    instance_copy.filterConnections(wires)
                    name = instance_copy.__class__.__name__
                    text += instance_copy.generateXMLText(name.split("_")[-1], 0)
        return text
    
    def GenerateNewName(self, tagname, name, format, start_idx=0, exclude={}, debug=False):
        names = exclude.copy()
        if tagname is not None:
            names.update(dict([(varname.upper(), True) 
                               for varname in self.GetEditedElementVariables(tagname, debug)]))
            words = tagname.split("::")
            if words[0] in ["P","T","A"]:
                element = self.GetEditedElement(tagname, debug)
                if element is not None and element.getbodyType() not in ["ST", "IL"]:
                    for instance in element.getinstances():
                        if isinstance(instance, 
                            (PLCOpenParser.GetElementClass("step", "sfcObjects"), 
                             PLCOpenParser.GetElementClass("connector", "commonObjects"), 
                             PLCOpenParser.GetElementClass("continuation", "commonObjects"))):
                            names[instance.getname().upper()] = True
        else:
            project = self.GetProject(debug)
            if project is not None:
                for datatype in project.getdataTypes():
                    names[datatype.getname().upper()] = True
                for pou in project.getpous():
                    names[pou.getname().upper()] = True
                    for var in self.GetPouInterfaceVars(pou, debug):
                        names[var["Name"].upper()] = True
                    for transition in pou.gettransitionList():
                        names[transition.getname().upper()] = True
                    for action in pou.getactionList():
                        names[action.getname().upper()] = True
                for config in project.getconfigurations():
                    names[config.getname().upper()] = True
                    for resource in config.getresource():
                        names[resource.getname().upper()] = True
            
        i = start_idx
        while name is None or names.get(name.upper(), False):
            name = (format%i)
            i += 1
        return name
    
    CheckPasteCompatibility = {"SFC": lambda name: True,
                               "LD": lambda name: not name.startswith("sfcObjects"),
                               "FBD": lambda name: name.startswith("fbdObjects") or name.startswith("commonObjects")}
    
    def PasteEditedElementInstances(self, tagname, text, new_pos, middle=False, debug=False):
        element = self.GetEditedElement(tagname, debug)
        element_name, element_type = self.GetEditedElementType(tagname, debug)
        if element is not None:
            bodytype = element.getbodyType()
            
            # Get edited element type scaling
            scaling = None
            project = self.GetProject(debug)
            if project is not None:
                properties = project.getcontentHeader()
                scaling = properties["scaling"][bodytype]
            
            # Get ids already by all the instances in edited element
            used_id = dict([(instance.getlocalId(), True) for instance in element.getinstances()])
            new_id = {}
            
            text = "<paste>%s</paste>"%text
            
            try:
                tree = minidom.parseString(text.encode("utf-8"))
            except:
                return _("Invalid plcopen element(s)!!!")
            instances = []
            exclude = {}
            for root in tree.childNodes:
                if root.nodeType == tree.ELEMENT_NODE and root.nodeName == "paste":
                    for child in root.childNodes:
                        if child.nodeType == tree.ELEMENT_NODE:
                            if not child.nodeName in plcopen.ElementNameToClass:
                                return _("\"%s\" element can't be pasted here!!!")%child.nodeName

                            classname = plcopen.ElementNameToClass[child.nodeName]
                            if not self.CheckPasteCompatibility[bodytype](classname):
                                return _("\"%s\" element can't be pasted here!!!")%child.nodeName

                            classobj = getattr(plcopen, classname, None)
                            if classobj is not None:
                                instance = classobj()
                                instance.loadXMLTree(child)
                                if child.nodeName == "block":
                                    blockname = instance.getinstanceName()
                                    if blockname is not None:
                                        blocktype = instance.gettypeName()
                                        if element_type == "function":
                                            return _("FunctionBlock \"%s\" can't be pasted in a Function!!!")%blocktype
                                        blockname = self.GenerateNewName(tagname, 
                                                                         blockname, 
                                                                         "%s%%d"%blocktype, 
                                                                         debug=debug)
                                        exclude[blockname] = True
                                        instance.setinstanceName(blockname)
                                        self.AddEditedElementPouVar(tagname, blocktype, blockname)
                                elif child.nodeName == "step":
                                    stepname = self.GenerateNewName(tagname, 
                                                                    instance.getname(), 
                                                                    "Step%d", 
                                                                    exclude=exclude, 
                                                                    debug=debug)
                                    exclude[stepname] = True
                                    instance.setname(stepname)
                                localid = instance.getlocalId()
                                if not used_id.has_key(localid):
                                    new_id[localid] = True
                                instances.append((child.nodeName, instance))
            
            if len(instances) == 0:
                return _("Invalid plcopen element(s)!!!")
            
            idx = 1
            translate_id = {}
            bbox = plcopen.rect()
            for name, instance in instances:
                localId = instance.getlocalId()
                bbox.union(instance.getBoundingBox())
                if used_id.has_key(localId):
                    while used_id.has_key(idx) or new_id.has_key(idx):
                        idx += 1
                    new_id[idx] = True
                    instance.setlocalId(idx)
                    translate_id[localId] = idx
            
            x, y, width, height = bbox.bounding_box()
            if middle:
                new_pos[0] -= width / 2
                new_pos[1] -= height / 2
            else:
                new_pos = map(lambda x: x + 30, new_pos)
            if scaling[0] != 0 and scaling[1] != 0:
                min_pos = map(lambda x: 30 / x, scaling)
                minx = round(min_pos[0])
                if int(min_pos[0]) == round(min_pos[0]):
                    minx += 1
                miny = round(min_pos[1])
                if int(min_pos[1]) == round(min_pos[1]):
                    miny += 1
                minx *= scaling[0]
                miny *= scaling[1]
                new_pos = (max(minx, round(new_pos[0] / scaling[0]) * scaling[0]),
                           max(miny, round(new_pos[1] / scaling[1]) * scaling[1]))
            else:
                new_pos = (max(30, new_pos[0]), max(30, new_pos[1]))
            diff = (new_pos[0] - x, new_pos[1] - y)
            
            connections = {}
            for name, instance in instances:
                connections.update(instance.updateConnectionsId(translate_id))
                if getattr(instance, "setexecutionOrderId", None) is not None:
                    instance.setexecutionOrderId(0)
                instance.translate(*diff)
                element.addinstance(name, instance)
            
            return new_id, connections
                
    # Return the current pou editing informations
    def GetEditedElementInstanceInfos(self, tagname, id = None, exclude = [], debug = False):
        infos = {}
        instance = None
        element = self.GetEditedElement(tagname, debug)
        if element is not None:
            # if id is defined
            if id is not None:
                instance = element.getinstance(id)
            else:
                instance = element.getrandomInstance(exclude)
        if instance is not None:
            infos = instance.getinfos()
            if infos["type"] in ["input", "output", "inout"]:
                var_type = self.GetEditedElementVarValueType(tagname, infos["specific_values"]["name"], debug)
                infos["specific_values"]["value_type"] = var_type
            return infos
        return None
    
    def ClearEditedElementExecutionOrder(self, tagname):
        element = self.GetEditedElement(tagname)
        if element is not None:
            element.resetexecutionOrder()
    
    def ResetEditedElementExecutionOrder(self, tagname):
        element = self.GetEditedElement(tagname)
        if element is not None:
            element.compileexecutionOrder()
    
    # Return the variable type of the given pou
    def GetEditedElementVarValueType(self, tagname, varname, debug = False):
        project = self.GetProject(debug)
        if project is not None:
            words = tagname.split("::")
            if words[0] in ["P","T","A"]:
                pou = self.Project.getpou(words[1])
                if pou is not None:
                    if words[0] == "T" and varname == words[2]:
                        return "BOOL"
                    if words[1] == varname:
                        return self.GetPouInterfaceReturnType(pou)
                    for type, varlist in pou.getvars():
                        for var in varlist.getvariable():
                            if var.getname() == varname:
                                vartype_content = var.gettype().getcontent()
                                vartype_content_type = vartype_content.getLocalTag()
                                if vartype_content_type == "derived":
                                    return vartype_content.getname()
                                else:
                                    return vartype_content_type.upper()
        return None
    
    def SetConnectionWires(self, connection, connector):
        wires = connector.GetWires()
        idx = 0
        for wire, handle in wires:
            points = wire.GetPoints(handle != 0)
            if handle == 0:
                result = wire.GetConnectedInfos(-1)
            else:
                result = wire.GetConnectedInfos(0)
            if result != None:
                refLocalId, formalParameter = result
                connections = connection.getconnections()
                if connections is None or len(connection.getconnections()) <= idx:
                    connection.addconnection()
                connection.setconnectionId(idx, refLocalId)
                connection.setconnectionPoints(idx, points)
                if formalParameter != "":
                    connection.setconnectionParameter(idx, formalParameter)
                else:
                    connection.setconnectionParameter(idx, None)
                idx += 1
    
    def AddEditedElementPouVar(self, tagname, type, name, location="", description=""):
        if self.Project is not None:
            words = tagname.split("::")
            if words[0] in ['P', 'T', 'A']:
                pou = self.Project.getpou(words[1])
                if pou is not None:
                    pou.addpouLocalVar(type, name, location, description)
    
    def AddEditedElementPouExternalVar(self, tagname, type, name):
        if self.Project is not None:
            words = tagname.split("::")
            if words[0] in ['P', 'T', 'A']:
                pou = self.Project.getpou(words[1])
                if pou is not None:
                    pou.addpouExternalVar(type, name)
            
    def ChangeEditedElementPouVar(self, tagname, old_type, old_name, new_type, new_name):
        if self.Project is not None:
            words = tagname.split("::")
            if words[0] in ['P', 'T', 'A']:
                pou = self.Project.getpou(words[1])
                if pou is not None:
                    pou.changepouVar(old_type, old_name, new_type, new_name)
    
    def RemoveEditedElementPouVar(self, tagname, type, name):
        if self.Project is not None:
            words = tagname.split("::")
            if words[0] in ['P', 'T', 'A']:
                pou = self.Project.getpou(words[1])
                if pou is not None:
                    pou.removepouVar(type, name)
    
    def AddEditedElementBlock(self, tagname, id, blocktype, blockname = None):
        element = self.GetEditedElement(tagname)
        if element is not None:
            block = PLCOpenParser.CreateElement("block", "fbdObjects")
            block.setlocalId(id)
            block.settypeName(blocktype)
            blocktype_infos = self.GetBlockType(blocktype)
            if blocktype_infos["type"] != "function" and blockname is not None:
                block.setinstanceName(blockname)
                self.AddEditedElementPouVar(tagname, blocktype, blockname)
            element.addinstance(block)
            self.Project.RefreshElementUsingTree()
    
    def SetEditedElementBlockInfos(self, tagname, id, infos):
        element = self.GetEditedElement(tagname)
        if element is not None:
            block = element.getinstance(id)
            if block is None:
                return 
            old_name = block.getinstanceName()
            old_type = block.gettypeName()
            new_name = infos.get("name", old_name)
            new_type = infos.get("type", old_type)
            if new_type != old_type:
                old_typeinfos = self.GetBlockType(old_type)
                new_typeinfos = self.GetBlockType(new_type)
                if old_typeinfos is None or new_typeinfos is None:
                    self.ChangeEditedElementPouVar(tagname, old_type, old_name, new_type, new_name)
                elif new_typeinfos["type"] != old_typeinfos["type"]:
                    if new_typeinfos["type"] == "function":
                        self.RemoveEditedElementPouVar(tagname, old_type, old_name)
                    else:
                        self.AddEditedElementPouVar(tagname, new_type, new_name)
                elif new_typeinfos["type"] != "function":
                    self.ChangeEditedElementPouVar(tagname, old_type, old_name, new_type, new_name)
            elif new_name != old_name:
                self.ChangeEditedElementPouVar(tagname, old_type, old_name, new_type, new_name)
            for param, value in infos.items():
                if param == "name":
                    block.setinstanceName(value)
                elif param == "type":
                    block.settypeName(value)
                elif param == "executionOrder" and block.getexecutionOrderId() != value:
                    element.setelementExecutionOrder(block, value)
                elif param == "height":
                    block.setheight(value)
                elif param == "width":
                    block.setwidth(value)
                elif param == "x":
                    block.setx(value)
                elif param == "y":
                    block.sety(value)
                elif param == "connectors":
                    block.inputVariables.setvariable([])
                    block.outputVariables.setvariable([])
                    for connector in value["inputs"]:
                        variable = PLCOpenParser.CreateElement("variable", "inputVariables")
                        block.inputVariables.appendvariable(variable)
                        variable.setformalParameter(connector.GetName())
                        if connector.IsNegated():
                            variable.setnegated(True)
                        if connector.GetEdge() != "none":
                            variable.setedge(connector.GetEdge())
                        position = connector.GetRelPosition()
                        variable.connectionPointIn.setrelPositionXY(position.x, position.y)
                        self.SetConnectionWires(variable.connectionPointIn, connector)
                    for connector in value["outputs"]:
                        variable = PLCOpenParser.CreateElement("variable", "outputVariables")
                        block.outputVariables.appendvariable(variable)
                        variable.setformalParameter(connector.GetName())
                        if connector.IsNegated():
                            variable.setnegated(True)
                        if connector.GetEdge() != "none":
                            variable.setedge(connector.GetEdge())
                        position = connector.GetRelPosition()
                        variable.addconnectionPointOut()
                        variable.connectionPointOut.setrelPositionXY(position.x, position.y)
            block.tostring()
            self.Project.RefreshElementUsingTree()
        
    def AddEditedElementVariable(self, tagname, id, var_type):
        element = self.GetEditedElement(tagname)
        if element is not None:
            variable = PLCOpenParser.CreateElement(
                {INPUT: "inVariable",
                 OUTPUT: "outVariable",
                 INOUT: "inOutVariable"}[var_type], "fbdObjects")
            variable.setlocalId(id)
            element.addinstance(variable)
        
    def SetEditedElementVariableInfos(self, tagname, id, infos):
        element = self.GetEditedElement(tagname)
        if element is not None:
            variable = element.getinstance(id)
            if variable is None:
                return 
            for param, value in infos.items():
                if param == "name":
                    expression = PLCOpenParser.CreateElement("expression", variable.getLocalTag())
                    expression.text = value
                    variable.setexpression(expression)
                elif param == "executionOrder" and variable.getexecutionOrderId() != value:
                    element.setelementExecutionOrder(variable, value)
                elif param == "height":
                    variable.setheight(value)
                elif param == "width":
                    variable.setwidth(value)
                elif param == "x":
                    variable.setx(value)
                elif param == "y":
                    variable.sety(value)
                elif param == "connectors":
                    if len(value["outputs"]) > 0:
                        output = value["outputs"][0]
                        if len(value["inputs"]) > 0:
                            variable.setnegatedOut(output.IsNegated())
                            variable.setedgeOut(output.GetEdge())
                        else:
                            variable.setnegated(output.IsNegated())
                            variable.setedge(output.GetEdge())
                        position = output.GetRelPosition()
                        variable.addconnectionPointOut()
                        variable.connectionPointOut.setrelPositionXY(position.x, position.y)
                    if len(value["inputs"]) > 0:
                        input = value["inputs"][0]
                        if len(value["outputs"]) > 0:
                            variable.setnegatedIn(input.IsNegated())
                            variable.setedgeIn(input.GetEdge())
                        else:
                            variable.setnegated(input.IsNegated())
                            variable.setedge(input.GetEdge())
                        position = input.GetRelPosition()
                        variable.addconnectionPointIn()
                        variable.connectionPointIn.setrelPositionXY(position.x, position.y)
                        self.SetConnectionWires(variable.connectionPointIn, input)

    def AddEditedElementConnection(self, tagname, id, connection_type):
        element = self.GetEditedElement(tagname)
        if element is not None:
            connection = PLCOpenParser.CreateElement(
                {CONNECTOR: "connector",
                 CONTINUATION: "continuation"}[connection_type], "commonObjects")
            connection.setlocalId(id)
            element.addinstance(connection)
        
    def SetEditedElementConnectionInfos(self, tagname, id, infos):
        element = self.GetEditedElement(tagname)
        if element is not None:
            connection = element.getinstance(id)
            if connection is None:
                return
            for param, value in infos.items():
                if param == "name":
                    connection.setname(value)    
                elif param == "height":
                    connection.setheight(value)
                elif param == "width":
                    connection.setwidth(value)
                elif param == "x":
                    connection.setx(value)
                elif param == "y":
                    connection.sety(value)
                elif param == "connector":
                    position = value.GetRelPosition()
                    if isinstance(connection, PLCOpenParser.GetElementClass("continuation", "commonObjects")):
                        connection.addconnectionPointOut()
                        connection.connectionPointOut.setrelPositionXY(position.x, position.y)
                    elif isinstance(connection, PLCOpenParser.GetElementClass("connector", "commonObjects")):
                        connection.addconnectionPointIn()
                        connection.connectionPointIn.setrelPositionXY(position.x, position.y)
                        self.SetConnectionWires(connection.connectionPointIn, value)

    def AddEditedElementComment(self, tagname, id):
        element = self.GetEditedElement(tagname)
        if element is not None:
            comment = PLCOpenParser.CreateElement("comment", "commonObjects")
            comment.setlocalId(id)
            element.addinstance(comment)
    
    def SetEditedElementCommentInfos(self, tagname, id, infos):
        element = self.GetEditedElement(tagname)
        if element is not None:
            comment = element.getinstance(id)
            for param, value in infos.items():
                if param == "content":
                    comment.setcontentText(value)
                elif param == "height":
                    comment.setheight(value)
                elif param == "width":
                    comment.setwidth(value)
                elif param == "x":
                    comment.setx(value)
                elif param == "y":
                    comment.sety(value)

    def AddEditedElementPowerRail(self, tagname, id, powerrail_type):
        element = self.GetEditedElement(tagname)
        if element is not None:
            powerrail = PLCOpenParser.CreateElement(
                {LEFTRAIL: "leftPowerRail",
                 RIGHTRAIL: "rightPowerRail"}[powerrail_type], "ldObjects")
            powerrail.setlocalId(id)
            element.addinstance(powerrail)
    
    def SetEditedElementPowerRailInfos(self, tagname, id, infos):
        element = self.GetEditedElement(tagname)
        if element is not None:
            powerrail = element.getinstance(id)
            if powerrail is None:
                return
            for param, value in infos.items():
                if param == "height":
                    powerrail.setheight(value)
                elif param == "width":
                    powerrail.setwidth(value)
                elif param == "x":
                    powerrail.setx(value)
                elif param == "y":
                    powerrail.sety(value)
                elif param == "connectors":
                    if isinstance(powerrail, PLCOpenParser.GetElementClass("leftPowerRail", "ldObjects")):
                        powerrail.setconnectionPointOut([])
                        for connector in value["outputs"]:
                            position = connector.GetRelPosition()
                            connection = PLCOpenParser.CreateElement("connectionPointOut", "leftPowerRail")
                            powerrail.appendconnectionPointOut(connection)
                            connection.setrelPositionXY(position.x, position.y)
                    elif isinstance(powerrail, PLCOpenParser.GetElementClass("rightPowerRail", "ldObjects")):
                        powerrail.setconnectionPointIn([])
                        for connector in value["inputs"]:
                            position = connector.GetRelPosition()
                            connection = PLCOpenParser.CreateElement("connectionPointIn", "rightPowerRail")
                            powerrail.appendconnectionPointIn(connection)
                            connection.setrelPositionXY(position.x, position.y)
                            self.SetConnectionWires(connection, connector)
                            
    def AddEditedElementContact(self, tagname, id):
        element = self.GetEditedElement(tagname)
        if element is not None:
            contact = PLCOpenParser.CreateElement("contact", "ldObjects")
            contact.setlocalId(id)
            element.addinstance(contact)

    def SetEditedElementContactInfos(self, tagname, id, infos):
        element = self.GetEditedElement(tagname)
        if element is not None:
            contact = element.getinstance(id)
            if contact is None:
                return
            for param, value in infos.items():
                if param == "name":
                    variable = PLCOpenParser.CreateElement("variable", "contact")
                    variable.text = value
                    contact.setvariable(variable)
                elif param == "type":
                    if value == CONTACT_NORMAL:
                        contact.setnegated(False)
                        contact.setedge("none")
                    elif value == CONTACT_REVERSE:
                        contact.setnegated(True)
                        contact.setedge("none")
                    elif value == CONTACT_RISING:
                        contact.setnegated(False)
                        contact.setedge("rising")
                    elif value == CONTACT_FALLING:
                        contact.setnegated(False)
                        contact.setedge("falling")
                elif param == "height":
                    contact.setheight(value)
                elif param == "width":
                    contact.setwidth(value)
                elif param == "x":
                    contact.setx(value)
                elif param == "y":
                    contact.sety(value)
                elif param == "connectors":
                    input_connector = value["inputs"][0]
                    position = input_connector.GetRelPosition()
                    contact.addconnectionPointIn()
                    contact.connectionPointIn.setrelPositionXY(position.x, position.y)
                    self.SetConnectionWires(contact.connectionPointIn, input_connector)
                    output_connector = value["outputs"][0]
                    position = output_connector.GetRelPosition()
                    contact.addconnectionPointOut()
                    contact.connectionPointOut.setrelPositionXY(position.x, position.y)

    def AddEditedElementCoil(self, tagname, id):
        element = self.GetEditedElement(tagname)
        if element is not None:
            coil = PLCOpenParser.CreateElement("coil", "ldObjects")
            coil.setlocalId(id)
            element.addinstance(coil)

    def SetEditedElementCoilInfos(self, tagname, id, infos):
        element = self.GetEditedElement(tagname)
        if element is not None:
            coil = element.getinstance(id)
            if coil is None:
                return
            for param, value in infos.items():
                if param == "name":
                    variable = PLCOpenParser.CreateElement("variable", "coil")
                    variable.text = value
                    coil.setvariable(variable)
                elif param == "type":
                    if value == COIL_NORMAL:
                        coil.setnegated(False)
                        coil.setstorage("none")
                        coil.setedge("none")
                    elif value == COIL_REVERSE:
                        coil.setnegated(True)
                        coil.setstorage("none")
                        coil.setedge("none")
                    elif value == COIL_SET:
                        coil.setnegated(False)
                        coil.setstorage("set")
                        coil.setedge("none")
                    elif value == COIL_RESET:
                        coil.setnegated(False)
                        coil.setstorage("reset")
                        coil.setedge("none")
                    elif value == COIL_RISING:
                        coil.setnegated(False)
                        coil.setstorage("none")
                        coil.setedge("rising")
                    elif value == COIL_FALLING:
                        coil.setnegated(False)
                        coil.setstorage("none")
                        coil.setedge("falling")
                elif param == "height":
                    coil.setheight(value)
                elif param == "width":
                    coil.setwidth(value)
                elif param == "x":
                    coil.setx(value)
                elif param == "y":
                    coil.sety(value)
                elif param == "connectors":
                    input_connector = value["inputs"][0]
                    position = input_connector.GetRelPosition()
                    coil.addconnectionPointIn()
                    coil.connectionPointIn.setrelPositionXY(position.x, position.y)
                    self.SetConnectionWires(coil.connectionPointIn, input_connector)
                    output_connector = value["outputs"][0]
                    position = output_connector.GetRelPosition()
                    coil.addconnectionPointOut()
                    coil.connectionPointOut.setrelPositionXY(position.x, position.y)

    def AddEditedElementStep(self, tagname, id):
        element = self.GetEditedElement(tagname)
        if element is not None:
            step = PLCOpenParser.CreateElement("step", "sfcObjects")
            step.setlocalId(id)
            element.addinstance(step)
    
    def SetEditedElementStepInfos(self, tagname, id, infos):
        element = self.GetEditedElement(tagname)
        if element is not None:
            step = element.getinstance(id)
            if step is None:
                return
            for param, value in infos.items():
                if param == "name":
                    step.setname(value)
                elif param == "initial":
                    step.setinitialStep(value)
                elif param == "height":
                    step.setheight(value)
                elif param == "width":
                    step.setwidth(value)
                elif param == "x":
                    step.setx(value)
                elif param == "y":
                    step.sety(value)
                elif param == "connectors":
                    if len(value["inputs"]) > 0:
                        input_connector = value["inputs"][0]
                        position = input_connector.GetRelPosition()
                        step.addconnectionPointIn()
                        step.connectionPointIn.setrelPositionXY(position.x, position.y)
                        self.SetConnectionWires(step.connectionPointIn, input_connector)
                    else:
                        step.deleteconnectionPointIn()
                    if len(value["outputs"]) > 0:
                        output_connector = value["outputs"][0]
                        position = output_connector.GetRelPosition()
                        step.addconnectionPointOut()
                        step.connectionPointOut.setrelPositionXY(position.x, position.y)
                    else:
                        step.deleteconnectionPointOut()
                elif param == "action":
                    if value:
                        position = value.GetRelPosition()
                        step.addconnectionPointOutAction()
                        step.connectionPointOutAction.setrelPositionXY(position.x, position.y)
                    else:
                        step.deleteconnectionPointOutAction()
    
    def AddEditedElementTransition(self, tagname, id):
        element = self.GetEditedElement(tagname)
        if element is not None:
            transition = PLCOpenParser.CreateElement("transition", "sfcObjects")
            transition.setlocalId(id)
            element.addinstance(transition)
    
    def SetEditedElementTransitionInfos(self, tagname, id, infos):
        element = self.GetEditedElement(tagname)
        if element is not None:
            transition = element.getinstance(id)
            if transition is None:
                return
            for param, value in infos.items():
                if param == "type" and value != "connection":
                    transition.setconditionContent(value, infos["condition"])
                elif param == "height":
                    transition.setheight(value)
                elif param == "width":
                    transition.setwidth(value)
                elif param == "x":
                    transition.setx(value)
                elif param == "y":
                    transition.sety(value)
                elif param == "priority":
                    if value != 0:
                        transition.setpriority(value)
                    else:
                        transition.setpriority(None)
                elif param == "connectors":
                    input_connector = value["inputs"][0]
                    position = input_connector.GetRelPosition()
                    transition.addconnectionPointIn()
                    transition.connectionPointIn.setrelPositionXY(position.x, position.y)
                    self.SetConnectionWires(transition.connectionPointIn, input_connector)
                    output_connector = value["outputs"][0]
                    position = output_connector.GetRelPosition()
                    transition.addconnectionPointOut()
                    transition.connectionPointOut.setrelPositionXY(position.x, position.y)
                elif infos.get("type", None) == "connection" and param == "connection" and value:
                    transition.setconditionContent("connection", None)
                    self.SetConnectionWires(transition.condition.content, value)
    
    def AddEditedElementDivergence(self, tagname, id, divergence_type):
        element = self.GetEditedElement(tagname)
        if element is not None:
            divergence = PLCOpenParser.CreateElement(
                {SELECTION_DIVERGENCE: "selectionDivergence",
                 SELECTION_CONVERGENCE: "selectionConvergence",
                 SIMULTANEOUS_DIVERGENCE: "simultaneousDivergence",
                 SIMULTANEOUS_CONVERGENCE: "simultaneousConvergence"}.get(
                    divergence_type), "sfcObjects")
            divergence.setlocalId(id)
            element.addinstance(divergence)
    
    DivergenceTypes = [
        (divergence_type, 
         PLCOpenParser.GetElementClass(divergence_type, "sfcObjects"))
        for divergence_type in ["selectionDivergence", "simultaneousDivergence",
                                "selectionConvergence", "simultaneousConvergence"]]
    
    def GetDivergenceType(self, divergence):
        for divergence_type, divergence_class in self.DivergenceTypes:
            if isinstance(divergence, divergence_class):
                return divergence_type
        return None
    
    def SetEditedElementDivergenceInfos(self, tagname, id, infos):
        element = self.GetEditedElement(tagname)
        if element is not None:
            divergence = element.getinstance(id)
            if divergence is None:
                return
            for param, value in infos.items():
                if param == "height":
                    divergence.setheight(value)
                elif param == "width":
                    divergence.setwidth(value)
                elif param == "x":
                    divergence.setx(value)
                elif param == "y":
                    divergence.sety(value)
                elif param == "connectors":
                    input_connectors = value["inputs"]
                    divergence_type = self.GetDivergenceType(divergence)
                    if divergence_type in ["selectionDivergence", "simultaneousDivergence"]:
                        position = input_connectors[0].GetRelPosition()
                        divergence.addconnectionPointIn()
                        divergence.connectionPointIn.setrelPositionXY(position.x, position.y)
                        self.SetConnectionWires(divergence.connectionPointIn, input_connectors[0])
                    else:
                        divergence.setconnectionPointIn([])
                        for input_connector in input_connectors:
                            position = input_connector.GetRelPosition()
                            connection = PLCOpenParser.CreateElement("connectionPointIn", divergence_type)
                            divergence.appendconnectionPointIn(connection)
                            connection.setrelPositionXY(position.x, position.y)
                            self.SetConnectionWires(connection, input_connector)
                    output_connectors = value["outputs"]
                    if divergence_type in ["selectionConvergence", "simultaneousConvergence"]:
                        position = output_connectors[0].GetRelPosition()
                        divergence.addconnectionPointOut()
                        divergence.connectionPointOut.setrelPositionXY(position.x, position.y)
                    else:
                        divergence.setconnectionPointOut([])
                        for output_connector in output_connectors:
                            position = output_connector.GetRelPosition()
                            connection = PLCOpenParser.CreateElement("connectionPointOut", divergence_type)
                            divergence.appendconnectionPointOut(connection)
                            connection.setrelPositionXY(position.x, position.y)
                            
    def AddEditedElementJump(self, tagname, id):
        element = self.GetEditedElement(tagname)
        if element is not None:
            jump = PLCOpenParser.CreateElement("jumpStep", "sfcObjects")
            jump.setlocalId(id)
            element.addinstance(jump)
    
    def SetEditedElementJumpInfos(self, tagname, id, infos):
        element = self.GetEditedElement(tagname)
        if element is not None:
            jump = element.getinstance(id)
            if jump is None:
                return
            for param, value in infos.items():
                if param == "target":
                    jump.settargetName(value)
                elif param == "height":
                    jump.setheight(value)
                elif param == "width":
                    jump.setwidth(value)
                elif param == "x":
                    jump.setx(value)
                elif param == "y":
                    jump.sety(value)
                elif param == "connector":
                    position = value.GetRelPosition()
                    jump.addconnectionPointIn()
                    jump.connectionPointIn.setrelPositionXY(position.x, position.y)
                    self.SetConnectionWires(jump.connectionPointIn, value)
 
    def AddEditedElementActionBlock(self, tagname, id):
        element = self.GetEditedElement(tagname)
        if element is not None:
            actionBlock = PLCOpenParser.CreateElement("actionBlock", "commonObjects")
            actionBlock.setlocalId(id)
            element.addinstance(actionBlock)
    
    def SetEditedElementActionBlockInfos(self, tagname, id, infos):
        element = self.GetEditedElement(tagname)
        if element is not None:
            actionBlock = element.getinstance(id)
            if actionBlock is None:
                return
            for param, value in infos.items():
                if param == "actions":
                    actionBlock.setactions(value)
                elif param == "height":
                    actionBlock.setheight(value)
                elif param == "width":
                    actionBlock.setwidth(value)
                elif param == "x":
                    actionBlock.setx(value)
                elif param == "y":
                    actionBlock.sety(value)
                elif param == "connector":
                    position = value.GetRelPosition()
                    actionBlock.addconnectionPointIn()
                    actionBlock.connectionPointIn.setrelPositionXY(position.x, position.y)
                    self.SetConnectionWires(actionBlock.connectionPointIn, value)
    
    def RemoveEditedElementInstance(self, tagname, id):
        element = self.GetEditedElement(tagname)
        if element is not None:
            instance = element.getinstance(id)
            if isinstance(instance, PLCOpenParser.GetElementClass("block", "fbdObjects")):
                self.RemoveEditedElementPouVar(tagname, instance.gettypeName(), instance.getinstanceName())
            element.removeinstance(id)
            self.Project.RefreshElementUsingTree()

    def GetEditedResourceVariables(self, tagname, debug = False):
        varlist = []
        words = tagname.split("::")
        for var in self.GetConfigurationGlobalVars(words[1], debug):
            if var["Type"] == "BOOL":
                varlist.append(var["Name"])
        for var in self.GetConfigurationResourceGlobalVars(words[1], words[2], debug):
            if var["Type"] == "BOOL":
                varlist.append(var["Name"])
        return varlist

    def SetEditedResourceInfos(self, tagname, tasks, instances):
        resource = self.GetEditedElement(tagname)
        if resource is not None:
            resource.settask([])
            resource.setpouInstance([])
            task_list = {}
            for task in tasks:
                new_task = plcopen.resource_task()
                new_task.setname(task["Name"])
                if task["Triggering"] == "Interrupt":
                    new_task.setsingle(task["Single"])
##                result = duration_model.match(task["Interval"]).groups()
##                if reduce(lambda x, y: x or y != None, result):
##                    values = []
##                    for value in result[:-1]:
##                        if value != None:
##                            values.append(int(value))
##                        else:
##                            values.append(0)
##                    if result[-1] is not None:
##                        values.append(int(float(result[-1]) * 1000))
##                    new_task.setinterval(datetime.time(*values))
                if task["Triggering"] == "Cyclic":
                    new_task.setinterval(task["Interval"])
                new_task.setpriority(int(task["Priority"]))
                if task["Name"] != "":
                    task_list[task["Name"]] = new_task
                resource.appendtask(new_task)
            for instance in instances:
                new_instance = plcopen.pouInstance()
                new_instance.setname(instance["Name"])
                new_instance.settypeName(instance["Type"])
                task_list.get(instance["Task"], resource).appendpouInstance(new_instance)

    def GetEditedResourceInfos(self, tagname, debug = False):
        resource = self.GetEditedElement(tagname, debug)
        if resource is not None:
            tasks = resource.gettask()
            instances = resource.getpouInstance()
            tasks_data = []
            instances_data = []
            for task in tasks:
                new_task = {}
                new_task["Name"] = task.getname()
                single = task.getsingle()
                if single is not None:
                    new_task["Single"] = single
                else:
                    new_task["Single"] = ""
                interval = task.getinterval()
                if interval is not None:
##                    text = ""
##                    if interval.hour != 0:
##                        text += "%dh"%interval.hour
##                    if interval.minute != 0:
##                        text += "%dm"%interval.minute
##                    if interval.second != 0:
##                        text += "%ds"%interval.second
##                    if interval.microsecond != 0:
##                        if interval.microsecond % 1000 != 0:
##                            text += "%.3fms"%(float(interval.microsecond) / 1000)
##                        else:
##                            text += "%dms"%(interval.microsecond / 1000)
##                    new_task["Interval"] = text
                    new_task["Interval"] = interval
                else:
                    new_task["Interval"] = ""
                if single is not None and interval is None:
                    new_task["Triggering"] = "Interrupt"
                elif interval is not None and single is None:
                    new_task["Triggering"] = "Cyclic"
                else:
                    new_task["Triggering"] = ""
                new_task["Priority"] = str(task.getpriority())
                tasks_data.append(new_task)
                for instance in task.getpouInstance():
                    new_instance = {}
                    new_instance["Name"] = instance.getname()
                    new_instance["Type"] = instance.gettypeName()
                    new_instance["Task"] = task.getname()
                    instances_data.append(new_instance)
            for instance in instances:
                new_instance = {}
                new_instance["Name"] = instance.getname()
                new_instance["Type"] = instance.gettypeName()
                new_instance["Task"] = ""
                instances_data.append(new_instance)
            return tasks_data, instances_data

    def OpenXMLFile(self, filepath):
        try:
            self.Project = LoadProject(filepath)
        except Exception, e:
            return _("Project file syntax error:\n\n") + str(e)
        self.SetFilePath(filepath)
        self.Project.RefreshElementUsingTree()
        self.Project.RefreshDataTypeHierarchy()
        self.Project.RefreshCustomBlockTypes()
        
        ## To remove when project buffering ready
        self.ProjectBufferEnabled = False
        
        self.CreateProjectBuffer(True)
        self.ProgramChunks = []
        self.ProgramOffset = 0
        self.NextCompiledProject = self.Project ## self.Copy(self.Project)
        self.CurrentCompiledProject = None
        self.Buffering = False
        self.CurrentElementEditing = None
        return None
        
    def SaveXMLFile(self, filepath = None):
        if not filepath and self.FilePath == "":
            return False
        else:
            contentheader = {"modificationDateTime": datetime.datetime(*localtime()[:6])}
            self.Project.setcontentHeader(contentheader)
            
            if filepath:
                SaveProject(self.Project, filepath)
            else:
                SaveProject(self.Project, self.FilePath)
            
            self.MarkProjectAsSaved()
            if filepath:
                self.SetFilePath(filepath)
            return True

#-------------------------------------------------------------------------------
#                       Search in Current Project Functions
#-------------------------------------------------------------------------------

    def SearchInProject(self, criteria):
        return self.Project.Search(criteria)

    def SearchInPou(self, tagname, criteria, debug=False):
        pou = self.GetEditedElement(tagname, debug)
        if pou is not None:
            return pou.Search(criteria)
        return []

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

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

    def CreateProjectBuffer(self, saved):
        if self.ProjectBufferEnabled:
            self.ProjectBuffer = UndoBuffer(cPickle.dumps(self.Project), saved)
        else:
            self.ProjectBuffer = None
            self.ProjectSaved = saved

    def IsProjectBufferEnabled(self):
        return self.ProjectBufferEnabled

    def EnableProjectBuffer(self, enable):
        self.ProjectBufferEnabled = enable
        if self.Project is not None:
            if enable:
                current_saved = self.ProjectSaved
            else:
                current_saved = self.ProjectBuffer.IsCurrentSaved()
            self.CreateProjectBuffer(current_saved)

    def BufferProject(self):
        if self.ProjectBuffer is not None:
            self.ProjectBuffer.Buffering(cPickle.dumps(self.Project))
        else:
            self.ProjectSaved = False

    def StartBuffering(self):
        if self.ProjectBuffer is not None:
            self.Buffering = True
        else:
            self.ProjectSaved = False
        
    def EndBuffering(self):
        if self.ProjectBuffer is not None and self.Buffering:
            self.ProjectBuffer.Buffering(cPickle.dumps(self.Project))
            self.Buffering = False

    def MarkProjectAsSaved(self):
        self.EndBuffering()
        if self.ProjectBuffer is not None:
            self.ProjectBuffer.CurrentSaved()
        else:
            self.ProjectSaved = True
    
    # Return if project is saved
    def ProjectIsSaved(self):
        if self.ProjectBuffer is not None:
            return self.ProjectBuffer.IsCurrentSaved() and not self.Buffering
        else:
            return self.ProjectSaved

    def LoadPrevious(self):
        self.EndBuffering()
        if self.ProjectBuffer is not None:
            self.Project = cPickle.loads(self.ProjectBuffer.Previous())
    
    def LoadNext(self):
        if self.ProjectBuffer is not None:
            self.Project = cPickle.loads(self.ProjectBuffer.Next())
    
    def GetBufferState(self):
        if self.ProjectBuffer is not None:
            first = self.ProjectBuffer.IsFirst() and not self.Buffering
            last = self.ProjectBuffer.IsLast()
            return not first, not last
        return False, False