editors/SFCViewer.py
author Andrey Skvortsov <andrej.skvortzov@gmail.com>
Thu, 28 Apr 2016 13:05:57 +0300
changeset 1507 d7f474d10210
parent 1347 533741e5075c
child 1567 a4bf874e4949
permissions -rw-r--r--
fix issue with sometimes wrong return code of ProcessLogger


As a result of wrong return code Beremiz gives folowing traceback:
Traceback (most recent call last):
File "./Beremiz.py", line 850, in OnMenu
getattr(self.CTR, method)()
File "/home/developer/WorkData/PLC/beremiz/beremiz/ProjectController.py", line 925, in _Build
IECGenRes = self._Generate_SoftPLC()
File "/home/developer/WorkData/PLC/beremiz/beremiz/ProjectController.py", line 568, in _Generate_SoftPLC
return self._Compile_ST_to_SoftPLC()
File "/home/developer/WorkData/PLC/beremiz/beremiz/ProjectController.py", line 661, in _Compile_ST_to_SoftPLC
C_files.remove("POUS.c")
ValueError: list.remove(x): x not in list

The problem is that both threads (for reading stdout and stderr) call self.Proc.poll(),
that updates internal returncode field. This call is done without any locking and the first thread gets correct result,
but other gets 0 as retval. If 0 gets thread, that afterwards calls callback finish, then wrong return code is returned
to the parent. Now only the thread with a callback polls for the return code, other thread just checked local value.

Additionally function spin() waits now until all threads finish reading their pipes, so the results are always correct.
#!/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 types import *

import wx

from Viewer import *

class SFC_Viewer(Viewer):
    
    def __init__(self, parent, tagname, window, controler, debug = False, instancepath = ""):
        Viewer.__init__(self, parent, tagname, window, controler, debug, instancepath)
        self.CurrentLanguage = "SFC"
    
    def ConnectConnectors(self, start, end):
        startpoint = [start.GetPosition(False), start.GetDirection()]
        endpoint = [end.GetPosition(False), end.GetDirection()]
        wire = Wire(self, startpoint, endpoint)
        self.AddWire(wire)
        start.Connect((wire, 0), False)
        end.Connect((wire, -1), False)
        wire.ConnectStartPoint(None, start)
        wire.ConnectEndPoint(None, end)
        return wire
    
    def CreateTransition(self, connector, next = None):
        previous = connector.GetParentBlock()
        id = self.GetNewId()
        transition = SFC_Transition(self, "reference", "", 0, id)
        pos = connector.GetPosition(False)
        transition.SetPosition(pos.x, pos.y + SFC_WIRE_MIN_SIZE)
        transition_connectors = transition.GetConnectors()
        wire = self.ConnectConnectors(transition_connectors["input"], connector)
        if isinstance(previous, SFC_Divergence):
            previous.RefreshConnectedPosition(connector)
        else:
            previous.RefreshOutputPosition()
        wire.SetPoints([wx.Point(pos.x, pos.y + GetWireSize(previous)), wx.Point(pos.x, pos.y)])
        self.AddBlock(transition)
        self.Controler.AddEditedElementTransition(self.TagName, id)
        self.RefreshTransitionModel(transition)
        if next:
            wire = self.ConnectConnectors(next, transition_connectors["output"])
            pos = transition_connectors["output"].GetPosition(False)
            next_block = next.GetParentBlock()
            next_pos = next.GetPosition(False)
            transition.RefreshOutputPosition((0, pos.y + SFC_WIRE_MIN_SIZE - next_pos.y))
            wire.SetPoints([wx.Point(pos.x, pos.y + SFC_WIRE_MIN_SIZE), wx.Point(pos.x, pos.y)])
            if isinstance(next_block, SFC_Divergence):
                next_block.RefreshPosition()
            transition.RefreshOutputModel(True)
        return transition
    
    def RemoveTransition(self, transition):
        connectors = transition.GetConnectors()
        input_wires = connectors["input"].GetWires()
        if len(input_wires) != 1:
            return
        input_wire = input_wires[0][0]
        previous = input_wire.EndConnected
        input_wire.Clean()
        self.RemoveWire(input_wire)
        output_wires = connectors["output"].GetWires()
        if len(output_wires) != 1:
            return
        output_wire = output_wires[0][0]
        next = output_wire.StartConnected
        output_wire.Clean()
        self.RemoveWire(output_wire)
        transition.Clean()
        self.RemoveBlock(transition)
        self.Controler.RemoveEditedElementInstance(self.TagName, transition.GetId())
        wire = self.ConnectConnectors(next, previous)
        return wire
    
    def CreateStep(self, name, connector, next = None):
        previous = connector.GetParentBlock()
        id = self.GetNewId()
        step = SFC_Step(self, name, False, id)
        if next:
            step.AddOutput()
        min_width, min_height = step.GetMinSize()
        pos = connector.GetPosition(False)
        step.SetPosition(pos.x, pos.y + SFC_WIRE_MIN_SIZE)
        step.SetSize(min_width, min_height)
        step_connectors = step.GetConnectors()
        wire = self.ConnectConnectors(step_connectors["input"], connector)
        if isinstance(previous, SFC_Divergence):
            previous.RefreshConnectedPosition(connector)
        else:
            previous.RefreshOutputPosition()
        wire.SetPoints([wx.Point(pos.x, pos.y + GetWireSize(previous)), wx.Point(pos.x, pos.y)])
        self.AddBlock(step)
        self.Controler.AddEditedElementStep(self.TagName, id)
        self.RefreshStepModel(step)
        if next:
            wire = self.ConnectConnectors(next, step_connectors["output"])
            pos = step_connectors["output"].GetPosition(False)
            next_block = next.GetParentBlock()
            next_pos = next.GetPosition(False)
            step.RefreshOutputPosition((0, pos.y + SFC_WIRE_MIN_SIZE - next_pos.y))
            wire.SetPoints([wx.Point(pos.x, pos.y + SFC_WIRE_MIN_SIZE), wx.Point(pos.x, pos.y)])
            if isinstance(next_block, SFC_Divergence):
                next_block.RefreshPosition()
            step.RefreshOutputModel(True)
        return step

    def RemoveStep(self, step):
        connectors = step.GetConnectors()
        if connectors["input"]:
            input_wires = connectors["input"].GetWires()
            if len(input_wires) != 1:
                return
            input_wire = input_wires[0][0]
            previous = input_wire.EndConnected
            input_wire.Clean()
            self.RemoveWire(input_wire)
        else:
            previous = None
        if connectors["output"]:
            output_wires = connectors["output"].GetWires()
            if len(output_wires) != 1:
                return
            output_wire = output_wires[0][0]
            next = output_wire.StartConnected
            output_wire.Clean()
            self.RemoveWire(output_wire)
        else:
            next = None
        action = step.GetActionConnected()
        if action:
            self.DeleteActionBlock(action.GetParentBlock())
        step.Clean()
        self.RemoveBlock(step)
        self.Controler.RemoveEditedElementInstance(self.TagName, step.GetId())
        if next and previous:
            wire = self.ConnectConnectors(next, previous)
            return wire
        else:
            return None

