PLCControler.py
author b.taylor@willowglen.ca
Fri, 18 Sep 2009 09:41:27 -0600
changeset 428 3b19c34bac04
parent 427 22d16c457d87
child 431 c1c92d068ac5
permissions -rw-r--r--
copy & paste for POUs
#!/usr/bin/env python
# -*- coding: utf-8 -*-

#This file is part of PLCOpenEditor, a library implementing an IEC 61131-3 editor
#based on the plcopen standard. 
#
#Copyright (C) 2007: Edouard TISSERANT and Laurent BESSARD
#
#See COPYING file for copyrights details.
#
#This library is free software; you can redistribute it and/or
#modify it under the terms of the GNU General Public
#License as published by the Free Software Foundation; either
#version 2.1 of the License, or (at your option) any later version.
#
#This library is distributed in the hope that it will be useful,
#but WITHOUT ANY WARRANTY; without even the implied warranty of
#MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
#General Public License for more details.
#
#You should have received a copy of the GNU General Public
#License along with this library; if not, write to the Free Software
#Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA

from xml.dom import minidom
from types import StringType, UnicodeType
import cPickle
import os,sys,re
import datetime
from time import localtime

from plcopen import plcopen
from plcopen.structures import *
from graphics.GraphicCommons import *
from PLCGenerator import *

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

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

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

VAR_CLASS_INFOS = {"Local" :    (plcopen.interface_localVars,    ITEM_VAR_LOCAL),
                   "Global" :   (plcopen.interface_globalVars,   ITEM_VAR_GLOBAL),
                   "External" : (plcopen.interface_externalVars, ITEM_VAR_EXTERNAL),
                   "Temp" :     (plcopen.interface_tempVars,     ITEM_VAR_TEMP),
                   "Input" :    (plcopen.interface_inputVars,    ITEM_VAR_INPUT),
                   "Output" :   (plcopen.interface_outputVars,   ITEM_VAR_OUTPUT),
                   "InOut" :    (plcopen.interface_inOutVars,    ITEM_VAR_INOUT)
                  }

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

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

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

# Length of the buffer
UNDO_BUFFER_LENGTH = 20

"""
Class implementing a buffer of changes made on the current editing model
"""
class UndoBuffer:

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

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


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

