PLCControler.py
author Edouard Tisserant <edouard.tisserant@gmail.com>
Wed, 04 Dec 2024 12:00:37 +0100
changeset 4059 03f007a175b5
parent 4008 f30573e98600
child 4063 6668dcbca915
permissions -rw-r--r--
IDE: Fix wrong red wire coloring in FBD
#!/usr/bin/env python
# -*- coding: utf-8 -*-

# This file is part of Beremiz, a Integrated Development Environment for
# programming IEC 61131-3 automates supporting plcopen standard and CanFestival.
#
# Copyright (C) 2007: Edouard TISSERANT and Laurent BESSARD
# Copyright (C) 2017: Andrey Skvortsov
#
# See COPYING file for copyrights details.
#
# This program 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
# of the License, or (at your option) any later version.
#
# This program 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 program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.


from copy import deepcopy
import os
import re
import datetime
from time import localtime
from functools import reduce

import util.paths as paths
from plcopen import *
from plcopen.types_enums import *
from plcopen.InstancesPathCollector import InstancesPathCollector
from plcopen.POUVariablesCollector import POUVariablesCollector
from plcopen.InstanceTagnameCollector import InstanceTagnameCollector
from plcopen.BlockInstanceCollector import BlockInstanceCollector
from plcopen.VariableInfoCollector import VariableInfoCollector
from graphics.GraphicCommons import *
from PLCGenerator import *

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

ScriptDirectory = paths.AbsDir(__file__)

# Length of the buffer
UNDO_BUFFER_LENGTH = 20


class UndoBuffer(object):
    """
    Undo Buffer for PLCOpenEditor
    Class implementing a buffer of changes made on the current editing model
    """

    def __init__(self, currentstate, issaved=False):
        """
        Constructor initialising buffer
        """
        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 range(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

    def Buffering(self, currentstate):
        """
        Add a new state in buffer
        """
        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)

    def Current(self):
        """
        Return current state of buffer
        """
        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