#-------------------------------------------------------------------------------
#                          Mouse event functions
#-------------------------------------------------------------------------------

    def OnViewerLeftDown(self, event):
        if self.GetDrawingMode() == FREEDRAWING_MODE:
            Viewer.OnViewerLeftDown(self, event)
        elif self.Mode == MODE_SELECTION:
            if event.ShiftDown() and not event.ControlDown() and self.SelectedElement is not None:
                element = self.FindElement(event, True)
                if element and not self.IsWire(element):
                    if isinstance(self.SelectedElement, Graphic_Group):
                        self.SelectedElement.SelectElement(element)
                    else:
                        group = Graphic_Group(self)
                        self.SelectedElement.SetSelected(False)
                        group.SelectElement(self.SelectedElement)
                        group.SelectElement(element)
                        self.SelectedElement = group
                    elements = self.SelectedElement.GetElements()
                    if len(elements) == 0:
                        self.SelectedElement = element
                    elif len(elements) == 1:
                        self.SelectedElement = elements[0]
                    self.SelectedElement.SetSelected(True)
            else:
                element = self.FindElement(event)
                if self.SelectedElement and self.SelectedElement != element:
                    if self.IsWire(self.SelectedElement):
                        self.SelectedElement.SetSelectedSegment(None)
                    else:
                        self.SelectedElement.SetSelected(False)
                    self.SelectedElement = None
                if element:
                    self.SelectedElement = element
                    self.SelectedElement.OnLeftDown(event, self.GetLogicalDC(), self.Scaling)
                    self.SelectedElement.Refresh()
                else:
                    self.rubberBand.Reset()
                    self.rubberBand.OnLeftDown(event, self.GetLogicalDC(), self.Scaling)
        elif self.Mode == MODE_COMMENT:
            self.rubberBand.Reset()
            self.rubberBand.OnLeftDown(event, self.GetLogicalDC(), self.Scaling)
        event.Skip()

    def OnViewerLeftUp(self, event):
        if self.GetDrawingMode() == FREEDRAWING_MODE:
            Viewer.OnViewerLeftUp(self, event)
        elif self.rubberBand.IsShown():
            if self.Mode == MODE_SELECTION:
                elements = self.SearchElements(self.rubberBand.GetCurrentExtent())
                self.rubberBand.OnLeftUp(event, self.GetLogicalDC(), self.Scaling)
                if len(elements) > 0:
                    self.SelectedElement = Graphic_Group(self)
                    self.SelectedElement.SetElements(elements)
                    self.SelectedElement.SetSelected(True)
            elif self.Mode == MODE_COMMENT:
                bbox = self.rubberBand.GetCurrentExtent()
                self.rubberBand.OnLeftUp(event, self.GetLogicalDC(), self.Scaling)
                wx.CallAfter(self.AddComment, bbox)
        elif self.Mode == MODE_INITIALSTEP:
            wx.CallAfter(self.AddInitialStep, GetScaledEventPosition(event, self.GetLogicalDC(), self.Scaling))
        elif self.Mode == MODE_SELECTION and self.SelectedElement:
            if self.IsWire(self.SelectedElement):
                self.SelectedElement.SetSelectedSegment(0)
            else:
                self.SelectedElement.OnLeftUp(event, self.GetLogicalDC(), self.Scaling)
                self.SelectedElement.Refresh()
            wx.CallAfter(self.SetCurrentCursor, 0)
        elif self.Mode == MODE_WIRE and self.SelectedElement:
            self.SelectedElement.ResetPoints()
            self.SelectedElement.OnMotion(event, self.GetLogicalDC(), self.Scaling)
            self.SelectedElement.GeneratePoints()
            self.SelectedElement.RefreshModel()
            self.SelectedElement.SetSelected(True)
        event.Skip()
    
    def OnViewerRightUp(self, event):
        if self.GetDrawingMode() == FREEDRAWING_MODE:
            Viewer.OnViewerRightUp(self, event)
        else:
            element = self.FindElement(event)
            if element:
                if self.SelectedElement and self.SelectedElement != element:
                    self.SelectedElement.SetSelected(False)
                self.SelectedElement = element
                if self.IsWire(self.SelectedElement):
                    self.SelectedElement.SetSelectedSegment(0)
                else:
                    self.SelectedElement.SetSelected(True)
                    self.SelectedElement.OnRightUp(event, self.GetLogicalDC(), self.Scaling)
                    self.SelectedElement.Refresh()
                wx.CallAfter(self.SetCurrentCursor, 0)
        event.Skip()
    
    def OnViewerLeftDClick(self, event):
        if self.GetDrawingMode() == FREEDRAWING_MODE:
            Viewer.OnViewerLeftDClick(self, event)
        elif self.Mode == MODE_SELECTION and self.SelectedElement:
            self.SelectedElement.OnLeftDClick(event, self.GetLogicalDC(), self.Scaling)
            self.Refresh(False)
        event.Skip()
    
    def OnViewerMotion(self, event):
        if self.GetDrawingMode() == FREEDRAWING_MODE:
            Viewer.OnViewerMotion(self, event)
        else:
            if self.rubberBand.IsShown():
                self.rubberBand.OnMotion(event, self.GetLogicalDC(), self.Scaling)
            elif self.Mode == MODE_SELECTION and self.SelectedElement:
                if not self.IsWire(self.SelectedElement) and not isinstance(self.SelectedElement, Graphic_Group):
                    self.SelectedElement.OnMotion(event, self.GetLogicalDC(), self.Scaling)
                    self.SelectedElement.Refresh()
            elif self.Mode == MODE_WIRE and self.SelectedElement:
                self.SelectedElement.ResetPoints()
                self.SelectedElement.OnMotion(event, self.GetLogicalDC(), self.Scaling)
                self.SelectedElement.GeneratePoints()
                self.SelectedElement.Refresh()
            self.UpdateScrollPos(event)
        event.Skip()