"""
Class which controls the operations made on the plcopen model and answers to view requests
"""
class PLCControler:
    
    # Create a new PLCControler
    def __init__(self):
        self.LastNewIndex = 0
        self.Reset()
    
    # Reset PLCControler internal variables
    def Reset(self):
        self.Project = None
        self.ProjectBufferEnabled = False
        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.PluginTypes = []
        self.ProgramFilePath = ""
        
    def GetQualifierTypes(self):
        return plcopen.QualifierList

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

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

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

    # Create a new project by replacing the current one
    def CreateNewProject(self, properties):
        # Create the project
        self.Project = plcopen.project()
        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 variables
    def GetProjectPouVariables(self, pou_name = None, debug = False):
        variables = []
        project = self.GetProject(debug)
        if project is not None:
            for pou in project.getpous():
                if pou_name is None or pou_name == pou.getname():
                    variables.extend([var["Name"] for var in self.GetPouInterfaceVars(pou, debug)])
                    for transition in pou.gettransitionList():
                        variables.append(transition.getname())
                    for action in pou.getactionList():
                        variables.append(action.getname())
        return variables
    
    # Return file path if project is an open file
    def GetFilePath(self):
        return self.FilePath
    
    # Return file path if project is an open file
    def GetProgramFilePath(self):
        return self.ProgramFilePath
    
    # Return file name and point out if file is up to date
    def GetFilename(self):
        if self.Project is not None:
            if self.ProjectIsSaved():
                return self.FileName
            else:
                return "~%s~"%self.FileName
        return ""
    
    # Change file path and save file name or create a default one if file path not defined
    def SetFilePath(self, filepath):
        self.FilePath = filepath
        if filepath == "":
            self.LastNewIndex += 1
            self.FileName = _("Unnamed%d")%self.LastNewIndex
        else:
            self.FileName = os.path.splitext(os.path.basename(filepath))[0]
    
    # Change project properties
    def SetProjectProperties(self, name = None, properties = None):
        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 name is not None or properties is not None:
                self.BufferProject()
            
    # Return project properties
    def GetProjectProperties(self, debug = False):
        project = self.GetProject(debug)
        if project is not None:
            properties = project.getfileHeader()
            properties.update(project.getcontentHeader())
            return properties
        return None
    
    # Return project informations
    def GetProjectInfos(self, debug = False):
        project = self.GetProject(debug)
        if project is not None:
            infos = {"name": project.getname(), "type": ITEM_PROJECT}
            datatypes = {"name": DATA_TYPES, "type": ITEM_DATATYPES, "values":[]}
            for datatype in project.getdataTypes():
                datatypes["values"].append({"name": datatype.getname(), "type": ITEM_DATATYPE, 
                    "tagname": self.ComputeDataTypeName(datatype.getname()), "values": []})
            pou_types = {"function": {"name": FUNCTIONS, "type": ITEM_FUNCTION, "values":[]},
                         "functionBlock": {"name": FUNCTION_BLOCKS, "type": ITEM_FUNCTIONBLOCK, "values":[]},
                         "program": {"name": PROGRAMS, "type": ITEM_PROGRAM, "values":[]}}
            for pou in project.getpous():
                pou_type = pou.getpouType()
                pou_infos = {"name": pou.getname(), "type": ITEM_POU,
                             "tagname": self.ComputePouName(pou.getname())}
                pou_values = []
                if pou.getbodyType() == "SFC":
                    transitions = []
                    for transition in pou.gettransitionList():
                        transitions.append({"name": transition.getname(), "type": ITEM_TRANSITION, 
                            "tagname": self.ComputePouTransitionName(pou.getname(), transition.getname()), 
                            "values": []})
                    pou_values.append({"name": TRANSITIONS, "type": ITEM_TRANSITIONS, "values": transitions})
                    actions = []
                    for action in pou.getactionList():
                        actions.append({"name": action.getname(), "type": ITEM_ACTION, 
                            "tagname": self.ComputePouActionName(pou.getname(), action.getname()), 
                            "values": []})
                    pou_values.append({"name": ACTIONS, "type": ITEM_ACTIONS, "values": actions})
                if pou_type in pou_types:
                    pou_infos["values"] = pou_values
                    pou_types[pou_type]["values"].append(pou_infos)
            configurations = {"name": CONFIGURATIONS, "type": ITEM_CONFIGURATIONS, "values": []}
            for config in project.getconfigurations():
                config_name = config.getname()
                config_infos = {"name": config_name, "type": ITEM_CONFIGURATION, 
                    "tagname": self.ComputeConfigurationName(config.getname()), 
                    "values": []}
                resources = {"name": RESOURCES, "type": ITEM_RESOURCES, "values": []}
                for resource in config.getresource():
                    resource_name = resource.getname()
                    resource_infos = {"name": resource_name, "type": ITEM_RESOURCE, 
                        "tagname": self.ComputeConfigurationResourceName(config.getname(), resource.getname()), 
                        "values": []}
                    resources["values"].append(resource_infos)
                config_infos["values"] = [resources]
                configurations["values"].append(config_infos)
            infos["values"] = [{"name": PROPERTIES, "type": ITEM_PROPERTIES, "values": []},
                               datatypes, pou_types["function"], pou_types["functionBlock"], 
                               pou_types["program"], configurations]
            return infos
        return None

    # Return project topology informations
    def GetProjectTopology(self, debug = False):
        project = self.GetProject(debug)
        if project is not None:
            infos = {"name": project.getname(), "type": ITEM_PROJECT, "values" : []}
            for config in project.getconfigurations():
                config_infos = {"name" : config.getname(), "type": ITEM_CONFIGURATION, "values" : []}
                for resource in config.getresource():
                    resource_infos = {"name" : resource.getname(), "type": ITEM_RESOURCE, "values": []}
                    for task in resource.gettask():
                        for pou in task.getpouInstance():
                            instance_infos = self.GetPouTopology(pou.getname(), pou.gettypeName(), debug=debug)
                            if instance_infos is not None:
                                resource_infos["values"].append(instance_infos)
                    for pou in resource.getpouInstance():
                        instance_infos = self.GetPouTopology(pou.getname(), pou.gettypeName(), debug=debug)
                        if instance_infos is not None:
                            resource_infos["values"].append(instance_infos)
                    for varlist in resource.getglobalVars():
                        for variable in varlist.getvariable():
                            vartype_content = variable.gettype().getcontent()
                            if vartype_content["name"] == "derived":
                                var_infos = self.GetPouTopology(variable.getname(), vartype_content["value"].getname(), True, debug)
                                if var_infos is not None:
                                    resource_infos["values"].append(var_infos)
                            elif vartype_content["name"] in ["string", "wstring"]:
                                resource_infos["values"].append({"name" : variable.getname(), 
                                                                 "elmt_type" : vartype_content["name"].upper(),
                                                                 "type" : ITEM_VAR_GLOBAL, "values" : []})
                            else:
                                resource_infos["values"].append({"name" : variable.getname(), 
                                                                 "elmt_type" : vartype_content["name"], 
                                                                 "type" : ITEM_VAR_GLOBAL, "values" : []})
                    config_infos["values"].append(resource_infos)
                for varlist in config.getglobalVars():
                    for variable in varlist.getvariable():
                        vartype_content = variable.gettype().getcontent()
                        if vartype_content["name"] == "derived":
                            var_infos = self.GetPouTopology(variable.getname(), vartype_content["value"].getname(), True, debug)
                            if var_infos is not None:
                                config_infos["values"].append(var_infos)
                        elif vartype_content["name"] in ["string", "wstring"]:
                            config_infos["values"].append({"name" : variable.getname(), 
                                                           "elmt_type" : vartype_content["name"].upper(), 
                                                           "type" : ITEM_VAR_GLOBAL, "values" : []})
                        else:
                            config_infos["values"].append({"name" : variable.getname(),
                                                           "elmt_type" : vartype_content["name"], 
                                                           "type" : ITEM_VAR_GLOBAL, "values" : []})
                infos["values"].append(config_infos)
            return infos
        return None
    
    # Return pou topology informations
    def GetPouTopology(self, name, type, global_var = False, debug = False):
        project = self.GetProject(debug)
        if project is not None:
            pou = project.getpou(type)
            if pou is not None:
                pou_type = pou.getpouType()
                if pou_type == "function":
                    return None
                elif pou_type == "program":
                    pou_infos = {"name" : name, "elmt_type" : type, "type" : ITEM_PROGRAM, 
                                 "tagname" : self.ComputePouName(pou.getname()), "values" : []}
                else:
                    pou_infos = {"name" : name, "elmt_type" : type, "type" : ITEM_FUNCTIONBLOCK, 
                                 "tagname" : self.ComputePouName(pou.getname()), "values" : []}
                if pou.getbodyType() == "SFC":
                    for transition in pou.gettransitionList():
                        pou_infos["values"].append({"name" : transition.getname(), 
                            "elmt_type" : "TRANSITION", "type" : ITEM_TRANSITION, 
                            "tagname" : self.ComputePouActionName(pou.getname(), transition.getname()),
                            "values" : []})
                    for action in pou.getactionList():
                        pou_infos["values"].append({"name": action.getname(), 
                            "elmt_type" : "ACTION", "type": ITEM_ACTION, 
                            "tagname" : self.ComputePouActionName(pou.getname(), action.getname()),
                            "values" : []})
                if pou.interface:
                    # Extract variables from every varLists
                    for type, varlist in pou.getvars():
                        infos = VAR_CLASS_INFOS.get(type, None)
                        if infos is not None:
                            current_var_class = infos[1]
                        else:
                            current_var_class = ITEM_VAR_LOCAL
                        for variable in varlist.getvariable():
                            vartype_content = variable.gettype().getcontent()
                            if vartype_content["name"] == "derived":
                                var_infos = self.GetPouTopology(variable.getname(), vartype_content["value"].getname())
                                if var_infos is not None:
                                    pou_infos["values"].append(var_infos)
                            elif vartype_content["name"] in ["string", "wstring"]:
                                pou_infos["values"].append({"name" : variable.getname(), 
                                                            "elmt_type" : vartype_content["name"].upper(), 
                                                            "type" : current_var_class, "values" : []})
                            else:
                                pou_infos["values"].append({"name" : variable.getname(), 
                                                            "elmt_type" : vartype_content["name"], 
                                                            "type" : current_var_class, "values" : []})
                return pou_infos
            block_infos = self.GetBlockType(type, debug = debug)
            if block_infos is not None:
                if block_infos["type"] == "function":
                    return None
                elif block_infos["type"] == "program":
                    pou_infos = {"name" : name, "elmt_type" : type, "type" : ITEM_PROGRAM, "values" : []}
                else:
                    pou_infos = {"name" : name, "elmt_type" : type, "type" : ITEM_FUNCTIONBLOCK, "values" : []}
                for varname, vartype, varmodifier in block_infos["inputs"]:
                    pou_infos["values"].append({"name" : varname, "elmt_type" : vartype, "type" : ITEM_VAR_INPUT, "values" : []})
                for varname, vartype, varmodifier in block_infos["outputs"]:
                    pou_infos["values"].append({"name" : varname, "elmt_type" : vartype, "type" : ITEM_VAR_INPUT, "values" : []})
                return pou_infos
            
            if type in self.GetDataTypes(debug = debug):
                if global_var:
                    return {"name" : name, "elmt_type" : type, "type" : ITEM_VAR_GLOBAL, "values" : []}
                else:
                    return {"name" : name, "elmt_type" : type, "type" : ITEM_VAR_LOCAL, "values" : []}
        return None

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

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

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

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

    def ProgramTransferred(self):
        self.CurrentCompiledProject = self.NextCompiledProject

    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):
        if self.Project is not None:
            # Add the datatype to project
            self.Project.appenddataType(datatype_name)
            self.BufferProject()
        
    # 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()
    
    def ProjectChangePouType(self, name, pou_type):
        if self.Project is not None:
            pou = self.Project.getpou(name)
            if pou is not None:
                pou.setpouType(pou_type)
                self.BufferProject()
                
    def ProjectCreatePouFrom(self, name, from_name):
        if self.Project is not None:
            pou = self.Project.getpou(from_name)
            if pou is not None:
                new_pou = self.Copy(pou)
                new_pou.setname(name)
                self.Project.insertpou(-1, new_pou)
                self.BufferProject()
    
    def PastePou(self, pou_type, pou_xml):
        '''
        Adds the POU defined by 'pou_xml' to the current project with type 'pou_type'
        '''
        try:
            tree = minidom.parseString(pou_xml)
            root = tree.childNodes[0]
        except:
            return _("Couldn't paste non-POU object.")

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

            name = new_pou.getname()
            orig_type = new_pou.getpouType()

            # prevent violations of POU content restrictions:
            # function blocks cannot be pasted as functions,
            # programs cannot be pasted as functions or function blocks
            if orig_type == 'functionBlock' and pou_type == 'function' or \
            orig_type == 'program' and pou_type in ['function', 'functionBlock']:
                return _('''%s "%s" can't be pasted as a %s.''') % (orig_type, name, pou_type)
 
            while self.Project.getpou(name):
                # a POU with that name already exists.
                # make a new name and test if a POU with that name exists.

                # append an incrementing numeric suffix to the POU name. it
                # doesn't count up perfectly, but as long as it's unique who cares?
                if name[-1] >= '0' and name[-1] <= '9':
                    last_digit = int(name[-1])
                    name = name[0:-1] + str(last_digit+1)
                else:
                    name = name + '1'

            # we've found a name that does not already exist, use it
            new_pou.setname(name)
            new_pou.setpouType(pou_type)

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

    # Remove a Pou from project
    def ProjectRemovePou(self, pou_name):
        if self.Project is not None:
            self.Project.removepou(pou_name)
            self.BufferProject()
    
    # Add a configuration to Project
    def ProjectAddConfiguration(self, config_name):
        if self.Project is not None:
            self.Project.addconfiguration(config_name)
            self.BufferProject()
    
    # 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):
        if self.Project is not None:
            self.Project.addconfigurationResource(config_name, resource_name)
            self.BufferProject()
    
    # 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()
    
    # 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()
    
    # Remove an Action from a Project Pou
    def ProjectRemovePouAction(self, pou_name, action_name):
        # Search if the pou removed is currently opened
        if self.Project is not None:
            pou = self.Project.getpou(pou_name)
            if pou is not None:
                pou.removeaction(action_name)
                self.BufferProject()
    
    # Change the name of a pou
    def ChangeDataTypeName(self, old_name, new_name):
        if self.Project is not None:
            # Found the pou corresponding to old name and change its name to new name
            datatype = self.Project.getdataType(old_name)
            if datatype is not None:
                datatype.setname(new_name)
                self.Project.updateElementName(old_name, new_name)
                self.Project.RefreshElementUsingTree()
                self.Project.RefreshDataTypeHierarchy()
                self.BufferProject()
    
    # Change the name of a pou
    def ChangePouName(self, old_name, new_name):
        if self.Project is not None:
            # Found the pou corresponding to old name and change its name to new name
            pou = self.Project.getpou(old_name)
            if pou is not None:
                pou.setname(new_name)
                self.Project.updateElementName(old_name, new_name)
                self.Project.RefreshElementUsingTree()
                self.Project.RefreshCustomBlockTypes()
                self.BufferProject()
    
    # Change the name of a pou transition
    def ChangePouTransitionName(self, pou_name, old_name, new_name):
        if self.Project is not None:
            # Found the pou transition corresponding to old name and change its name to new name
            pou = self.Project.getpou(pou_name)
            if pou is not None:
                transition = pou.gettransition(old_name)
                if transition is not None:
                    transition.setname(new_name)
                    pou.updateElementName(old_name, new_name)
                    self.BufferProject()
    
    # Change the name of a pou action
    def ChangePouActionName(self, pou_name, old_name, new_name):
        if self.Project is not None:
            # Found the pou action corresponding to old name and change its name to new name
            pou = self.Project.getpou(pou_name)
            if pou is not None:
                action = pou.getaction(old_name)
                if action is not None:
                    action.setname(new_name)
                    pou.updateElementName(old_name, new_name)
                    self.BufferProject()
    
    # Change the name of a pou variable
    def ChangePouVariableName(self, pou_name, old_name, new_name):
        if self.Project is not None:
            # Found the pou action corresponding to old name and change its name to new name
            pou = self.Project.getpou(pou_name)
            if pou is not None:
                for type, varlist in pou.getvars():
                    for var in varlist.getvariable():
                        if var.getname() == old_name:
                            var.setname(new_name)
                self.Project.RefreshCustomBlockTypes()
                self.BufferProject()
        
    # Change the name of a configuration
    def ChangeConfigurationName(self, old_name, new_name):
        if self.Project is not None:
            # Found the configuration corresponding to old name and change its name to new name
            configuration = self.Project.getconfiguration(old_name)
            if configuration is not None:
                configuration.setname(new_name)
                self.BufferProject()
    
    # Change the name of a configuration resource
    def ChangeConfigurationResourceName(self, config_name, old_name, new_name):
        if self.Project is not None:
            # Found the resource corresponding to old name and change its name to new name
            resource = self.Project.getconfigurationResource(config_name, old_name)
            if resource is not None:
                resource.setname(new_name)
                self.BufferProject()
    
    # Return the 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["Retain"], 
                                var["Constant"], 
                                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 = infos[0]()
                else:
                    current_varlist = plcopen.varList()
                varlist_list.append((var["Class"], current_varlist))
                if var["Retain"] == "Yes":
                    current_varlist.setretain(True)
                if var["Constant"] == "Yes":
                    current_varlist.setconstant(True)
            # Create variable and change its properties
            tempvar = plcopen.varListPlain_variable()
            tempvar.setname(var["Name"])

            var_type = plcopen.dataType()
            if var["Type"] in self.GetBaseTypes():
                if var["Type"] == "STRING":
                    var_type.setcontent({"name" : "string", "value" : plcopen.elementaryTypes_string()})
                elif var["Type"] == "WSTRING":
                    var_type.setcontent({"name" : "wstring", "value" : plcopen.elementaryTypes_wstring()})
                else:
                    var_type.setcontent({"name" : var["Type"], "value" : None})
            else:
                derived_type = plcopen.derivedTypes_derived()
                derived_type.setname(var["Type"])
                var_type.setcontent({"name" : "derived", "value" : derived_type})
            tempvar.settype(var_type)

            if var["Initial Value"] != "":
                value = plcopen.value()
                value.setvalue(var["Initial Value"])
                tempvar.setinitialValue(value)
            if var["Location"] != "":
                tempvar.setaddress(var["Location"])
            else:
                tempvar.setaddress(None)
            if var['Documentation'] != "":
                ft = plcopen.formattedText()
                ft.settext(var['Documentation'])
                tempvar.setdocumentation(ft)

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

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

        vartype_content = var.gettype().getcontent()
        if vartype_content["name"] == "derived":
            tempvar["Type"] = vartype_content["value"].getname()
        elif vartype_content["name"] in ["string", "wstring"]:
            tempvar["Type"] = vartype_content["name"].upper()
        else:
            tempvar["Type"] = vartype_content["name"]

        tempvar["Edit"] = True

        initial = var.getinitialValue()
        if initial:
            tempvar["Initial Value"] = initial.getvalue()
        else:
            tempvar["Initial Value"] = ""

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

        if varlist.getretain():
            tempvar["Retain"] = "Yes"
        else:
            tempvar["Retain"] = "No"

        if varlist.getconstant():
            tempvar["Constant"] = "Yes"
        else:
            tempvar["Constant"] = "No"

        doc = var.getdocumentation()
        if doc:
            tempvar["Documentation"] = doc.gettext()
        else:
            tempvar["Documentation"] = ""

        return tempvar

    # 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([])
                for vartype, varlist in self.ExtractVarLists(vars):
                    configuration.globalVars.append(varlist)
    
    # Return the configuration globalvars
    def GetConfigurationGlobalVars(self, name, debug = False):
        vars = []
        project = self.GetProject(debug)
        if project is not None:
            # Found the configuration corresponding to name
            configuration = project.getconfiguration(name)
            if configuration is not None:
                # Extract variables from every varLists
                for varlist in configuration.getglobalVars():
                    for var in varlist.getvariable():
                        tempvar = self.GetVariableDictionary(varlist, var)
                        tempvar["Class"] = "Global"
                        vars.append(tempvar)
        return vars

    # 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([])
                for vartype, varlist in self.ExtractVarLists(vars):
                    resource.globalVars.append(varlist)
    
    # Return the resource globalvars
    def GetConfigurationResourceGlobalVars(self, config_name, name, debug = False):
        vars = []
        project = self.GetProject(debug)
        if project is not None:
            # Found the resource corresponding to name
            resource = project.getconfigurationResource(config_name, name)
            if resource:
                # Extract variables from every varLists
                for varlist in resource.getglobalVars():
                    for var in varlist.getvariable():
                        tempvar = self.GetVariableDictionary(varlist, var)
                        tempvar["Class"] = "Global"
                        vars.append(tempvar)
        return vars
    
    # Recursively generate element name tree for a structured variable
    def GenerateVarTree(self, typename, debug = False):
        project = self.GetProject(debug)
        if project is not None:
            blocktype = self.GetBlockType(typename, debug = debug)
            if blocktype is not None:
                tree = []
                for var_name, var_type, var_modifier in blocktype["inputs"] + blocktype["outputs"]:
                    tree.append((var_name, var_type, self.GenerateVarTree(var_type, debug)))
                return tree, []
            datatype = project.getdataType(typename)
            if datatype is not None:
                tree = []
                basetype_content = datatype.baseType.getcontent()
                if basetype_content["name"] == "derived":
                    return self.GenerateVarTree(basetype_content["value"].getname())
                elif basetype_content["name"] == "array":
                    dimensions = []
                    base_type = basetype_content["value"].baseType.getcontent()
                    if base_type["name"] == "derived":
                        tree = self.GenerateVarTree(base_type["value"].getname())
                        if len(tree[1]) == 0:
                            tree = tree[0]
                        for dimension in basetype_content["value"].getdimension():
                            dimensions.append((dimension.getlower(), dimension.getupper()))
                    return tree, dimensions
                elif basetype_content["name"] == "struct":
                    for element in basetype_content["value"].getvariable():
                        element_type = element.type.getcontent()
                        if element_type["name"] == "derived":
                            tree.append((element.getname(), element_type["value"].getname(), self.GenerateVarTree(element_type["value"].getname())))
                        else:
                            tree.append((element.getname(), element_type["name"], ([], [])))
                    return tree, []
        return [], []

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

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

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

                    vars.append(tempvar)
        return vars

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

    # Function that add a new plugin to the plugin list
    def AddPluginBlockList(self, blocklist):
        self.PluginTypes.extend(blocklist)
        
    # Function that clear the plugin list
    def ClearPluginTypes(self):
        for i in xrange(len(self.PluginTypes)):
            self.PluginTypes.pop(0)

    # Function that returns the block definition associated to the block type given
    def GetBlockType(self, type, inputs = None, debug = False):
        for category in BlockTypes + self.PluginTypes:
            for blocktype in category["list"]:
                if inputs:
                    block_inputs = tuple([var_type for name, var_type, modifier in blocktype["inputs"]])
                    same_inputs = inputs == block_inputs
                else:
                    same_inputs = True
                if blocktype["name"] == type and same_inputs:
                    return blocktype
        project = self.GetProject(debug)
        if project is not None:
            return project.GetCustomBlockType(type, inputs)
        return None

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

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

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

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

    # Return Base Type of given possible derived type
    def GetBaseType(self, type, debug = False):
        project = self.GetProject(debug)
        if project is not None:
            return project.GetBaseType(type)
        return None

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

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

    def GetDataTypeRange(self, type, debug = False):
        project = self.GetProject(debug)
        if project is not None:
            return project.GetDataTypeRange(type)
        elif type in DataTypeRange:
            return DataTypeRange[type]
        return None
    
    # Return Subrange types
    def GetSubrangeBaseTypes(self, exclude, debug = False):
        project = self.GetProject(debug)
        if project is not None:
            return project.GetSubrangeBaseTypes(exclude)
        return []
    
    # Return Enumerated Values
    def GetEnumeratedDataValues(self, debug = False):
        project = self.GetProject(debug)
        if project is not None:
            return project.GetEnumeratedDataTypeValues()
        return []

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

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

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

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

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

    # Return the data type informations
    def GetDataTypeInfos(self, tagname, debug = False):
        project = self.GetProject(debug)
        if project is not None:
            words = tagname.split("::")
            if words[0] == "D":
                infos = {}
                datatype = project.getdataType(words[1])
                basetype_content = datatype.baseType.getcontent()
                if basetype_content["value"] is None:
                    infos["type"] = "Directly"
                    infos["base_type"] = basetype_content["name"]
                elif basetype_content["name"] == "derived":
                    infos["type"] = "Directly"
                    infos["base_type"] = basetype_content["value"].getname()
                elif basetype_content["name"] in ["subrangeSigned", "subrangeUnsigned"]:
                    infos["type"] = "Subrange"
                    infos["min"] = basetype_content["value"].range.getlower()
                    infos["max"] = basetype_content["value"].range.getupper()
                    base_type = basetype_content["value"].baseType.getcontent()
                    if base_type["value"] is None:
                        infos["base_type"] = base_type["name"]
                    else:
                        infos["base_type"] = base_type["value"].getname()
                elif basetype_content["name"] == "enum":
                    infos["type"] = "Enumerated"
                    infos["values"] = []
                    for value in basetype_content["value"].values.getvalue():
                        infos["values"].append(value.getname())
                elif basetype_content["name"] == "array":
                    infos["type"] = "Array"
                    infos["dimensions"] = []
                    for dimension in basetype_content["value"].getdimension():
                        infos["dimensions"].append((dimension.getlower(), dimension.getupper()))
                    base_type = basetype_content["value"].baseType.getcontent()
                    if base_type["value"] is None:
                        infos["base_type"] = base_type["name"]
                    else:
                        infos["base_type"] = base_type["value"].getname()
                elif basetype_content["name"] == "struct":
                    infos["type"] = "Structure"
                    infos["elements"] = []
                    for element in basetype_content["value"].getvariable():
                        element_infos = {}
                        element_infos["Name"] = element.getname()
                        element_type = element.type.getcontent()
                        if element_type["value"] is None:
                            element_infos["Type"] = element_type["name"]
                        else:
                            element_infos["Type"] = element_type["value"].getname()
                        if element.initialValue is not None:
                            element_infos["Initial Value"] = str(element.initialValue.getvalue())
                        else:
                            element_infos["Initial Value"] = ""
                        infos["elements"].append(element_infos)
                if datatype.initialValue is not None:
                    infos["initial"] = str(datatype.initialValue.getvalue())
                else:
                    infos["initial"] = ""
                return infos
        return None
    
    # Change the data type informations
    def SetDataTypeInfos(self, tagname, infos):
        words = tagname.split("::")
        if self.Project is not None and words[0] == "D":
            datatype = self.Project.getdataType(words[1])
            if infos["type"] == "Directly":
                if infos["base_type"] in self.GetBaseTypes():
                    if infos["base_type"] == "STRING":
                        datatype.baseType.setcontent({"name" : "string", "value" : plcopen.elementaryTypes_string()})
                    elif infos["base_type"] == "WSTRING":
                        datatype.baseType.setcontent({"name" : "wstring", "value" : plcopen.elementaryTypes_wstring()})
                    else:
                        datatype.baseType.setcontent({"name" : infos["base_type"], "value" : None})
                else:
                    derived_datatype = plcopen.derivedTypes_derived()
                    derived_datatype.setname(infos["base_type"])
                    datatype.baseType.setcontent({"name" : "derived", "value" : derived_datatype})
            elif infos["type"] == "Subrange":
                if infos["base_type"] in GetSubTypes("ANY_UINT"):
                    subrange = plcopen.derivedTypes_subrangeUnsigned()
                    datatype.baseType.setcontent({"name" : "subrangeUnsigned", "value" : subrange})
                else:
                    subrange = plcopen.derivedTypes_subrangeSigned()
                    datatype.baseType.setcontent({"name" : "subrangeSigned", "value" : subrange})
                subrange.range.setlower(infos["min"])
                subrange.range.setupper(infos["max"])
                if infos["base_type"] in self.GetBaseTypes():
                    subrange.baseType.setcontent({"name" : infos["base_type"], "value" : None})
                else:
                    derived_datatype = plcopen.derivedTypes_derived()
                    derived_datatype.setname(infos["base_type"])
                    subrange.baseType.setcontent({"name" : "derived", "value" : derived_datatype})
            elif infos["type"] == "Enumerated":
                enumerated = plcopen.derivedTypes_enum()
                for i, enum_value in enumerate(infos["values"]):
                    value = plcopen.values_value()
                    value.setname(enum_value)
                    if i == 0:
                        enumerated.values.setvalue([value])
                    else:
                        enumerated.values.appendvalue(value)
                datatype.baseType.setcontent({"name" : "enum", "value" : enumerated})
            elif infos["type"] == "Array":
                array = plcopen.derivedTypes_array()
                for i, dimension in enumerate(infos["dimensions"]):
                    dimension_range = plcopen.rangeSigned()
                    dimension_range.setlower(dimension[0])
                    dimension_range.setupper(dimension[1])
                    if i == 0:
                        array.setdimension([dimension_range])
                    else:
                        array.appenddimension(dimension_range)
                if infos["base_type"] in self.GetBaseTypes():
                    if infos["base_type"] == "STRING":
                        array.baseType.setcontent({"name" : "string", "value" : plcopen.elementaryTypes_string()})
                    elif infos["base_type"] == "WSTRING":
                        array.baseType.setcontent({"name" : "wstring", "value" : plcopen.wstring()})
                    else:
                        array.baseType.setcontent({"name" : infos["base_type"], "value" : None})
                else:
                    derived_datatype = plcopen.derivedTypes_derived()
                    derived_datatype.setname(infos["base_type"])
                    array.baseType.setcontent({"name" : "derived", "value" : derived_datatype})
                datatype.baseType.setcontent({"name" : "array", "value" : array})
            elif infos["type"] == "Structure":
                struct = plcopen.varListPlain()
                for i, element_infos in enumerate(infos["elements"]):
                    element = plcopen.varListPlain_variable()
                    element.setname(element_infos["Name"])
                    if element_infos["Type"] in self.GetBaseTypes():
                        if element_infos["Type"] == "STRING":
                            element.type.setcontent({"name" : "string", "value" : plcopen.elementaryTypes_string()})
                        elif element_infos["Type"] == "WSTRING":
                            element.type.setcontent({"name" : "wstring", "value" : plcopen.wstring()})
                        else:
                            element.type.setcontent({"name" : element_infos["Type"], "value" : None})
                    else:
                        derived_datatype = plcopen.derivedTypes_derived()
                        derived_datatype.setname(element_infos["Type"])
                        element.type.setcontent({"name" : "derived", "value" : derived_datatype})
                    if element_infos["Initial Value"] != "":
                        value = plcopen.value()
                        value.setvalue(element_infos["Initial Value"])
                        element.setinitialValue(value)
                    if i == 0:
                        struct.setvariable([element])
                    else:
                        struct.appendvariable(element)
                datatype.baseType.setcontent({"name" : "struct", "value" : struct})
            if infos["initial"] != "":
                if datatype.initialValue is None:
                    datatype.initialValue = plcopen.value()
                datatype.initialValue.setvalue(infos["initial"])
            else:
                datatype.initialValue = None
            self.Project.RefreshDataTypeHierarchy()
            self.Project.RefreshElementUsingTree()
            self.BufferProject()
    