class PLCControler(object):
    """
    Controler for PLCOpenEditor
    Class which controls the operations made on the plcopen model and answers to view requests
    """

    # Create a new PLCControler
    def __init__(self):
        self.LastNewIndex = 0
        self.Reset()
        self.InstancesPathCollector = InstancesPathCollector(self)
        self.POUVariablesCollector = POUVariablesCollector(self)
        self.InstanceTagnameCollector = InstanceTagnameCollector(self)
        self.BlockInstanceCollector = BlockInstanceCollector(self)
        self.VariableInfoCollector = VariableInfoCollector(self)

    # 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 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("")

        # Initialize the project buffer
        self.CreateProjectBuffer(False)
        self.ProgramChunks = []
        self.ProgramOffset = 0
        self.NextCompiledProject = 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=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": 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": ComputePouName(pou.getname())}
                pou_values = []
                if pou.getbodyType() == "SFC":
                    transitions = []
                    for transition in pou.gettransitionList():
                        transitions.append({
                            "name": transition.getname(),
                            "type": ITEM_TRANSITION,
                            "tagname": 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": 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": 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": 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 GetPouVariables(self, tagname, debug=False):
        project = self.GetProject(debug)
        if project is not None:
            obj = None
            words = tagname.split("::")
            if words[0] == "P":
                obj = self.GetPou(words[1], debug)
            elif words[0] != "D":
                obj = self.GetEditedElement(tagname, debug)
            if obj is not None:
                return self.POUVariablesCollector.Collect(obj, debug)

        return None

    def GetInstanceList(self, root, name, debug=False):
        return self.InstancesPathCollector.Collect(root, name, debug)

    def SearchPouInstances(self, tagname, debug=False):
        project = self.GetProject(debug)
        if project is not None:
            words = tagname.split("::")
            if words[0] == "P":
                return self.GetInstanceList(project, words[1])
            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(
                            ComputePouName(words[1]), debug)]
        return []

    def GetPouInstanceTagName(self, instance_path, debug=False):
        project = self.GetProject(debug)
        if project is not None:
            return self.InstanceTagnameCollector.Collect(project,
                                                         debug,
                                                         instance_path)

    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 len(self.GetInstanceList(project, name, debug)) > 0
        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 len(self.GetInstanceList(project, name, debug)) > 0
        return False

    # Return if pou given by name is directly or undirectly used by the reference pou
    def PouIsUsedBy(self, name, reference, debug=False):
        pou_infos = self.GetPou(reference, debug)
        if pou_infos is not None:
            return len(self.GetInstanceList(pou_infos, name, debug)) > 0
        return False

    def GenerateProgram(self, filepath=None, **kwargs):
        errors = []
        warnings = []
        if self.Project is not None:
            try:
                self.ProgramChunks = GenerateCurrentProgram(self, self.Project, errors, warnings,**kwargs)
                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", encoding='utf-8')
                    programfile.write(program_text)
                    programfile.close()
                    self.ProgramFilePath = filepath
                return program_text, errors, warnings
            except PLCGenException as ex:
                errors.append(ex)
        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 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 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:
                new_pou = self.Copy(pou)
                idx = 0
                new_name = name + "_" + pou_type
                while self.Project.getpou(new_name) is not None:
                    idx += 1
                    new_name = "%s%d" % (name, idx)
                new_pou.setname(new_name)

                orig_type = pou.getpouType()
                if orig_type == 'function' and pou_type in ['functionBlock', 'program']:
                    # delete return type
                    return_type_obj = new_pou.interface.getreturnType()
                    new_pou.interface.remove(return_type_obj)
                    # To be ultimately correct we could re-create an
                    # output variable with same name+_out or so
                    # but in any case user will have to connect/assign
                    # this output, so better leave it as-is

                new_pou.setpouType(pou_type)
                self.Project.insertpou(0, new_pou)
                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.tostring()
        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:
            new_pou, error = LoadPou(pou_xml)
        except Exception:
            error = ""
        if error is not None:
            return _("Couldn't paste non-POU object.")

        name = new_pou.getname()

        idx = 0
        new_name = name
        while self.Project.getpou(new_name) is not None:
            # 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']:
                msg = _('''{a1} "{a2}" can't be pasted as a {a3}.''').format(a1=orig_type, a2=name, a3=pou_type)
                return msg

            new_pou.setpouType(pou_type)

        self.Project.insertpou(0, new_pou)
        self.BufferProject()

        return ComputePouName(new_name),

    # 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 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 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 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 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.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.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.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)
                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, tuple):
                if var.Type[0] == "array":
                    _array_type, base_type_name, dimensions = var.Type
                    array = PLCOpenParser.CreateElement("array", "dataType")
                    baseType = PLCOpenParser.CreateElement("baseType", "array")
                    array.setbaseType(baseType)
                    for i, dimension in enumerate(dimensions):
                        dimension_range = PLCOpenParser.CreateElement("dimension", "array")
                        if i == 0:
                            array.setdimension([dimension_range])
                        else:
                            array.appenddimension(dimension_range)
                        dimension_range.setlower(dimension[0])
                        dimension_range.setupper(dimension[1])
                    if base_type_name in self.GetBaseTypes():
                        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)
                        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.InitialValue != "":
                value = PLCOpenParser.CreateElement("initialValue", "variable")
                value.setvalue(var.InitialValue)
                tempvar.setinitialValue(value)
            if var.Location != "":
                tempvar.setaddress(var.Location)
            else:
                tempvar.setaddress(None)
            if var.Documentation != "":
                ft = PLCOpenParser.CreateElement("documentation", "variable")
                ft.setanyText(var.Documentation)
                tempvar.setdocumentation(ft)

            # Add variable to varList
            current_varlist.appendvariable(tempvar)
        return varlist_list

    def GetVariableDictionary(self, object_with_vars, tree=False, debug=False):
        variables = []
        self.VariableInfoCollector.Collect(object_with_vars,
                                           debug, variables, tree)
        return variables

    # Add a global var to configuration to configuration
    def AddConfigurationGlobalVar(self, config_name, var_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(
                    self.GetVarTypeObject(var_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):
        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 defined in configuration
                return self.GetVariableDictionary(configuration, debug)

        return []

    # 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):
        project = self.GetProject(debug)
        if project is not None:
            # Found the resource corresponding to name
            resource = project.getconfigurationResource(config_name, name)
            if resource is not None:
                # Extract variables defined in configuration
                return self.GetVariableDictionary(resource, debug)

        return []

    # 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

    # Return the interface for the given pou
    def GetPouInterfaceVars(self, pou, tree=False, debug=False):
        interface = pou.interface
        # Verify that the pou has an interface
        if interface is not None:
            # Extract variables defined in interface
            return self.GetVariableDictionary(interface, tree, debug)
        return []

    # 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)])

    # 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 return_type_obj is None:
                    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_obj.setcontent(derived_type)

    def UpdateProjectUsedPous(self, old_name, new_name):
        if self.Project is not None:
            self.Project.updateElementName(old_name, new_name)

    def UpdateEditedElementUsedVariable(self, tagname, old_name, new_name):
        pou = self.GetEditedElement(tagname)
        if pou is not None:
            pou.updateElementName(old_name, new_name)

    # Return the return type of the given pou
    def GetPouInterfaceReturnType(self, pou, tree=False, debug=False):
        # 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:
                factory = self.VariableInfoCollector.Collect(return_type,
                                                             debug, [], tree)
                if tree:
                    return [factory.GetType(), factory.GetTree()]
                return factory.GetType()

        if tree:
            return [None, ([], [])]
        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": [pou.getblockInfos()
                              for pou in confnodetypes["types"].getpous()]}
                    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 GetConfNodeDataTypes(self, exclude=None, only_locatables=False):
        return [{"name": _("%s Data Types") % confnodetypes["name"],
                 "list": [
                     datatype.getname()
                     for datatype in confnodetypes["types"].getdataTypes()
                     if not only_locatables or self.IsLocatableDataType(datatype)]}
                for confnodetypes in self.ConfNodeTypes]

    def GetVariableLocationTree(self):
        return []

    def GetConfNodeGlobalInstances(self):
        return []

    def GetConfigurationExtraVariables(self):
        global_vars = []
        for global_instance in self.GetConfNodeGlobalInstances():
            if type(global_instance)==tuple:
                # usual global without modifier from a CTN or a library
                var_name, var_type, var_initial = global_instance
                tempvar = PLCOpenParser.CreateElement("variable", "globalVars")
                tempvar.setname(var_name)

                tempvartype = PLCOpenParser.CreateElement("type", "variable")
                if var_type in self.GetBaseTypes():
                    tempvartype.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)
            else:
                # case of varlists from a TC6 library
                global_vars.append(global_instance)

        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 = {}
        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, [x[0] == "ANY" or self.IsOfType(*x) for x in zip(inputs, block_inputs)], True):
                    return blocktype
            else:
                if result_blocktype:
                    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:
            return result_blocktype
        project = self.GetProject(debug)
        if project is not None:
            blocktype = project.getpou(typename)
            if blocktype is not None:
                blocktype_infos = blocktype.getblockInfos()
                if inputs in [None, "undefined"]:
                    return blocktype_infos

                if inputs == tuple([var_type
                                    for _name, var_type, _modifier in blocktype_infos["inputs"]]):
                    return blocktype_infos

        return None

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

    # Return Function Block types checking for recursion
    def GetFunctionBlockTypes(self, tagname="", debug=False):
        project = self.GetProject(debug)
        words = tagname.split("::")
        name = None
        if project is not None and words[0] in ["P", "T", "A"]:
            name = words[1]
        blocktypes = []
        for blocks in self.TotalTypesDict.values():
            for _sectioname, block in blocks:
                if block["type"] == "functionBlock":
                    blocktypes.append(block["name"])
        if project is not None:
            blocktypes.extend([
                pou.getname()
                for pou in project.getpous(name, ["functionBlock"])
                if (name is None or
                    len(self.GetInstanceList(pou, name, debug)) == 0)])
        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(
                [pou.getname()
                 for pou in project.getpous(filter=["program"])])
        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.getname()
                for datatype in project.getdataTypes(name)
                if ((not only_locatables or self.IsLocatableDataType(datatype, debug)) and
                    (name is None or len(self.GetInstanceList(datatype, name, debug)) == 0))])
        if confnodetypes:
            for category in self.GetConfNodeDataTypes(name, only_locatables):
                datatypes.extend(category["list"])
        return datatypes

    # Return Data Type Object
    def GetPou(self, typename, debug=False):
        project = self.GetProject(debug)
        if project is not None:
            result = project.getpou(typename)
            if result is not None:
                return result
        for standardlibrary in list(StdBlckLibs.values()):
            result = standardlibrary.getpou(typename)
            if result is not None:
                return result
        for confnodetype in self.ConfNodeTypes:
            result = confnodetype["types"].getpou(typename)
            if result is not None:
                return result
        return None

    # Return Data Type Object
    def GetDataType(self, typename, debug=False):
        project = self.GetProject(debug)
        if project is not None:
            result = project.getdataType(typename)
            if result is not None:
                return result
        for confnodetype in self.ConfNodeTypes:
            result = confnodetype["types"].getdataType(typename)
            if result is not None:
                return result
        return None

    # Return Data Type Object Base Type
    def GetDataTypeBaseType(self, datatype):
        basetype_content = datatype.baseType.getcontent()
        basetype_content_type = basetype_content.getLocalTag()
        if basetype_content_type in ["array", "subrangeSigned", "subrangeUnsigned"]:
            basetype = basetype_content.baseType.getcontent()
            basetype_type = basetype.getLocalTag()
            return (basetype.getname() if basetype_type == "derived"
                    else basetype_type.upper())
        return (basetype_content.getname() if basetype_content_type == "derived"
                else basetype_content_type.upper())

    # Return Base Type of given possible derived type
    def GetBaseType(self, typename, debug=False):
        if typename in TypeHierarchy:
            return typename

        datatype = self.GetDataType(typename, debug)
        if datatype is not None:
            basetype = self.GetDataTypeBaseType(datatype)
            if basetype is not None:
                return self.GetBaseType(basetype, debug)
            return typename

        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, typename, reference, debug=False):
        if typename is None or reference is None:
            return True
        
        typename = typename.upper()
        reference = reference.upper()
        if typename == reference:
            return True

        basetype = TypeHierarchy.get(typename)
        if basetype is not None:
            return self.IsOfType(basetype, reference)

        datatype = self.GetDataType(typename, debug)
        if datatype is not None:
            basetype = self.GetDataTypeBaseType(datatype)
            if basetype is not None:
                return self.IsOfType(basetype, reference, debug)

        return False

    def IsEndType(self, typename):
        # Check if the type is a base type        
        if type(typename) == str:
            return not typename.startswith("ANY")
        return True

    def IsLocatableDataType(self, datatype, debug=False):
        basetype_content = datatype.baseType.getcontent()
        basetype_content_type = basetype_content.getLocalTag()
        if basetype_content_type in ["enum"]:
            return False
        elif basetype_content_type == "derived":
            return self.IsLocatableType(basetype_content.getname())
        elif basetype_content_type == "array":
            array_base_type = basetype_content.baseType.getcontent()
            if array_base_type.getLocalTag() == "derived":
                return self.IsLocatableType(array_base_type.getname(), debug)
        return True

    def IsLocatableType(self, typename, debug=False):
        if isinstance(typename, tuple) or self.GetBlockType(typename) is not None:
            return False

        # the size of these types is implementation dependend
        if typename in ["TIME", "DATE", "DT", "TOD"]:
            return False

        datatype = self.GetDataType(typename, debug)
        if datatype is not None:
            return self.IsLocatableDataType(datatype)
        return True

    def IsEnumeratedType(self, typename, debug=False):
        if isinstance(typename, tuple):
            typename = typename[1]
        datatype = self.GetDataType(typename, debug)
        if datatype is not None:
            basetype_content = datatype.baseType.getcontent()
            basetype_content_type = basetype_content.getLocalTag()
            if basetype_content_type == "derived":
                return self.IsEnumeratedType(basetype_content_type, debug)
            return basetype_content_type == "enum"
        return False

    def IsSubrangeType(self, typename, exclude=None, debug=False):
        if typename == exclude:
            return False
        if isinstance(typename, tuple):
            typename = typename[1]
        datatype = self.GetDataType(typename, debug)
        if datatype is not None:
            basetype_content = datatype.baseType.getcontent()
            basetype_content_type = basetype_content.getLocalTag()
            if basetype_content_type == "derived":
                return self.IsSubrangeType(basetype_content_type, exclude, debug)
            elif basetype_content_type in ["subrangeSigned", "subrangeUnsigned"]:
                return not self.IsOfType(
                    self.GetDataTypeBaseType(datatype), exclude)
        return False

    def IsNumType(self, typename, debug=False):
        return self.IsOfType(typename, "ANY_NUM", debug) or\
               self.IsOfType(typename, "ANY_BIT", debug)

    def GetDataTypeRange(self, typename, debug=False):
        range = DataTypeRange.get(typename)
        if range is not None:
            return range
        datatype = self.GetDataType(typename, debug)
        if datatype is not None:
            basetype_content = datatype.baseType.getcontent()
            basetype_content_type = basetype_content.getLocalTag()
            if basetype_content_type in ["subrangeSigned", "subrangeUnsigned"]:
                return (basetype_content.range.getlower(),
                        basetype_content.range.getupper())
            elif basetype_content_type == "derived":
                return self.GetDataTypeRange(basetype_content.getname(), debug)
        return None

    # Return Subrange types
    def GetSubrangeBaseTypes(self, exclude, debug=False):
        subrange_basetypes = list(DataTypeRange.keys())
        project = self.GetProject(debug)
        if project is not None:
            subrange_basetypes.extend(
                [datatype.getname() for datatype in project.getdataTypes()
                 if self.IsSubrangeType(datatype.getname(), exclude, debug)])
        for confnodetype in self.ConfNodeTypes:
            subrange_basetypes.extend(
                [datatype.getname() for datatype in confnodetype["types"].getdataTypes()
                 if self.IsSubrangeType(datatype.getname(), exclude, debug)])
        return subrange_basetypes

    # Return Enumerated Values
    def GetEnumeratedDataValues(self, typename=None, debug=False):
        values = []
        if typename is not None:
            datatype_obj = self.GetDataType(typename, debug)
            if datatype_obj is not None:
                basetype_content = datatype_obj.baseType.getcontent()
                basetype_content_type = basetype_content.getLocalTag()
                if basetype_content_type == "enum":
                    return [value.getname()
                            for value in basetype_content.xpath(
                                "ppx:values/ppx:value",
                                namespaces=PLCOpenParser.NSMAP)]
                elif basetype_content_type == "derived":
                    return self.GetEnumeratedDataValues(basetype_content.getname(), debug)
        else:
            project = self.GetProject(debug)
            if project is not None:
                values.extend(project.GetEnumeratedDataTypeValues())
            for confnodetype in self.ConfNodeTypes:
                values.extend(confnodetype["types"].GetEnumeratedDataTypeValues())
        return values

    # -------------------------------------------------------------------------------
    #                    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 = base_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"] = 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"] = 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":
                subrange = PLCOpenParser.CreateElement(
                    "subrangeUnsigned"
                    if infos["base_type"] in GetSubTypes("ANY_UINT")
                    else "subrangeSigned", "dataType")
                datatype.baseType.setcontent(subrange)
                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"], "dataType"))
                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")
                datatype.baseType.setcontent(enumerated)
                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)
            elif infos["type"] == "Array":
                array = PLCOpenParser.CreateElement("array", "dataType")
                datatype.baseType.setcontent(array)
                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)
            elif infos["type"] == "Structure":
                struct = PLCOpenParser.CreateElement("struct", "dataType")
                datatype.baseType.setcontent(struct)
                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"], tuple):
                        if element_infos["Type"][0] == "array":
                            _array_type, base_type_name, dimensions = element_infos["Type"]
                            array = PLCOpenParser.CreateElement("array", "dataType")
                            baseType = PLCOpenParser.CreateElement("baseType", "array")
                            array.setbaseType(baseType)
                            element_type.setcontent(array)
                            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():
                                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)
                    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)
            if infos["initial"] != "":
                if datatype.initialValue is None:
                    datatype.initialValue = PLCOpenParser.CreateElement("initialValue", "dataType")
                datatype.initialValue.setvalue(infos["initial"])
            else:
                datatype.initialValue = None
            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, tree=False, 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, tree, debug)
        return []

    # Return the edited element return type
    def GetEditedElementInterfaceReturnType(self, tagname, tree=False, 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, tree, debug)
        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)

    # 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:
            return element.tostring()
        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])
            copy_body = PLCOpenParser.CreateElement("body", "pou")
            element.append(copy_body)
            copy_body.setcontent(
                PLCOpenParser.CreateElement(element.getbodyType(), "body"))
            for id in blocks_id:
                instance = element.getinstance(id)
                if instance is not None:
                    copy_body.appendcontentInstance(self.Copy(instance))
                    instance_copy = copy_body.getcontentInstance(id)
                    instance_copy.filterConnections(wires)
                    text += instance_copy.tostring()
            element.remove(copy_body)
        return text

    def GenerateNewName(self, tagname, name, format, start_idx=0, exclude=None, debug=False):
        if name is not None:
            result = re.search(VARIABLE_NAME_SUFFIX_MODEL, name)
            if result is not None:
                format = name[:result.start(1)] + '%d'
                start_idx = int(result.group(1))
            else:
                format = name + '%d'

        names = {} if exclude is None else 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
            elif words[0] == 'R':
                element = self.GetEditedElement(tagname, debug)
                for task in element.gettask():
                    names[task.getname().upper()] = True
                    for instance in task.getpouInstance():
                        names[instance.getname().upper()] = True
                for instance in element.getpouInstance():
                    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=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

    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 = {}

            try:
                instances, error = LoadPouInstances(text, bodytype)
            except Exception:
                instances, error = [], ""
            if error is not None or len(instances) == 0:
                return _("Invalid plcopen element(s)!!!")

            exclude = {}
            for instance in instances:
                element.addinstance(instance)
                instance_type = instance.getLocalTag()
                if instance_type == "block":
                    blocktype = instance.gettypeName()
                    blocktype_infos = self.GetBlockType(blocktype)
                    blockname = instance.getinstanceName()
                    if blocktype_infos["type"] != "function" and blockname is not None:
                        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 instance_type == "step":
                    stepname = self.GenerateNewName(tagname,
                                                    instance.getname(),
                                                    "Step%d",
                                                    exclude=exclude,
                                                    debug=debug)
                    exclude[stepname] = True
                    instance.setname(stepname)
                localid = instance.getlocalId()
                if localid not in used_id:
                    new_id[localid] = True

            idx = 1
            translate_id = {}
            bbox = rect()
            for instance in instances:
                localId = instance.getlocalId()
                bbox.union(instance.getBoundingBox())
                if localId in used_id:
                    while (idx in used_id) or (idx in new_id):
                        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 = [x + 30 for x in new_pos]
            if scaling[0] != 0 and scaling[1] != 0:
                min_pos = [30 / x for x in 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 = (int(new_pos[0] - x), int(new_pos[1] - y))

            connections = {}
            for instance in instances:
                connections.update(instance.updateConnectionsId(translate_id))
                if getattr(instance, "setexecutionOrderId", None) is not None:
                    instance.setexecutionOrderId(0)
                instance.translate(*diff)

            return new_id, connections

    def GetEditedElementInstancesInfos(self, tagname, debug=False):
        element = self.GetEditedElement(tagname, debug)
        if element is not None:
            return self.BlockInstanceCollector.Collect(element, debug)
        return {}

    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()

    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 is not 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 GetVarTypeObject(self, var_type):
        var_type_obj = PLCOpenParser.CreateElement("type", "variable")
        if not var_type.startswith("ANY") and TypeHierarchy.get(var_type):
            var_type_obj.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_obj.setcontent(derived_type)
        return var_type_obj

    def AddEditedElementPouVar(self, tagname, var_type, name, **args):
        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(
                        self.GetVarTypeObject(var_type),
                        name, **args)

    def AddEditedElementPouExternalVar(self, tagname, var_type, name, **args):
        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(
                        self.GetVarTypeObject(var_type),
                        name, **args)

    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)

    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 list(infos.items()):
                if param == "name":
                    if value != "":
                        block.setinstanceName(value)
                    else:
                        block.attrib.pop("instanceName", None)
                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()

    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 list(infos.items()):
                if param == "name":
                    variable.setexpression(value)
                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 list(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 list(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 list(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 list(infos.items()):
                if param == "name":
                    contact.setvariable(value)
                elif param == "type":
                    negated, edge = {
                        CONTACT_NORMAL: (False, "none"),
                        CONTACT_REVERSE: (True, "none"),
                        CONTACT_RISING: (False, "rising"),
                        CONTACT_FALLING: (False, "falling")}[value]
                    contact.setnegated(negated)
                    contact.setedge(edge)
                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 list(infos.items()):
                if param == "name":
                    coil.setvariable(value)
                elif param == "type":
                    negated, storage, edge = {
                        COIL_NORMAL: (False, "none", "none"),
                        COIL_REVERSE: (True, "none", "none"),
                        COIL_SET: (False, "set", "none"),
                        COIL_RESET: (False, "reset", "none"),
                        COIL_RISING: (False, "none", "rising"),
                        COIL_FALLING: (False, "none", "falling")}[value]
                    coil.setnegated(negated)
                    coil.setstorage(storage)
                    coil.setedge(edge)
                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 list(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 list(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 list(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 list(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 list(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)

    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 = PLCOpenParser.CreateElement("task", "resource")
                resource.appendtask(new_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
            for instance in instances:
                task = task_list.get(instance["Task"])
                if task is not None:
                    new_instance = PLCOpenParser.CreateElement("pouInstance", "task")
                    task.appendpouInstance(new_instance)
                else:
                    new_instance = PLCOpenParser.CreateElement("pouInstance", "resource")
                    resource.appendpouInstance(new_instance)
                new_instance.setname(instance["Name"])
                new_instance.settypeName(instance["Type"])

    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):
        self.Project, error = LoadProject(filepath)
        if self.Project is None:
            return _("Project file syntax error:\n\n") + error
        self.SetFilePath(filepath)
        self.CreateProjectBuffer(True)
        self.ProgramChunks = []
        self.ProgramOffset = 0
        self.NextCompiledProject = self.Copy(self.Project)
        self.CurrentCompiledProject = None
        self.Buffering = False
        self.CurrentElementEditing = None
        return error

    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):
        project_matches = self.Project.Search(criteria)
        ctn_matches = self.CTNSearch(criteria)
        return project_matches + ctn_matches

    def SearchInPou(self, tagname, criteria, debug=False):
        pou = self.GetEditedElement(tagname, debug)
        if pou is not None:
            search_results = pou.Search(criteria, [tagname])
            if tagname.split("::")[0] in ['A', 'T']:
                parent_pou_tagname = "P::%s" % (tagname.split("::")[-2])
                parent_pou = self.GetEditedElement(parent_pou_tagname, debug)
                for infos, start, end, text in parent_pou.Search(criteria):
                    if infos[1] in ["var_local", "var_input", "var_output", "var_inout"]:
                        search_results.append((infos, start, end, text))
            return search_results
        return []

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

    def Copy(self, model):
        """Return a copy of the project"""
        return deepcopy(model)

    def CreateProjectBuffer(self, saved):
        if self.ProjectBufferEnabled:
            self.ProjectBuffer = UndoBuffer(PLCOpenParser.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(PLCOpenParser.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(PLCOpenParser.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 = PLCOpenParser.Loads(self.ProjectBuffer.Previous())

    def LoadNext(self):
        if self.ProjectBuffer is not None:
            self.Project = PLCOpenParser.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