#-------------------------------------------------------------------------------
#                          Keyboard event functions
#-------------------------------------------------------------------------------

    def OnChar(self, event):
        if self.GetDrawingMode() == FREEDRAWING_MODE:
            Viewer.OnChar(self, event)
        else:
            xpos, ypos = self.GetScrollPos(wx.HORIZONTAL), self.GetScrollPos(wx.VERTICAL)
            xmax = self.GetScrollRange(wx.HORIZONTAL) - self.GetScrollThumb(wx.HORIZONTAL)
            ymax = self.GetScrollRange(wx.VERTICAL) - self.GetScrollThumb(wx.VERTICAL)
            keycode = event.GetKeyCode()
            if self.Scaling:
                scaling = self.Scaling
            else:
                scaling = (8, 8)
            if keycode == wx.WXK_DELETE and self.SelectedElement:
                self.SelectedElement.Delete()
                self.SelectedElement = None
                self.RefreshBuffer()
                self.RefreshScrollBars()
                self.Refresh(False)
            elif keycode == wx.WXK_LEFT:
                if event.ControlDown() and event.ShiftDown():
                    self.Scroll(0, ypos)
                elif event.ControlDown():
                    event.Skip()
                elif self.SelectedElement:
                    self.SelectedElement.Move(-scaling[0], 0)
                    self.SelectedElement.RefreshModel()
                    self.RefreshBuffer()
                    self.RefreshScrollBars()
                    self.RefreshRect(self.GetScrolledRect(self.SelectedElement.GetRedrawRect(-scaling[0], 0)), False)
            elif keycode == wx.WXK_RIGHT:
                if event.ControlDown() and event.ShiftDown():
                    self.Scroll(xmax, ypos)
                elif event.ControlDown():
                    event.Skip()
                elif self.SelectedElement:
                    self.SelectedElement.Move(scaling[0], 0)
                    self.SelectedElement.RefreshModel()
                    self.RefreshBuffer()
                    self.RefreshScrollBars()
                    self.RefreshRect(self.GetScrolledRect(self.SelectedElement.GetRedrawRect(scaling[0], 0)), False)
            elif keycode == wx.WXK_UP:
                if event.ControlDown() and event.ShiftDown():
                    self.Scroll(xpos, 0)
                elif event.ControlDown():
                    event.Skip()
                elif self.SelectedElement:
                    self.SelectedElement.Move(0, -scaling[1])
                    self.SelectedElement.RefreshModel()
                    self.RefreshBuffer()
                    self.RefreshScrollBars()
                    self.RefreshRect(self.GetScrolledRect(self.SelectedElement.GetRedrawRect(0, -scaling[1])), False)
            elif keycode == wx.WXK_DOWN:
                if event.ControlDown() and event.ShiftDown():
                    self.Scroll(xpos, ymax)
                elif event.ControlDown():
                    event.Skip()
                elif self.SelectedElement:
                    self.SelectedElement.Move(0, scaling[1])
                    self.SelectedElement.RefreshModel()
                    self.RefreshBuffer()
                    self.RefreshScrollBars()
                    self.RefreshRect(self.GetScrolledRect(self.SelectedElement.GetRedrawRect(0, scaling[1])), False)
            else:
                event.Skip()
        