#-------------------------------------------------------------------------------
#                       Project opened Pous management functions
#-------------------------------------------------------------------------------

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

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

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

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

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

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

    # Return the names of the pou elements
    def GetEditedElementVariables(self, tagname, debug = False):
        words = tagname.split("::")
        if words[0] in ["P","T","A"]:
            return self.GetProjectPouVariables(words[1], debug)
        return []

    def GetEditedElementCopy(self, tagname, debug = False):
        element = self.GetEditedElement(tagname, debug)
        if element is not None:
            name = element.__class__.__name__
            return element.generateXMLText(name.split("_")[-1], 0)
        return ""
        
    def GetEditedElementInstancesCopy(self, tagname, blocks_id = None, wires = None, debug = False):
        element = self.GetEditedElement(tagname, debug)
        text = ""
        if element is not None:
            wires = dict([(wire, True) for wire in wires if wire[0] in blocks_id and wire[1] in blocks_id])
            for id in blocks_id:
                instance = element.getinstance(id)
                if instance is not None:
                    instance_copy = self.Copy(instance)
                    instance_copy.filterConnections(wires)
                    name = instance_copy.__class__.__name__
                    text += instance_copy.generateXMLText(name.split("_")[-1], 0)
        return text
    
    def GenerateNewName(self, tagname, name, format, exclude={}, debug=False):
        names = exclude.copy()
        names.update(dict([(varname.upper(), True) for varname in self.GetEditedElementVariables(tagname, debug)]))
        element = self.GetEditedElement(tagname, debug)
        if element is not None:
            for instance in element.getinstances():
                if isinstance(instance, (plcopen.sfcObjects_step, plcopen.commonObjects_connector, plcopen.commonObjects_continuation)):
                    names[instance.getname()] = True
        i = 1
        while names.get(name.upper(), False):
            name = (format%i)
            i += 1
        return name
    
    CheckPasteCompatibility = {"SFC": lambda name: True,
                               "LD": lambda name: not name.startswith("sfcObjects"),
                               "FBD": lambda name: name.startswith("fbdObjects") or name.startswith("commonObjects")}
    
    def PasteEditedElementInstances(self, tagname, text, new_pos, middle=False, debug=False):
        element = self.GetEditedElement(tagname, debug)
        element_name, element_type = self.GetEditedElementType(tagname, debug)
        if element is not None:
            bodytype = element.getbodyType()
            
            # Get edited element type scaling
            scaling = None
            project = self.GetProject(debug)
            if project is not None:
                properties = project.getcontentHeader()
                scaling = properties["scaling"][bodytype]
            
            # Get ids already by all the instances in edited element
            used_id = dict([(instance.getlocalId(), True) for instance in element.getinstances()])
            new_id = {}
            
            text = "<paste>%s</paste>"%text
            
            try:
                tree = minidom.parseString(text)
            except:
                return _("Invalid plcopen element(s)!!!")
            instances = []
            exclude = {}
            for root in tree.childNodes:
                if root.nodeType == tree.ELEMENT_NODE and root.nodeName == "paste":
                    for child in root.childNodes:
                        if child.nodeType == tree.ELEMENT_NODE:
                            if not child.nodeName in plcopen.ElementNameToClass:
                                return _("\"%s\" element can't be pasted here!!!")%child.nodeName

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

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

    def AddEditedElementPowerRail(self, tagname, id, type):
        element = self.GetEditedElement(tagname)
        if element is not None:
            if type == LEFTRAIL:
                name = "leftPowerRail"
                powerrail = plcopen.ldObjects_leftPowerRail()
            elif type == RIGHTRAIL:
                name = "rightPowerRail"
                powerrail = plcopen.ldObjects_rightPowerRail()
            powerrail.setlocalId(id)
            element.addinstance(name, powerrail)
    
    def SetEditedElementPowerRailInfos(self, tagname, id, infos):
        element = self.GetEditedElement(tagname)
        if element is not None:
            powerrail = element.getinstance(id)
            if powerrail is None:
                return
            for param, value in infos.items():
                if param == "height":
                    powerrail.setheight(value)
                elif param == "width":
                    powerrail.setwidth(value)
                elif param == "x":
                    powerrail.setx(value)
                elif param == "y":
                    powerrail.sety(value)
                elif param == "connectors":
                    if isinstance(powerrail, plcopen.ldObjects_leftPowerRail):
                        powerrail.setconnectionPointOut([])
                        for connector in value["outputs"]:
                            position = connector.GetRelPosition()
                            connection = plcopen.leftPowerRail_connectionPointOut()
                            connection.setrelPositionXY(position.x, position.y)
                            powerrail.connectionPointOut.append(connection)
                    elif isinstance(powerrail, plcopen.ldObjects_rightPowerRail):
                        powerrail.setconnectionPointIn([])
                        for connector in value["inputs"]:
                            position = connector.GetRelPosition()
                            connection = plcopen.connectionPointIn()
                            connection.setrelPositionXY(position.x, position.y)
                            self.SetConnectionWires(connection, connector)
                            powerrail.connectionPointIn.append(connection)

    def AddEditedElementContact(self, tagname, id):
        element = self.GetEditedElement(tagname)
        if element is not None:
            contact = plcopen.ldObjects_contact()
            contact.setlocalId(id)
            element.addinstance("contact", contact)

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

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

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

    def AddEditedElementStep(self, tagname, id):
        element = self.GetEditedElement(tagname)
        if element is not None:
            step = plcopen.sfcObjects_step()
            step.setlocalId(id)
            element.addinstance("step", step)
    
    def SetEditedElementStepInfos(self, tagname, id, infos):
        element = self.GetEditedElement(tagname)
        if element is not None:
            step = element.getinstance(id)
            if step is None:
                return
            for param, value in infos.items():
                if param == "name":
                    step.setname(value)
                elif param == "initial":
                    step.setinitialStep(value)
                elif param == "height":
                    step.setheight(value)
                elif param == "width":
                    step.setwidth(value)
                elif param == "x":
                    step.setx(value)
                elif param == "y":
                    step.sety(value)
                elif param == "connectors":
                    if len(value["inputs"]) > 0:
                        input_connector = value["inputs"][0]
                        position = input_connector.GetRelPosition()
                        step.addconnectionPointIn()
                        step.connectionPointIn.setrelPositionXY(position.x, position.y)
                        self.SetConnectionWires(step.connectionPointIn, input_connector)
                    else:
                        step.deleteconnectionPointIn()
                    if len(value["outputs"]) > 0:
                        output_connector = value["outputs"][0]
                        position = output_connector.GetRelPosition()
                        step.addconnectionPointOut()
                        step.connectionPointOut.setrelPositionXY(position.x, position.y)
                    else:
                        step.deleteconnectionPointOut()
                elif param == "action":
                    if value:
                        position = value.GetRelPosition()
                        step.addconnectionPointOutAction()
                        step.connectionPointOutAction.setrelPositionXY(position.x, position.y)
                    else:
                        step.deleteconnectionPointOutAction()
    
    def AddEditedElementTransition(self, tagname, id):
        element = self.GetEditedElement(tagname)
        if element is not None:
            transition = plcopen.sfcObjects_transition()
            transition.setlocalId(id)
            element.addinstance("transition", transition)
    
    def SetEditedElementTransitionInfos(self, tagname, id, infos):
        element = self.GetEditedElement(tagname)
        if element is not None:
            transition = element.getinstance(id)
            if transition is None:
                return
            for param, value in infos.items():
                if param == "type" and value != "connection":
                    transition.setconditionContent(value, infos["condition"])
                elif param == "height":
                    transition.setheight(value)
                elif param == "width":
                    transition.setwidth(value)
                elif param == "x":
                    transition.setx(value)
                elif param == "y":
                    transition.sety(value)
                elif param == "priority":
                    if value != 0:
                        transition.setpriority(value)
                    else:
                        transition.setpriority(None)
                elif param == "connectors":
                    input_connector = value["inputs"][0]
                    position = input_connector.GetRelPosition()
                    transition.addconnectionPointIn()
                    transition.connectionPointIn.setrelPositionXY(position.x, position.y)
                    self.SetConnectionWires(transition.connectionPointIn, input_connector)
                    output_connector = value["outputs"][0]
                    position = output_connector.GetRelPosition()
                    transition.addconnectionPointOut()
                    transition.connectionPointOut.setrelPositionXY(position.x, position.y)
                elif infos.get("type", None) == "connection" and param == "connection" and value:
                    transition.setconditionContent("connection", None)
                    self.SetConnectionWires(transition.condition.content["value"], value)
    
    def AddEditedElementDivergence(self, tagname, id, type):
        element = self.GetEditedElement(tagname)
        if element is not None:
            if type == SELECTION_DIVERGENCE:
                name = "selectionDivergence"
                divergence = plcopen.sfcObjects_selectionDivergence()
            elif type == SELECTION_CONVERGENCE:
                name = "selectionConvergence"
                divergence = plcopen.sfcObjects_selectionConvergence()
            elif type == SIMULTANEOUS_DIVERGENCE:
                name = "simultaneousDivergence"
                divergence = plcopen.sfcObjects_simultaneousDivergence()
            elif type == SIMULTANEOUS_CONVERGENCE:
                name = "simultaneousConvergence"
                divergence = plcopen.sfcObjects_simultaneousConvergence()
            divergence.setlocalId(id)
            element.addinstance(name, divergence)
    
    def SetEditedElementDivergenceInfos(self, tagname, id, infos):
        element = self.GetEditedElement(tagname)
        if element is not None:
            divergence = element.getinstance(id)
            if divergence is None:
                return
            for param, value in infos.items():
                if param == "height":
                    divergence.setheight(value)
                elif param == "width":
                    divergence.setwidth(value)
                elif param == "x":
                    divergence.setx(value)
                elif param == "y":
                    divergence.sety(value)
                elif param == "connectors":
                    input_connectors = value["inputs"]
                    if isinstance(divergence, (plcopen.sfcObjects_selectionDivergence, plcopen.sfcObjects_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()
                            if isinstance(divergence, plcopen.sfcObjects_selectionConvergence):
                                connection = plcopen.selectionConvergence_connectionPointIn()
                            else:
                                connection = plcopen.connectionPointIn()
                            connection.setrelPositionXY(position.x, position.y)
                            self.SetConnectionWires(connection, input_connector)
                            divergence.appendconnectionPointIn(connection)
                    output_connectors = value["outputs"]
                    if isinstance(divergence, (plcopen.sfcObjects_selectionConvergence, plcopen.sfcObjects_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()
                            if isinstance(divergence, plcopen.sfcObjects_selectionDivergence):
                                connection = plcopen.selectionDivergence_connectionPointOut()
                            else:
                                connection = plcopen.simultaneousDivergence_connectionPointOut()
                            connection.setrelPositionXY(position.x, position.y)
                            divergence.appendconnectionPointOut(connection)
    
    def AddEditedElementJump(self, tagname, id):
        element = self.GetEditedElement(tagname)
        if element is not None:
            jump = plcopen.sfcObjects_jumpStep()
            jump.setlocalId(id)
            element.addinstance("jumpStep", jump)
    
    def SetEditedElementJumpInfos(self, tagname, id, infos):
        element = self.GetEditedElement(tagname)
        if element is not None:
            jump = element.getinstance(id)
            if jump is None:
                return
            for param, value in infos.items():
                if param == "target":
                    jump.settargetName(value)
                elif param == "height":
                    jump.setheight(value)
                elif param == "width":
                    jump.setwidth(value)
                elif param == "x":
                    jump.setx(value)
                elif param == "y":
                    jump.sety(value)
                elif param == "connector":
                    position = value.GetRelPosition()
                    jump.addconnectionPointIn()
                    jump.connectionPointIn.setrelPositionXY(position.x, position.y)
                    self.SetConnectionWires(jump.connectionPointIn, value)
 
    def AddEditedElementActionBlock(self, tagname, id):
        element = self.GetEditedElement(tagname)
        if element is not None:
            actionBlock = plcopen.commonObjects_actionBlock()
            actionBlock.setlocalId(id)
            element.addinstance("actionBlock", actionBlock)
    
    def SetEditedElementActionBlockInfos(self, tagname, id, infos):
        element = self.GetEditedElement(tagname)
        if element is not None:
            actionBlock = element.getinstance(id)
            if actionBlock is None:
                return
            for param, value in infos.items():
                if param == "actions":
                    actionBlock.setactions(value)
                elif param == "height":
                    actionBlock.setheight(value)
                elif param == "width":
                    actionBlock.setwidth(value)
                elif param == "x":
                    actionBlock.setx(value)
                elif param == "y":
                    actionBlock.sety(value)
                elif param == "connector":
                    position = value.GetRelPosition()
                    actionBlock.addconnectionPointIn()
                    actionBlock.connectionPointIn.setrelPositionXY(position.x, position.y)
                    self.SetConnectionWires(actionBlock.connectionPointIn, value)
    
    def RemoveEditedElementInstance(self, tagname, id):
        element = self.GetEditedElement(tagname)
        if element is not None:
            instance = element.getinstance(id)
            if isinstance(instance, plcopen.fbdObjects_block):
                self.RemoveEditedElementPouVar(tagname, instance.gettypeName(), instance.getinstanceName())
            element.removeinstance(id)
            self.Project.RefreshElementUsingTree()

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

    def SetEditedResourceInfos(self, tagname, tasks, instances):
        resource = self.GetEditedElement(tagname)
        if resource is not None:
            resource.settask([])
            resource.setpouInstance([])
            task_list = {}
            for task in tasks:
                new_task = plcopen.resource_task()
                new_task.setname(task["Name"])
                if task["Single"] != "":
                    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["Interval"] != "":
                    new_task.setinterval(task["Interval"])
                new_task.setpriority(int(task["Priority"]))
                if task["Name"] != "":
                    task_list[task["Name"]] = new_task
                resource.appendtask(new_task)
            for instance in instances:
                new_instance = plcopen.pouInstance()
                new_instance.setname(instance["Name"])
                new_instance.settypeName(instance["Type"])
                if instance["Task"] != "":
                    task_list[instance["Task"]].appendpouInstance(new_instance)
                else:
                    resource.appendpouInstance(new_instance)

    def GetEditedResourceInfos(self, tagname, debug = False):
        resource = self.GetEditedElement(tagname, debug)
        if resource is not None:
            tasks = resource.gettask()
            instances = resource.getpouInstance()
            tasks_data = []
            instances_data = []
            for task in tasks:
                new_task = {}
                new_task["Name"] = task.getname()
                single = task.getsingle()
                if single:
                    new_task["Single"] = single
                else:
                    new_task["Single"] = ""
                interval = task.getinterval()
                if interval:
##                    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"] = ""
                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):
        xmlfile = open(filepath, 'r')
        tree = minidom.parse(xmlfile)
        xmlfile.close()
        
        self.Project = plcopen.project()
        for child in tree.childNodes:
            if child.nodeType == tree.ELEMENT_NODE and child.nodeName == "project":
                self.Project.loadXMLTree(child, ["xmlns", "xmlns:xhtml", "xmlns:xsi", "xsi:schemaLocation"])
                self.SetFilePath(filepath)
                self.Project.RefreshElementUsingTree()
                self.Project.RefreshDataTypeHierarchy()
                self.Project.RefreshCustomBlockTypes()
                self.CreateProjectBuffer(True)
                self.ProgramChunks = []
                self.ProgramOffset = 0
                self.NextCompiledProject = self.Copy(self.Project)
                self.CurrentCompiledProject = None
                self.Buffering = False
                self.CurrentElementEditing = None
                return None
        return _("No PLC project found")

    def SaveXMLFile(self, filepath = None):
        if not filepath and self.FilePath == "":
            return False
        else:
            contentheader = self.Project.getcontentHeader()
            contentheader["modificationDateTime"] = datetime.datetime(*localtime()[:6])
            self.Project.setcontentHeader(contentheader)
            
            text = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
            extras = {"xmlns" : "http://www.plcopen.org/xml/tc6.xsd",
                      "xmlns:xhtml" : "http://www.w3.org/1999/xhtml",
                      "xmlns:xsi" : "http://www.w3.org/2001/XMLSchema-instance",
                      "xsi:schemaLocation" : "http://www.plcopen.org/xml/tc6.xsd"}
            text += self.Project.generateXMLText("project", 0, extras)
            
            if filepath:
                xmlfile = open(filepath,"w")
            else:
                xmlfile = open(self.FilePath,"w")
            xmlfile.write(text)
            xmlfile.close()
            self.MarkProjectAsSaved()
            if filepath:
                self.SetFilePath(filepath)
            return True

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

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

    def CreateProjectBuffer(self, saved):
        if self.ProjectBufferEnabled:
            self.ProjectBuffer = UndoBuffer(self.Copy(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(self.Copy(self.Project))
        else:
            self.ProjectSaved = False

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

    def MarkProjectAsSaved(self):
        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()
        else:
            return self.ProjectSaved

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