#-------------------------------------------------------------------------------
#                          Adding element functions
#-------------------------------------------------------------------------------

    def AddInitialStep(self, pos):
        dialog = SFCStepNameDialog(self.ParentWindow, _("Please enter step name"), _("Add a new initial step"), "", wx.OK|wx.CANCEL)
        dialog.SetPouNames(self.Controler.GetProjectPouNames(self.Debug))
        dialog.SetVariables(self.Controler.GetEditedElementInterfaceVars(self.TagName, debug=self.Debug))
        dialog.SetStepNames([block.GetName() for block in self.Blocks if isinstance(block, SFC_Step)])
        if dialog.ShowModal() == wx.ID_OK:
            id = self.GetNewId()
            name = dialog.GetValue()
            step = SFC_Step(self, name, True, id)
            min_width, min_height = step.GetMinSize()
            step.SetPosition(pos.x, pos.y)
            width, height = step.GetSize()
            step.SetSize(max(min_width, width), max(min_height, height))
            self.AddBlock(step)
            self.Controler.AddEditedElementStep(self.TagName, id)
            self.RefreshStepModel(step)
            self.RefreshBuffer()
            self.RefreshScrollBars()
            self.Refresh(False)
        dialog.Destroy()

    def AddStep(self):
        if self.SelectedElement in self.Wires or isinstance(self.SelectedElement, SFC_Step):
            dialog = SFCStepNameDialog(self.ParentWindow, _("Add a new step"), _("Please enter step name"), "", wx.OK|wx.CANCEL)
            dialog.SetPouNames(self.Controler.GetProjectPouNames(self.Debug))
            dialog.SetVariables(self.Controler.GetEditedElementInterfaceVars(self.TagName, debug=self.Debug))
            dialog.SetStepNames([block.GetName() for block in self.Blocks if isinstance(block, SFC_Step)])
            if dialog.ShowModal() == wx.ID_OK:
                name = dialog.GetValue()
                if self.IsWire(self.SelectedElement):
                    self.SelectedElement.SetSelectedSegment(None)
                    previous = self.SelectedElement.EndConnected
                    next = self.SelectedElement.StartConnected
                    self.SelectedElement.Clean()
                    self.RemoveWire(self.SelectedElement)
                else:
                    connectors = self.SelectedElement.GetConnectors()
                    if connectors["output"]:
                        previous = connectors["output"]
                        wires = previous.GetWires()
                        if len(wires) != 1:
                            return
                        wire = wires[0][0]
                        next = wire.StartConnected
                        wire.Clean()
                        self.RemoveWire(wire)
                    else:
                        self.SelectedElement.AddOutput()
                        connectors = self.SelectedElement.GetConnectors()
                        self.RefreshStepModel(self.SelectedElement)
                        previous = connectors["output"]
                        next = None
                previous_block = previous.GetParentBlock()
                if isinstance(previous_block, SFC_Step) or isinstance(previous_block, SFC_Divergence) and previous_block.GetType() in [SELECTION_DIVERGENCE, SELECTION_CONVERGENCE]:
                    transition = self.CreateTransition(previous)
                    transition_connectors = transition.GetConnectors()
                    step = self.CreateStep(name, transition_connectors["output"], next)
                else:
                    step = self.CreateStep(name, previous)
                    step.AddOutput()
                    step.RefreshModel()
                    step_connectors = step.GetConnectors()
                    transition = self.CreateTransition(step_connectors["output"], next)
                if self.IsWire(self.SelectedElement):
                    self.SelectedElement = wire
                    self.SelectedElement.SetSelectedSegment(0)
                else:
                    self.SelectedElement.SetSelected(False)
                    self.SelectedElement = step
                    self.SelectedElement.SetSelected(True)
                self.RefreshBuffer()
                self.RefreshScrollBars()
                self.Refresh(False)
            dialog.Destroy()
    
    def AddStepAction(self):
        if isinstance(self.SelectedElement, SFC_Step):
            connectors = self.SelectedElement.GetConnectors()
            if not connectors["action"]:
                dialog = ActionBlockDialog(self.ParentWindow)
                dialog.SetQualifierList(self.Controler.GetQualifierTypes())
                dialog.SetActionList(self.Controler.GetEditedElementActions(self.TagName, self.Debug))
                dialog.SetVariableList(self.Controler.GetEditedElementInterfaceVars(self.TagName, debug=self.Debug))
                if dialog.ShowModal() == wx.ID_OK:
                    actions = dialog.GetValues()
                    self.SelectedElement.AddAction()
                    self.RefreshStepModel(self.SelectedElement)
                    connectors = self.SelectedElement.GetConnectors()
                    pos = connectors["action"].GetPosition(False)
                    id = self.GetNewId()
                    actionblock = SFC_ActionBlock(self, [], id)
                    actionblock.SetPosition(pos.x + SFC_WIRE_MIN_SIZE, pos.y - SFC_STEP_DEFAULT_SIZE[1] / 2)
                    actionblock_connector = actionblock.GetConnector()
                    wire = self.ConnectConnectors(actionblock_connector, connectors["action"])
                    wire.SetPoints([wx.Point(pos.x + SFC_WIRE_MIN_SIZE, pos.y), wx.Point(pos.x, pos.y)])
                    actionblock.SetActions(actions)
                    self.AddBlock(actionblock)
                    self.Controler.AddEditedElementActionBlock(self.TagName, id)
                    self.RefreshActionBlockModel(actionblock)
                    self.RefreshBuffer()
                    self.RefreshScrollBars()
                    self.Refresh(False)
                dialog.Destroy()
    
    def AddDivergence(self):
        if self.SelectedElement in self.Wires or isinstance(self.SelectedElement, Graphic_Group) or isinstance(self.SelectedElement, SFC_Step):        
            dialog = SFCDivergenceDialog(self.ParentWindow)
            dialog.SetPreviewFont(self.GetFont())
            if dialog.ShowModal() == wx.ID_OK:
                value = dialog.GetValues()
                if value["type"] == SELECTION_DIVERGENCE:
                    if self.SelectedElement in self.Wires and isinstance(self.SelectedElement.EndConnected.GetParentBlock(), SFC_Step):
                        self.SelectedElement.SetSelectedSegment(None)
                        previous = self.SelectedElement.EndConnected
                        next = self.SelectedElement.StartConnected
                        self.SelectedElement.Clean()
                        self.RemoveWire(self.SelectedElement)
                        self.SelectedElement = None
                    elif isinstance(self.SelectedElement, SFC_Step):
                        connectors = self.SelectedElement.GetConnectors()
                        if connectors["output"]:
                            previous = connectors["output"]
                            wires = previous.GetWires()
                            if len(wires) != 1:
                                return
                            wire = wires[0][0]
                            next = wire.StartConnected
                            wire.Clean()
                            self.RemoveWire(wire)
                        else:
                            self.SelectedElement.AddOutput()
                            connectors = self.SelectedElement.GetConnectors()
                            self.RefreshStepModel(self.SelectedElement)
                            previous = connectors["output"]
                            next = None
                    else:
                        return
                    id = self.GetNewId()
                    divergence = SFC_Divergence(self, SELECTION_DIVERGENCE, value["number"], id)
                    pos = previous.GetPosition(False)
                    previous_block = previous.GetParentBlock()
                    wire_size = GetWireSize(previous_block)
                    divergence.SetPosition(pos.x, pos.y + wire_size)
                    divergence_connectors = divergence.GetConnectors()
                    wire = self.ConnectConnectors(divergence_connectors["inputs"][0], previous)
                    previous_block.RefreshOutputPosition()
                    wire.SetPoints([wx.Point(pos.x, pos.y + wire_size), wx.Point(pos.x, pos.y)])
                    self.AddBlock(divergence)
                    self.Controler.AddEditedElementDivergence(self.TagName, id, value["type"])
                    self.RefreshDivergenceModel(divergence)
                    for index, connector in enumerate(divergence_connectors["outputs"]):
                        if next:
                            wire = self.ConnectConnectors(next, connector)
                            pos = connector.GetPosition(False)
                            next_pos = next.GetPosition(False)
                            next_block = next.GetParentBlock()
                            divergence.RefreshOutputPosition((0, pos.y + SFC_WIRE_MIN_SIZE - next_pos.y))
                            divergence.RefreshConnectedPosition(connector)
                            wire.SetPoints([wx.Point(pos.x, pos.y + SFC_WIRE_MIN_SIZE), wx.Point(pos.x, pos.y)])
                            next_block.RefreshModel()
                            next = None
                        else:
                            transition = self.CreateTransition(connector)
                            transition_connectors = transition.GetConnectors()
                            step = self.CreateStep("Step", transition_connectors["output"])
                elif value["type"] == SIMULTANEOUS_DIVERGENCE:
                    if self.SelectedElement in self.Wires and isinstance(self.SelectedElement.EndConnected.GetParentBlock(), SFC_Transition):
                        self.SelectedElement.SetSelectedSegment(None)
                        previous = self.SelectedElement.EndConnected
                        next = self.SelectedElement.StartConnected
                        self.SelectedElement.Clean()
                        self.RemoveWire(self.SelectedElement)
                        self.SelectedElement = None
                    elif isinstance(self.SelectedElement, SFC_Step):
                        connectors = self.SelectedElement.GetConnectors()
                        if connectors["output"]:
                            previous = connectors["output"]
                            wires = previous.GetWires()
                            if len(wires) != 1:
                                return
                            wire = wires[0][0]
                            next = wire.StartConnected
                            wire.Clean()
                            self.RemoveWire(wire)
                        else:
                            self.SelectedElement.AddOutput()
                            connectors = self.SelectedElement.GetConnectors()
                            self.RefreshStepModel(self.SelectedElement)
                            previous = connectors["output"]
                            next = None
                        transition = self.CreateTransition(previous)
                        transition_connectors = transition.GetConnectors()
                        previous = transition_connectors["output"]
                    else:
                        return
                    id = self.GetNewId()
                    divergence = SFC_Divergence(self, SIMULTANEOUS_DIVERGENCE, value["number"], id)
                    pos = previous.GetPosition(False)
                    previous_block = previous.GetParentBlock()
                    wire_size = GetWireSize(previous_block)
                    divergence.SetPosition(pos.x, pos.y + wire_size)
                    divergence_connectors = divergence.GetConnectors()
                    wire = self.ConnectConnectors(divergence_connectors["inputs"][0], previous)
                    previous_block.RefreshOutputPosition()
                    wire.SetPoints([wx.Point(pos.x, pos.y + wire_size), wx.Point(pos.x, pos.y)])
                    self.AddBlock(divergence)
                    self.Controler.AddEditedElementDivergence(self.TagName, id, value["type"])
                    self.RefreshDivergenceModel(divergence)
                    for index, connector in enumerate(divergence_connectors["outputs"]):
                        if next:
                            wire = self.ConnectConnectors(next, connector)
                            pos = connector.GetPosition(False)
                            next_pos = next.GetPosition(False)
                            next_block = next.GetParentBlock()
                            divergence.RefreshOutputPosition((0, pos.y + SFC_WIRE_MIN_SIZE - next_pos.y))
                            divergence.RefreshConnectedPosition(connector)
                            wire.SetPoints([wx.Point(pos.x, pos.y + SFC_WIRE_MIN_SIZE), wx.Point(pos.x, pos.y)])
                            next_block.RefreshModel()
                            next = None
                        else:
                            step = self.CreateStep("Step", connector)
                elif isinstance(self.SelectedElement, Graphic_Group) and len(self.SelectedElement.GetElements()) > 1:
                    next = None
                    for element in self.SelectedElement.GetElements():
                        connectors = element.GetConnectors()
                        if not isinstance(element, SFC_Step) or connectors["output"] and next:
                            return
                        elif connectors["output"] and not next:
                            wires = connectors["output"].GetWires()
                            if len(wires) != 1:
                                return
                            if value["type"] == SELECTION_CONVERGENCE:
                                transition = wires[0][0].StartConnected.GetParentBlock()
                                transition_connectors = transition.GetConnectors()
                                wires = transition_connectors["output"].GetWires()
                                if len(wires) != 1:
                                    return
                            wire = wires[0][0]
                            next = wire.StartConnected
                            wire.Clean()
                            self.RemoveWire(wire)
                    inputs = []
                    for input in self.SelectedElement.GetElements():
                        input_connectors = input.GetConnectors()
                        if not input_connectors["output"]:
                            input.AddOutput()
                            input.RefreshModel()
                            input_connectors = input.GetConnectors()
                            if value["type"] == SELECTION_CONVERGENCE:
                                transition = self.CreateTransition(input_connectors["output"])
                                transition_connectors = transition.GetConnectors()
                                inputs.append(transition_connectors["output"])
                            else:
                                inputs.append(input_connectors["output"])
                        elif value["type"] == SELECTION_CONVERGENCE:
                            wires = input_connectors["output"].GetWires()
                            transition = wires[0][0].StartConnected.GetParentBlock()
                            transition_connectors = transition.GetConnectors()
                            inputs.append(transition_connectors["output"])
                        else:
                            inputs.append(input_connectors["output"])
                    id = self.GetNewId()
                    divergence = SFC_Divergence(self, value["type"], len(inputs), id)
                    pos = inputs[0].GetPosition(False)
                    divergence.SetPosition(pos.x, pos.y + SFC_WIRE_MIN_SIZE)
                    divergence_connectors = divergence.GetConnectors()
                    for i, input in enumerate(inputs):
                        pos = input.GetPosition(False)
                        wire = self.ConnectConnectors(divergence_connectors["inputs"][i], input)
                        wire_size = GetWireSize(input)
                        wire.SetPoints([wx.Point(pos.x, pos.y + wire_size), wx.Point(pos.x, pos.y)])
                        input_block = input.GetParentBlock()
                        input_block.RefreshOutputPosition()
                    divergence.RefreshPosition()
                    pos = divergence_connectors["outputs"][0].GetRelPosition()
                    divergence.MoveConnector(divergence_connectors["outputs"][0], - pos.x)
                    self.AddBlock(divergence)
                    self.Controler.AddEditedElementDivergence(self.TagName, id, value["type"])
                    self.RefreshDivergenceModel(divergence)
                    if next:
                        wire = self.ConnectConnectors(next, divergence_connectors["outputs"][0])
                        pos = divergence_connectors["outputs"][0].GetPosition(False)
                        next_pos = next.GetPosition(False)
                        next_block = next.GetParentBlock()
                        divergence.RefreshOutputPosition((0, pos.y + SFC_WIRE_MIN_SIZE - next_pos.y))
                        divergence.RefreshConnectedPosition(divergence_connectors["outputs"][0])
                        wire.SetPoints([wx.Point(pos.x, pos.y + SFC_WIRE_MIN_SIZE), wx.Point(pos.x, pos.y)])
                        next_block.RefreshModel()
                    else:
                        if value["type"] == SELECTION_CONVERGENCE:
                            previous = divergence_connectors["outputs"][0]
                        else:
                            transition = self.CreateTransition(divergence_connectors["outputs"][0])
                            transition_connectors = transition.GetConnectors()
                            previous = transition_connectors["output"]
                        self.CreateStep("Step", previous)
                self.RefreshBuffer()
                self.RefreshScrollBars()
                self.Refresh(False)
            dialog.Destroy()
    
    def AddDivergenceBranch(self, divergence):
        if isinstance(divergence, SFC_Divergence):
            if self.GetDrawingMode() == FREEDRAWING_MODE:
                divergence.AddBranch()
                self.RefreshDivergenceModel(divergence)
            else:
                type = divergence.GetType()
                if type in [SELECTION_DIVERGENCE, SIMULTANEOUS_DIVERGENCE]:
                    divergence.AddBranch()
                    divergence_connectors = divergence.GetConnectors()
                    if type == SELECTION_DIVERGENCE:
                        transition = self.CreateTransition(divergence_connectors["outputs"][-1])
                        transition_connectors = transition.GetConnectors()
                        previous = transition_connectors["output"]
                    else:
                        previous = divergence_connectors["outputs"][-1]
                    step = self.CreateStep("Step", previous)
            self.RefreshBuffer()
            self.RefreshScrollBars()
            self.Refresh(False)
    
    def RemoveDivergenceBranch(self, divergence):
        if isinstance(divergence, SFC_Divergence):
            if self.GetDrawingMode() == FREEDRAWING_MODE:
                divergence.RemoveHandledBranch()
                self.RefreshDivergenceModel(divergence)
                self.RefreshBuffer()
                self.RefreshScrollBars()
                self.Refresh(False)
    
    def AddJump(self):
        if isinstance(self.SelectedElement, SFC_Step) and not self.SelectedElement.Output:
            choices = []
            for block in self.Blocks:
                if isinstance(block, SFC_Step):
                    choices.append(block.GetName())
            dialog = wx.SingleChoiceDialog(self.ParentWindow, 
                  _("Add a new jump"), _("Please choose a target"), 
                  choices, wx.DEFAULT_DIALOG_STYLE|wx.OK|wx.CANCEL)
            if dialog.ShowModal() == wx.ID_OK:
                value = dialog.GetStringSelection()
                self.SelectedElement.AddOutput()
                self.RefreshStepModel(self.SelectedElement)
                step_connectors = self.SelectedElement.GetConnectors()
                transition = self.CreateTransition(step_connectors["output"])
                transition_connectors = transition.GetConnectors()
                id = self.GetNewId()
                jump = SFC_Jump(self, value, id)
                pos = transition_connectors["output"].GetPosition(False)
                jump.SetPosition(pos.x, pos.y + SFC_WIRE_MIN_SIZE)
                self.AddBlock(jump)
                self.Controler.AddEditedElementJump(self.TagName, id)
                jump_connector = jump.GetConnector()
                wire = self.ConnectConnectors(jump_connector, transition_connectors["output"])
                transition.RefreshOutputPosition()
                wire.SetPoints([wx.Point(pos.x, pos.y + SFC_WIRE_MIN_SIZE), wx.Point(pos.x, pos.y)])
                self.RefreshJumpModel(jump)
                self.RefreshBuffer()
                self.RefreshScrollBars()
                self.Refresh(False)
            dialog.Destroy()

    def EditStepContent(self, step):
        if self.GetDrawingMode() == FREEDRAWING_MODE:
            Viewer.EditStepContent(self, step)
        else:
            dialog = SFCStepNameDialog(self.ParentWindow, _("Edit step name"), _("Please enter step name"), step.GetName(), wx.OK|wx.CANCEL)
            dialog.SetPouNames(self.Controler.GetProjectPouNames(self.Debug))
            dialog.SetVariables(self.Controler.GetEditedElementInterfaceVars(self.TagName, debug=self.Debug))
            dialog.SetStepNames([block.GetName() for block in self.Blocks if isinstance(block, SFC_Step) and block.GetName() != step.GetName()])
            if dialog.ShowModal() == wx.ID_OK:
                value = dialog.GetValue()
                step.SetName(value)
                min_size = step.GetMinSize()
                size = step.GetSize()
                step.UpdateSize(max(min_size[0], size[0]), max(min_size[1], size[1]))
                step.RefreshModel()
                self.RefreshBuffer()
                self.RefreshScrollBars()
                self.Refresh(False)
            dialog.Destroy()

#-------------------------------------------------------------------------------
#                          Delete element functions
#-------------------------------------------------------------------------------

    def DeleteStep(self, step):
        if self.GetDrawingMode() == FREEDRAWING_MODE:
            Viewer.DeleteStep(self, step)
        else:
            step_connectors = step.GetConnectors()
            if not step.GetInitial() or not step_connectors["output"]:
                previous = step.GetPreviousConnector()
                if previous:
                    previous_block = previous.GetParentBlock()
                else:
                    previous_block = None
                next = step.GetNextConnector()
                if next:
                    next_block = next.GetParentBlock()
                else:
                    next_block = None
                if isinstance(next_block, SFC_Transition):
                    self.RemoveTransition(next_block)
                    next = step.GetNextConnector()
                    if next:
                        next_block = next.GetParentBlock()
                    else:
                        next_block = None
                elif isinstance(previous_block, SFC_Transition):
                    self.RemoveTransition(previous_block)
                    previous = step.GetPreviousConnector()
                    if previous:
                        previous_block = previous.GetParentBlock()
                    else:
                        previous_block = None
                wire = self.RemoveStep(step)
                self.SelectedElement = None
                if next_block:
                    if isinstance(next_block, SFC_Divergence) and next_block.GetType() == SIMULTANEOUS_CONVERGENCE and isinstance(previous_block, SFC_Divergence) and previous_block.GetType() == SIMULTANEOUS_DIVERGENCE:
                        wire.Clean()
                        self.RemoveWire(wire)
                        next_block.RemoveBranch(next)
                        if next_block.GetBranchNumber() < 2:
                            self.DeleteDivergence(next_block)
                        else:
                            next_block.RefreshModel()
                        previous_block.RemoveBranch(previous)
                        if previous_block.GetBranchNumber() < 2:
                            self.DeleteDivergence(previous_block)
                        else:
                            previous_block.RefreshModel()
                    else:
                        pos = previous.GetPosition(False)
                        next_pos = next.GetPosition(False)
                        wire_size = GetWireSize(previous_block)
                        previous_block.RefreshOutputPosition((0, pos.y + wire_size - next_pos.y))
                        wire.SetPoints([wx.Point(pos.x, pos.y + wire_size), wx.Point(pos.x, pos.y)])
                        if isinstance(next_block, SFC_Divergence):
                            next_block.RefreshPosition()
                        previous_block.RefreshOutputModel(True)
                else:
                    if isinstance(previous_block, SFC_Step):
                        previous_block.RemoveOutput()
                        self.RefreshStepModel(previous_block)
                    elif isinstance(previous_block, SFC_Divergence):
                        if previous_block.GetType() in [SELECTION_CONVERGENCE, SIMULTANEOUS_CONVERGENCE]:
                            self.DeleteDivergence(previous_block)
                        else:
                            previous_block.RemoveBranch(previous)
                            if previous_block.GetBranchNumber() < 2:
                                self.DeleteDivergence(previous_block)
                            else:
                                self.RefreshDivergenceModel(previous_block)
        
    def DeleteTransition(self, transition):
        if self.GetDrawingMode() == FREEDRAWING_MODE:
            Viewer.DeleteTransition(self, transition)
        else:
            previous = transition.GetPreviousConnector()
            previous_block = previous.GetParentBlock()
            next = transition.GetNextConnector()
            next_block = next.GetParentBlock()
            if isinstance(previous_block, SFC_Divergence) and previous_block.GetType() == SELECTION_DIVERGENCE and isinstance(next_block, SFC_Divergence) and next_block.GetType() == SELECTION_CONVERGENCE:
                wires = previous.GetWires()
                if len(wires) != 1:
                    return
                wire = wires[0][0]
                wire.Clean()
                self.RemoveWire(wire)
                wires = next.GetWires()
                if len(wires) != 1:
                    return
                wire = wires[0][0]
                wire.Clean()
                self.RemoveWire(wire)
                transition.Clean()
                self.RemoveBlock(transition)
                self.Controler.RemoveEditedElementInstance(self.TagName, transition.GetId())
                previous_block.RemoveBranch(previous)
                if previous_block.GetBranchNumber() < 2:
                    self.DeleteDivergence(previous_block)
                else:
                    self.RefreshDivergenceModel(previous_block)
                next_block.RemoveBranch(next)
                if next_block.GetBranchNumber() < 2:
                    self.DeleteDivergence(next_block)
                else:
                    self.RefreshDivergenceModel(next_block)
            
    def DeleteDivergence(self, divergence):
        if self.GetDrawingMode() == FREEDRAWING_MODE:
            Viewer.DeleteDivergence(self, divergence)
        else:
            connectors = divergence.GetConnectors()
            type = divergence.GetType()
            if type in [SELECTION_CONVERGENCE, SIMULTANEOUS_CONVERGENCE]:
                wires = connectors["outputs"][0].GetWires()
                if len(wires) > 1:
                    return
                elif len(wires) == 1:
                    next = wires[0][0].StartConnected
                    next_block = next.GetParentBlock()
                    wire = wires[0][0]
                    wire.Clean()
                    self.RemoveWire(wire)
                else:
                    next = None
                    next_block = None
                for index, connector in enumerate(connectors["inputs"]):
                    if next and index == 0:
                        wires = connector.GetWires()
                        wire = wires[0][0]
                        previous = wires[0][0].EndConnected
                        wire.Clean()
                        self.RemoveWire(wire)
                    else:
                        if type == SELECTION_CONVERGENCE:
                            wires = connector.GetWires()
                            previous_block = wires[0][0].EndConnected.GetParentBlock()
                            self.RemoveTransition(previous_block)
                        wires = connector.GetWires()
                        wire = wires[0][0]
                        previous_connector = wire.EndConnected
                        previous_block = previous_connector.GetParentBlock()
                        wire.Clean()
                        self.RemoveWire(wire)
                        if isinstance(previous_block, SFC_Step):
                            previous_block.RemoveOutput()
                            self.RefreshStepModel(previous_block)
                        elif isinstance(previous_block, SFC_Divergence):
                            if previous_block.GetType() in [SELECTION_DIVERGENCE, SIMULTANEOUS_DIVERGENCE]:
                                previous_block.RemoveBranch(previous_connector)
                                if previous_block.GetBranchNumber() < 2:
                                    self.DeleteDivergence(previous_block)
                                else:
                                    self.RefreshDivergenceModel(previous_block)
                            else:
                                self.DeleteDivergence(previous_block)
                divergence.Clean()
                self.RemoveBlock(divergence)
                self.Controler.RemoveEditedElementInstance(self.TagName, divergence.GetId())
                if next:
                    wire = self.ConnectConnectors(next, previous)
                    previous_block = previous.GetParentBlock()
                    previous_pos = previous.GetPosition(False)
                    next_pos = next.GetPosition(False)
                    wire_size = GetWireSize(previous_block)
                    previous_block.RefreshOutputPosition((0, previous_pos.y + wire_size - next_pos.y))
                    wire.SetPoints([wx.Point(previous_pos.x, previous_pos.y + wire_size), 
                        wx.Point(previous_pos.x, previous_pos.y)])
                    if isinstance(next_block, SFC_Divergence):
                        next_block.RefreshPosition()
                    previous_block.RefreshOutputModel(True)
            elif divergence.GetBranchNumber() == 1:
                wires = connectors["inputs"][0].GetWires()
                if len(wires) != 1:
                    return
                wire = wires[0][0]
                previous = wire.EndConnected
                previous_block = previous.GetParentBlock()
                wire.Clean()
                self.RemoveWire(wire)
                wires = connectors["outputs"][0].GetWires()
                if len(wires) != 1:
                    return
                wire = wires[0][0]
                next = wire.StartConnected
                next_block = next.GetParentBlock()
                wire.Clean()
                self.RemoveWire(wire)
                divergence.Clean()
                self.RemoveBlock(divergence)
                self.Controler.RemoveEditedElementInstance(self.TagName, divergence.GetId())
                wire = self.ConnectConnectors(next, previous)
                previous_pos = previous.GetPosition(False)
                next_pos = next.GetPosition(False)
                wire_size = GetWireSize(previous_block)
                previous_block.RefreshOutputPosition((previous_pos.x - next_pos.x, previous_pos.y + wire_size - next_pos.y))
                wire.SetPoints([wx.Point(previous_pos.x, previous_pos.y + wire_size), 
                    wx.Point(previous_pos.x, previous_pos.y)])
                if isinstance(next_block, SFC_Divergence):
                    next_block.RefreshPosition()
                previous_block.RefreshOutputModel(True)
            
    def DeleteJump(self, jump):
        if self.GetDrawingMode() == FREEDRAWING_MODE:
            Viewer.DeleteJump(self, jump)
        else:
            previous = jump.GetPreviousConnector()
            previous_block = previous.GetParentBlock()
            if isinstance(previous_block, SFC_Transition):
                self.RemoveTransition(previous_block)
                previous = jump.GetPreviousConnector()
                if previous:
                    previous_block = previous.GetParentBlock()
                else:
                    previous_block = None
            wires = previous.GetWires()
            if len(wires) != 1:
                return
            wire = wires[0][0]
            wire.Clean()
            self.RemoveWire(wire)
            jump.Clean()
            self.RemoveBlock(jump)
            self.Controler.RemoveEditedElementInstance(self.TagName, jump.GetId())
            if isinstance(previous_block, SFC_Step):
                previous_block.RemoveOutput()
                self.RefreshStepModel(previous_block)
            elif isinstance(previous_block, SFC_Divergence):
                if previous_block.GetType() in [SELECTION_CONVERGENCE, SIMULTANEOUS_CONVERGENCE]:
                    self.DeleteDivergence(previous_block)
                else:
                    previous_block.RemoveBranch(previous)
                    if previous_block.GetBranchNumber() < 2:
                        self.DeleteDivergence(previous_block)
                    else:
                        previous_block.RefreshModel()
            
    def DeleteActionBlock(self, actionblock):
        if self.GetDrawingMode() == FREEDRAWING_MODE:
            Viewer.DeleteActionBlock(self, actionblock)
        else:
            connector = actionblock.GetConnector()
            wires = connector.GetWires()
            if len(wires) != 1:
                return
            wire = wires[0][0]
            step = wire.EndConnected.GetParentBlock()
            wire.Clean()
            self.RemoveWire(wire)
            actionblock.Clean()
            self.RemoveBlock(actionblock)
            self.Controler.RemoveEditedElementInstance(self.TagName, actionblock.GetId())
            step.RemoveAction()
            self.RefreshStepModel(step)
            step.RefreshOutputPosition()
            step.RefreshOutputModel(True)
            
    def DeleteWire(self, wire):
        if self.GetDrawingMode() == FREEDRAWING_MODE:
            Viewer.DeleteWire(self, wire)

#-------------------------------------------------------------------------------
#                          Model update functions
#-------------------------------------------------------------------------------

    def RefreshBlockModel(self, block):
        blockid = block.GetId()
        infos = {}
        infos["type"] = block.GetType()
        infos["name"] = block.GetName()
        infos["x"], infos["y"] = block.GetPosition()
        infos["width"], infos["height"] = block.GetSize()
        infos["connectors"] = block.GetConnectors()
        self.Controler.SetEditedElementBlockInfos(self.TagName, blockid, infos)