Adding support for Debugging in PLCOpenEditor
authorlbessard
Sun, 07 Sep 2008 15:29:12 +0200
changeset 253 d9391572655f
parent 252 166ee9d2e233
child 254 27abdcf9a460
Adding support for Debugging in PLCOpenEditor
PLCOpenEditor.py
Viewer.py
graphics/GraphicCommons.py
graphics/LD_Objects.py
graphics/SFC_Objects.py
--- a/PLCOpenEditor.py	Sun Sep 07 15:27:53 2008 +0200
+++ b/PLCOpenEditor.py	Sun Sep 07 15:29:12 2008 +0200
@@ -390,13 +390,11 @@
         
         typestreestyle = wx.TR_HAS_BUTTONS|wx.TR_SINGLE|wx.SUNKEN_BORDER
         if not self.Debug:
-            typestreestyle |= wx.TR_HIDE_ROOT
+            typestreestyle |= wx.TR_EDIT_LABELS
         self.TypesTree = wx.TreeCtrl(id=ID_PLCOPENEDITORTYPESTREE,
                   name='TypesTree', parent=self.TreeNoteBook, 
                   pos=wx.Point(0, 0), size=wx.Size(0, 0),
                   style=typestreestyle)
-        self.TreeNoteBook.AddPage(self.TypesTree, "Types")
-        
         if not self.Debug:
             if wx.Platform == '__WXMSW__':
                 self.Bind(wx.EVT_TREE_ITEM_RIGHT_CLICK, self.OnTypesTreeRightUp,
@@ -425,14 +423,15 @@
                   name='InstancesTree', parent=self.TreeNoteBook, 
                   pos=wx.Point(0, 0), size=wx.Size(0, 0),
                   style=wx.TR_HAS_BUTTONS|wx.TR_SINGLE|wx.SUNKEN_BORDER)
-        self.TreeNoteBook.AddPage(self.InstancesTree, "Instances")
-        
         if self.Debug:
             self.Bind(wx.EVT_TREE_BEGIN_DRAG, self.OnInstancesTreeBeginDrag,
                   id=ID_PLCOPENEDITORINSTANCESTREE)
             self.Bind(wx.EVT_TREE_ITEM_ACTIVATED, self.OnInstancesTreeItemActivated,
                   id=ID_PLCOPENEDITORINSTANCESTREE)
             
+            self.TreeNoteBook.AddPage(self.InstancesTree, "Instances")
+            self.TreeNoteBook.AddPage(self.TypesTree, "Types")
+            
             if wx.VERSION < (2, 8, 0):
                 self.TabsOpened = wx.Notebook(id=ID_PLCOPENEDITORTABSOPENED,
                       name='TabsOpened', parent=self.MainSplitter, pos=wx.Point(0,
@@ -451,6 +450,9 @@
                         self.OnPouSelectedChanged)
                 self.AUIManager.AddPane(self.TabsOpened, wx.aui.AuiPaneInfo().CentrePane())
         else:
+            self.TreeNoteBook.AddPage(self.TypesTree, "Types")
+            self.TreeNoteBook.AddPage(self.InstancesTree, "Instances")
+            
             if wx.VERSION < (2, 8, 0):
                 self.ToolBar = self.CreateToolBar(wx.TB_HORIZONTAL|wx.TB_FLAT|wx.NO_BORDER, 
                       ID_PLCOPENEDITORTOOLBAR, 'ToolBar')
@@ -651,10 +653,13 @@
         return self.DrawingMode
 
     def RefreshTitle(self):
+        name = "PLCOpenEditor"
+        if self.Debug:
+            name += " (Debug)"
         if self.Controler.HasOpenedProject() > 0:
-            self.SetTitle("PLCOpenEditor - %s"%self.Controler.GetFilename())
-        else:
-            self.SetTitle("PLCOpenEditor")
+            self.SetTitle("%s - %s"%(name, self.Controler.GetFilename()))
+        else:
+            self.SetTitle(name)
 
     def ShowProperties(self):
         old_values = self.Controler.GetProjectProperties(self.Debug)
@@ -3643,7 +3648,9 @@
                             editor = wx.grid.GridCellChoiceEditor()
                             excluded = []
                             if self.Parent.PouIsUsed:
-                                excluded.extend(["Input","Output","InOut"])    
+                                excluded.extend(["Input","Output","InOut"])
+                            if self.Parent.IsFunctionBlockType(self.data[row]["Type"]):
+                                excluded.extend(["Local","Temp"])
                             editor.SetParameters(",".join([choice for choice in self.Parent.ClassList if choice not in excluded]))
                     elif colname in ["Retain", "Constant"]:
                         editor = wx.grid.GridCellChoiceEditor()
@@ -3960,6 +3967,14 @@
     def SetTagName(self, tagname):
         self.TagName = tagname
     
+    def IsFunctionBlockType(self, name):
+        bodytype = self.Controler.GetEditedElementBodyType(self.TagName, self.ParentWindow.Debug)
+        pouname, poutype = self.Controler.GetEditedElementType(self.TagName, self.ParentWindow.Debug)
+        if poutype != "function" and bodytype in ["ST", "IL"]:
+            return False
+        else:
+            return name in self.Controler.GetFunctionBlockTypes(self.TagName, self.ParentWindow.Debug)
+    
     def RefreshView(self):
         self.PouNames = self.Controler.GetProjectPouNames(self.ParentWindow.Debug)
         
@@ -4099,6 +4114,7 @@
     
     def OnVariablesGridEditorShown(self, event):
         row, col = event.GetRow(), event.GetCol() 
+        classtype = self.Table.GetValueByName(row, "Class")
         if self.Table.GetColLabelValue(col) == "Type":
             type_menu = wx.Menu(title='')
             base_menu = wx.Menu(title='')
@@ -4108,17 +4124,20 @@
                 self.Bind(wx.EVT_MENU, self.GetVariableTypeFunction(base_type), id=new_id)
             type_menu.AppendMenu(wx.NewId(), "Base Types", base_menu)
             datatype_menu = wx.Menu(title='')
-            for datatype in self.Controler.GetDataTypes(basetypes = False, debug = self.Debug):
+            for datatype in self.Controler.GetDataTypes(basetypes = False, debug = self.ParentWindow.Debug):
                 new_id = wx.NewId()
                 AppendMenu(datatype_menu, help='', id=new_id, kind=wx.ITEM_NORMAL, text=datatype)
                 self.Bind(wx.EVT_MENU, self.GetVariableTypeFunction(datatype), id=new_id)
             type_menu.AppendMenu(wx.NewId(), "User Data Types", datatype_menu)
             functionblock_menu = wx.Menu(title='')
-            for functionblock_type in self.Controler.GetFunctionBlockTypes(debug = self.Debug):
-                new_id = wx.NewId()
-                AppendMenu(functionblock_menu, help='', id=new_id, kind=wx.ITEM_NORMAL, text=functionblock_type)
-                self.Bind(wx.EVT_MENU, self.GetVariableTypeFunction(functionblock_type), id=new_id)
-            type_menu.AppendMenu(wx.NewId(), "Function Block Types", functionblock_menu)
+            bodytype = self.Controler.GetEditedElementBodyType(self.TagName, self.ParentWindow.Debug)
+            pouname, poutype = self.Controler.GetEditedElementType(self.TagName, self.ParentWindow.Debug)
+            if classtype in ["Input","Output","InOut","External","Global"] or poutype != "function" and bodytype in ["ST", "IL"]:
+                for functionblock_type in self.Controler.GetFunctionBlockTypes(self.TagName, self.ParentWindow.Debug):
+                    new_id = wx.NewId()
+                    AppendMenu(functionblock_menu, help='', id=new_id, kind=wx.ITEM_NORMAL, text=functionblock_type)
+                    self.Bind(wx.EVT_MENU, self.GetVariableTypeFunction(functionblock_type), id=new_id)
+                type_menu.AppendMenu(wx.NewId(), "Function Block Types", functionblock_menu)
             rect = self.VariablesGrid.BlockToDeviceRect((row, col), (row, col))
             self.VariablesGrid.PopupMenuXY(type_menu, rect.x + rect.width, rect.y + self.VariablesGrid.GetColLabelSize())
             event.Veto()
--- a/Viewer.py	Sun Sep 07 15:27:53 2008 +0200
+++ b/Viewer.py	Sun Sep 07 15:29:12 2008 +0200
@@ -102,10 +102,13 @@
                 message = "Programs can't be used by other POUs!"
             elif values[1] in ["function", "functionBlock", "program"]:
                 name, type = self.ParentWindow.Controler.GetEditedElementType(self.ParentWindow.GetTagName(), self.ParentWindow.Debug)
+                words = self.ParentWindow.TagName.split("::")
                 if name == values[0]:
                     message = "\"%s\" can't use itself!"%name
                 elif type == "function" and values[1] != "function":
-                    message = "Function Blocks can't be used by Functions!"
+                    message = "Function Blocks can't be used in Functions!"
+                elif words[0] == "T" and values[1] != "function":
+                    message = "Function Blocks can't be used in Transitions!"
                 elif self.ParentWindow.Controler.PouIsUsedBy(name, values[0], self.ParentWindow.Debug):
                     message = "\"%s\" is already used by \"%s\"!"%(name, values[0])
                 else:
@@ -351,6 +354,7 @@
         self.Bind(wx.EVT_MOTION, self.OnViewerMotion)
         self.Bind(wx.EVT_CHAR, self.OnChar)
         self.Bind(wx.EVT_SCROLLWIN, self.OnScrollWindow)
+        self.Bind(wx.EVT_MOUSEWHEEL, self.OnMouseWheelWindow)
         self.Bind(wx.EVT_SIZE, self.OnMoveWindow)
     
     def GetScrolledRect(self, rect):
@@ -397,6 +401,10 @@
             self.PrepareDC(dc)
         return dc
 
+    def GetMiniFont(self):
+        font = self.GetFont()
+        return wx.Font(font.GetPointSize() * 0.75, wx.SWISS, wx.NORMAL, wx.NORMAL, faceName = faces["helv"])
+
 #-------------------------------------------------------------------------------
 #                         Element management functions
 #-------------------------------------------------------------------------------
@@ -574,23 +582,70 @@
                         if connectorname == "":
                             connectorname = "OUT"
                         iec_path = "%s.%s%d_%s"%(self.InstancePath, block.GetType(), block.GetId(), connectorname)
-                    self.Subscribed[wire] = iec_path.upper()
-                    self.Controler.SubscribeDebugIECVariable(iec_path.upper(), wire)
+                    if self.Controler.SubscribeDebugIECVariable(iec_path.upper(), wire) is not None:
+                        self.Subscribed[wire] = iec_path.upper()
+                    else:
+                        wire.SetValue("undefined")
+                elif isinstance(block, FBD_Variable):
+                    iec_path = "%s.%s"%(self.InstancePath, block.GetName())
+                    if self.Controler.SubscribeDebugIECVariable(iec_path.upper(), wire) is not None:
+                        self.Subscribed[wire] = iec_path.upper()
+                    else:
+                        wire.SetValue("undefined")
+                elif isinstance(block, FBD_Connector):
+                    wire.SetValue("undefined")
 
         if self.Debug:
             for block in self.Blocks.keys():
-                if isinstance(block, (LD_Contact, LD_Coil)):
-                    block.SetValue(False)
-                    block.SpreadCurrent()
+                block.SpreadCurrent()
                 if isinstance(block, LD_Contact):
                     iec_path = "%s.%s"%(self.InstancePath, block.GetName())
-                    self.Subscribed[block] = iec_path.upper()
-                    self.Controler.SubscribeDebugIECVariable(iec_path.upper(), block)
-        
+                    if self.Controler.SubscribeDebugIECVariable(iec_path.upper(), block) is not None:
+                        self.Subscribed[block] = iec_path.upper()
+                elif isinstance(block, SFC_Step):
+                    iec_path = "%s.%s.X"%(self.InstancePath, block.GetName())
+                    if self.Controler.SubscribeDebugIECVariable(iec_path.upper(), block) is not None:
+                        self.Subscribed[block] = iec_path.upper()
+                elif isinstance(block, SFC_Transition):
+                    connectors = block.GetConnectors()
+                    previous_steps = self.GetPreviousSteps(connectors["input"])
+                    next_steps = self.GetNextSteps(connectors["output"])
+                    iec_path = "%s.%s->%s"%(self.InstancePath, ",".join(previous_steps), ",".join(next_steps))
+                    if self.Controler.SubscribeDebugIECVariable(iec_path.upper(), block) is not None:
+                        self.Subscribed[block] = iec_path.upper()
+                    else:
+                        print "Unknown IEC_Path", iec_path
+                
         self.RefreshVisibleElements()
         self.ShowErrors()
         self.Refresh(False)
     
+    def GetPreviousSteps(self, connector):
+        steps = []
+        for wire, handle in connector.GetWires():
+            previous = wire.GetOtherConnected(connector).GetParentBlock()
+            if isinstance(previous, SFC_Step):
+                steps.append(previous.GetName())
+            elif isinstance(previous, SFC_Divergence) and previous.GetType() in [SIMULTANEOUS_CONVERGENCE, SELECTION_DIVERGENCE]:
+                connectors = previous.GetConnectors()
+                for input in connectors["inputs"]:
+                    steps.extend(self.GetPreviousSteps(input))
+        return steps
+    
+    def GetNextSteps(self, connector):
+        steps = []
+        for wire, handle in connector.GetWires():
+            next = wire.GetOtherConnected(connector).GetParentBlock()
+            if isinstance(next, SFC_Step):
+                steps.append(next.GetName())
+            elif isinstance(next, SFC_Jump):
+                steps.append(next.GetTarget())
+            elif isinstance(next, SFC_Divergence) and next.GetType() in [SIMULTANEOUS_DIVERGENCE, SELECTION_CONVERGENCE]:
+                connectors = next.GetConnectors()
+                for output in connectors["outputs"]:
+                    steps.extend(self.GetNextSteps(output))
+        return steps
+    
     def GetMaxSize(self):
         maxx = maxy = 0
         for element in self.GetElements():
@@ -1525,7 +1580,11 @@
         dialog.Destroy()
     
     def AddNewVariable(self, bbox):
-        dialog = VariablePropertiesDialog(self.ParentWindow, self.Controler)
+        words = self.TagName.split("::")
+        if words[0] == "T":
+            dialog = VariablePropertiesDialog(self.ParentWindow, self.Controler, words[2])
+        else:
+            dialog = VariablePropertiesDialog(self.ParentWindow, self.Controler)
         dialog.SetPreviewFont(self.GetFont())
         dialog.SetMinVariableSize((bbox.width, bbox.height))
         varlist = []
@@ -1827,7 +1886,11 @@
         dialog.Destroy()
 
     def EditVariableContent(self, variable):
-        dialog = VariablePropertiesDialog(self.ParentWindow, self.Controler)
+        words = self.TagName.split("::")
+        if words[0] == "T":
+            dialog = VariablePropertiesDialog(self.ParentWindow, self.Controler, words[2])
+        else:
+            dialog = VariablePropertiesDialog(self.ParentWindow, self.Controler)
         dialog.SetPreviewFont(self.GetFont())
         dialog.SetMinVariableSize(variable.GetSize())
         varlist = []
@@ -2470,6 +2533,12 @@
             self.RefreshVisibleElements(yp = event.GetPosition())
         event.Skip()
 
+    def OnMouseWheelWindow(self, event):
+        x, y = self.GetViewStart()
+        yp = max(0, min(y - event.GetWheelRotation() / event.GetWheelDelta() * 3, self.GetVirtualSize()[1] / self.GetScrollPixelsPerUnit()[1]))
+        self.RefreshVisibleElements(yp = yp)
+        self.Scroll(x, yp)
+        
     def OnMoveWindow(self, event):
         self.GetBestSize()
         self.RefreshScrollBars()
--- a/graphics/GraphicCommons.py	Sun Sep 07 15:27:53 2008 +0200
+++ b/graphics/GraphicCommons.py	Sun Sep 07 15:29:12 2008 +0200
@@ -976,8 +976,10 @@
         current = False
         for wire, handle in self.Wires:
             value = wire.GetValue()
-            if isinstance(value, BooleanType):
+            if current != "undefined" and isinstance(value, BooleanType):
                 current |= wire.GetValue()
+            elif value == "undefined":
+                current = "undefined"
         return current
     
     def SpreadCurrent(self, spreading):
@@ -1195,6 +1197,8 @@
                 dc.SetPen(wx.RED_PEN)
             elif isinstance(self.Value, BooleanType) and self.Value:
                 dc.SetPen(wx.GREEN_PEN)
+            elif self.Value == "undefined":
+                dc.SetPen(wx.Pen(wx.NamedColour("orange")))
             else:
                 dc.SetPen(self.Pen)
             dc.SetBrush(wx.WHITE_BRUSH)
@@ -1271,11 +1275,11 @@
         self.Valid = True
         self.Value = None
         self.ValueSize = None
+        self.ComputedValue = None
         self.OverStart = False
         self.OverEnd = False
         self.ComputingType = False
-        parent_font = parent.GetFont()
-        self.Font = wx.Font(parent_font.GetPointSize() * 0.75, wx.SWISS, wx.NORMAL, wx.NORMAL, faceName = parent_font.GetFaceName())
+        self.Font = parent.GetMiniFont()
         
     def Flush(self):
         self.StartConnected = None
@@ -1376,19 +1380,19 @@
         return False
     
     def SetValue(self, value):
-        if value is not None and not isinstance(value, BooleanType):
-            if isinstance(value, StringType):
-                value = "\"%s\""%value
-            else:
-                value = str(value)
-            if len(value) > 4:
-                value = value[:4] + "_"
         if self.Value != value:
             self.Value = value
-            if isinstance(self.Value, StringType):
+            if value is not None and not isinstance(value, BooleanType):
+                if isinstance(value, StringType):
+                    self.ComputedValue = "\"%s\""%value
+                else:
+                    self.ComputedValue = str(value)
+                if len(self.ComputedValue) > 4:
+                    self.ComputedValue = self.ComputedValue[:4] + "..."            
+            if isinstance(self.ComputedValue, StringType):
                 dc = wx.ClientDC(self.Parent)
                 dc.SetFont(self.Font)
-                self.ValueSize = dc.GetTextExtent(self.Value)
+                self.ValueSize = dc.GetTextExtent(self.ComputedValue)
             else:
                 self.ValueSize = None
             if self.StartConnected:
@@ -2183,6 +2187,8 @@
             dc.SetPen(wx.RED_PEN)
         elif isinstance(self.Value, BooleanType) and self.Value:
             dc.SetPen(wx.GREEN_PEN)
+        elif self.Value == "undefined":
+            dc.SetPen(wx.Pen(wx.NamedColour("orange")))
         else:
             dc.SetPen(wx.BLACK_PEN)
         dc.SetBrush(wx.BLACK_BRUSH)
@@ -2201,22 +2207,22 @@
                         self.Points[self.SelectedSegment + 1].x, self.Points[self.SelectedSegment + 1].y)
             if self.SelectedSegment == len(self.Segments) - 1:
                 dc.DrawPoint(self.Points[-1].x, self.Points[-1].y)
-        if self.Value is not None and not isinstance(self.Value, BooleanType):
+        if self.Value is not None and not isinstance(self.Value, BooleanType) and self.Value != "undefined":
             dc.SetFont(self.Font)
             dc.SetTextForeground(wx.NamedColour("purple"))
             width, height = self.ValueSize
             if self.BoundingBox[2] > width * 4 or self.BoundingBox[3] > height * 4:
-                x = self.Points[0].x + width * self.StartPoint[1][0] / 2
+                x = self.Points[0].x + width * (self.StartPoint[1][0] - 1) / 2
                 y = self.Points[0].y + height * (self.StartPoint[1][1] - 1) / 2
-                dc.DrawText(self.Value, x, y)
-                x = self.Points[-1].x + width * self.EndPoint[1][0] / 2
+                dc.DrawText(self.ComputedValue, x, y)
+                x = self.Points[-1].x + width * (self.EndPoint[1][0] - 1) / 2
                 y = self.Points[-1].y + height * (self.EndPoint[1][1] - 1) / 2
-                dc.DrawText(self.Value, x, y)
+                dc.DrawText(self.ComputedValue, x, y)
             else:
                 middle = len(self.Segments) / 2 + len(self.Segments) % 2 - 1
                 x = (self.Points[middle].x + self.Points[middle + 1].x - width) / 2
                 y = (self.Points[middle].y + self.Points[middle + 1].y - height) / 2
-                dc.DrawText(self.Value, x, y)
+                dc.DrawText(self.ComputedValue, x, y)
             dc.SetFont(self.Parent.GetFont())
             dc.SetTextForeground(wx.BLACK)
 
--- a/graphics/LD_Objects.py	Sun Sep 07 15:27:53 2008 +0200
+++ b/graphics/LD_Objects.py	Sun Sep 07 15:29:12 2008 +0200
@@ -400,8 +400,10 @@
             self.SpreadCurrent()
     
     def SpreadCurrent(self):
-        spreading = self.Input.ReceivingCurrent()
-        if self.Value is not None:
+        if self.Parent.Debug:
+            if self.Value is None:
+                self.Value = False
+            spreading = self.Input.ReceivingCurrent()
             if self.Type == CONTACT_NORMAL:
                 spreading &= self.Value
             elif self.Type == CONTACT_REVERSE:
@@ -412,11 +414,11 @@
                 spreading &= self.Value and not self.PreviousValue
             else:
                 spreading = False
-        if spreading and not self.PreviousSpreading:
-            self.Output.SpreadCurrent(True)
-        elif not spreading and self.PreviousSpreading:
-            self.Output.SpreadCurrent(False)
-        self.PreviousSpreading = spreading
+            if spreading and not self.PreviousSpreading:
+                self.Output.SpreadCurrent(True)
+            elif not spreading and self.PreviousSpreading:
+                self.Output.SpreadCurrent(False)
+            self.PreviousSpreading = spreading
     
     # Make a clone of this LD_Contact
     def Clone(self, parent, id = None, pos = None):
@@ -687,20 +689,16 @@
             self.Output.Flush()
             self.Output = None
     
-    def SetValue(self, value):
-        if self.Value != value:
-            self.Value = value
-            self.Refresh()
-    
     def SpreadCurrent(self):
-        self.PreviousValue = self.Value
-        self.Value = self.Input.ReceivingCurrent()
-        if self.Value and not self.PreviousValue:
-            self.Output.SpreadCurrent(True)
-        elif not self.Value and self.PreviousValue:
-            self.Output.SpreadCurrent(False)
-        if self.Value != self.PreviousValue:
-            self.Refresh()
+        if self.Parent.Debug:
+            self.PreviousValue = self.Value
+            self.Value = self.Input.ReceivingCurrent()
+            if self.Value and not self.PreviousValue:
+                self.Output.SpreadCurrent(True)
+            elif not self.Value and self.PreviousValue:
+                self.Output.SpreadCurrent(False)
+            if self.Value != self.PreviousValue:
+                self.Refresh()
     
     # Make a clone of this LD_Coil
     def Clone(self, parent, id = None, pos = None):
--- a/graphics/SFC_Objects.py	Sun Sep 07 15:27:53 2008 +0200
+++ b/graphics/SFC_Objects.py	Sun Sep 07 15:29:12 2008 +0200
@@ -58,6 +58,9 @@
             self.Input = None
         self.Output = None
         self.Action = None
+        self.Value = None
+        self.PreviousValue = None
+        self.PreviousSpreading = False
     
     def Flush(self):
         if self.Input is not None:
@@ -70,6 +73,26 @@
             self.Action.Flush()
             self.Action = None
     
+    def SetValue(self, value):
+        self.PreviousValue = self.Value
+        self.Value = value
+        if self.Value != self.PreviousValue:
+            self.Refresh()
+            self.SpreadCurrent()
+    
+    def SpreadCurrent(self):
+        if self.Parent.Debug:
+            spreading = self.Value
+            if spreading and not self.PreviousSpreading:
+                self.Output.SpreadCurrent(True)
+                if self.Action is not None:
+                    self.Action.SpreadCurrent(True)
+            elif not spreading and self.PreviousSpreading:
+                self.Output.SpreadCurrent(False)
+                if self.Action is not None:
+                    self.Action.SpreadCurrent(False)
+            self.PreviousSpreading = spreading
+    
     # Make a clone of this SFC_Step
     def Clone(self, parent, id = None, name = "Step", pos = None):
         step = SFC_Step(parent, name, self.Initial, id)
@@ -472,7 +495,10 @@
     # Draws step
     def Draw(self, dc):
         Graphic_Element.Draw(self, dc)
-        dc.SetPen(wx.BLACK_PEN)
+        if self.Value:
+            dc.SetPen(wx.GREEN_PEN)
+        else:
+            dc.SetPen(wx.BLACK_PEN)
         dc.SetBrush(wx.WHITE_BRUSH)
         
         if getattr(dc, "printing", False):
@@ -522,6 +548,9 @@
         self.SetType(type, condition)
         self.SetPriority(priority)
         self.Errors = {}
+        self.Value = None
+        self.PreviousValue = None
+        self.PreviousSpreading = False
     
     def Flush(self):
         if self.Input is not None:
@@ -534,6 +563,24 @@
             self.Condition.Flush()
             self.Condition = None
     
+    def SetValue(self, value):
+        self.PreviousValue = self.Value
+        self.Value = value
+        if self.Value != self.PreviousValue:
+            self.Refresh()
+            self.SpreadCurrent()
+    
+    def SpreadCurrent(self):
+        if self.Parent.Debug:
+            if self.Value is None:
+                self.Value = False
+            spreading = self.Input.ReceivingCurrent() & self.Value
+            if spreading and not self.PreviousSpreading:
+                self.Output.SpreadCurrent(True)
+            elif not spreading and self.PreviousSpreading:
+                self.Output.SpreadCurrent(False)
+            self.PreviousSpreading = spreading
+    
     # Make a clone of this SFC_Transition
     def Clone(self, parent, id = None, pos = None):
         transition = SFC_Transition(parent, self.Type, self.Condition, self.Priority, id)
@@ -851,8 +898,12 @@
     # Draws transition
     def Draw(self, dc):
         Graphic_Element.Draw(self, dc)
-        dc.SetPen(wx.BLACK_PEN)
-        dc.SetBrush(wx.BLACK_BRUSH)
+        if self.Value:
+            dc.SetPen(wx.GREEN_PEN)
+            dc.SetBrush(wx.GREEN_BRUSH)
+        else:
+            dc.SetPen(wx.BLACK_PEN)
+            dc.SetBrush(wx.BLACK_BRUSH)
         
         if getattr(dc, "printing", False):
             if self.Type != "connection":
@@ -929,6 +980,8 @@
             for i in xrange(number):
                 self.Inputs.append(Connector(self, "", None, wx.Point(i * SFC_DEFAULT_SEQUENCE_INTERVAL, 0), NORTH, onlyone = True))
             self.Outputs = [Connector(self, "", None, wx.Point(self.Size[0] / 2, self.Size[1]), SOUTH, onlyone = True)]
+        self.Value = None
+        self.PreviousValue = None
     
     def Flush(self):
         for input in self.Inputs:
@@ -938,6 +991,30 @@
             output.Flush()
         self.Outputs = []
     
+    def SpreadCurrent(self):
+        if self.Parent.Debug:
+            self.PreviousValue = self.Value
+            if self.Type == SELECTION_CONVERGENCE:
+                self.Value = False
+                for input in self.Inputs:
+                    self.Value |= input.ReceivingCurrent()
+            elif self.Type == SIMULTANEOUS_CONVERGENCE:
+                self.Value = True
+                for input in self.Inputs:
+                    self.Value &= input.ReceivingCurrent()
+            elif self.Type in [SELECTION_DIVERGENCE, SIMULTANEOUS_DIVERGENCE]:
+                self.Value = self.Inputs[0].ReceivingCurrent()
+            else:
+                self.Value = False
+            if self.Value and not self.PreviousValue:
+                self.Refresh()
+                for output in self.Outputs:
+                    output.SpreadCurrent(True)
+            elif not self.Value and self.PreviousValue:
+                self.Refresh()
+                for output in self.Outputs:
+                    output.SpreadCurrent(False)
+    
     # Make a clone of this SFC_Divergence
     def Clone(self, parent, id = None, pos = None):
         divergence = SFC_Divergence(parent, self.Type, max(len(self.Inputs), len(self.Outputs)), id)
@@ -1313,8 +1390,12 @@
     # Draws divergence
     def Draw(self, dc):
         Graphic_Element.Draw(self, dc)
-        dc.SetPen(wx.BLACK_PEN)
-        dc.SetBrush(wx.BLACK_BRUSH)
+        if self.Value:
+            dc.SetPen(wx.GREEN_PEN)
+            dc.SetBrush(wx.GREEN_BRUSH)
+        else:
+            dc.SetPen(wx.BLACK_PEN)
+            dc.SetBrush(wx.BLACK_BRUSH)
         # Draw plain rectangle for representing the divergence
         if self.Type in [SELECTION_DIVERGENCE, SELECTION_CONVERGENCE]:
             dc.DrawRectangle(self.Pos.x, self.Pos.y, self.Size[0] + 1, self.Size[1] + 1)
@@ -1349,12 +1430,21 @@
         self.Errors = {}
         # Create an input and output connector
         self.Input = Connector(self, "", None, wx.Point(self.Size[0] / 2, 0), NORTH, onlyone = True)
+        self.Value = None
+        self.PreviousValue = None
         
     def Flush(self):
         if self.Input is not None:
             self.Input.Flush()
             self.Input = None
     
+    def SpreadCurrent(self):
+        if self.Parent.Debug:
+            self.PreviousValue = self.Value
+            self.Value = self.Input.ReceivingCurrent()
+            if self.Value != self.PreviousValue:
+                self.Refresh()
+    
     # Make a clone of this SFC_Jump
     def Clone(self, parent, id = None, pos = None):
         jump = SFC_Jump(parent, self.Target, id)
@@ -1531,8 +1621,12 @@
     # Draws divergence
     def Draw(self, dc):
         Graphic_Element.Draw(self, dc)
-        dc.SetPen(wx.BLACK_PEN)
-        dc.SetBrush(wx.BLACK_BRUSH)
+        if self.Value:
+            dc.SetPen(wx.GREEN_PEN)
+            dc.SetBrush(wx.GREEN_BRUSH)
+        else:
+            dc.SetPen(wx.BLACK_PEN)
+            dc.SetBrush(wx.BLACK_BRUSH)
         
         if getattr(dc, "printing", False):
             target_size = dc.GetTextExtent(self.Target)
@@ -1576,12 +1670,21 @@
         # Create an input and output connector
         self.Input = Connector(self, "", None, wx.Point(0, SFC_ACTION_MIN_SIZE[1] / 2), WEST, onlyone = True)
         self.SetActions(actions)
+        self.Value = None
+        self.PreviousValue = None
     
     def Flush(self):
         if self.Input is not None:
             self.Input.Flush()
             self.Input = None
     
+    def SpreadCurrent(self):
+        if self.Parent.Debug:
+            self.PreviousValue = self.Value
+            self.Value = self.Input.ReceivingCurrent()
+            if self.Value != self.PreviousValue:
+                self.Refresh()
+    
     # Make a clone of this SFC_ActionBlock
     def Clone(self, parent, id = None, pos = None):
         actions = [action.copy() for action in self.Actions]
@@ -1751,7 +1854,10 @@
     # Draws divergence
     def Draw(self, dc):
         Graphic_Element.Draw(self, dc)
-        dc.SetPen(wx.BLACK_PEN)
+        if self.Value:
+            dc.SetPen(wx.GREEN_PEN)
+        else:
+            dc.SetPen(wx.BLACK_PEN)
         dc.SetBrush(wx.WHITE_BRUSH)
         colsize = [self.ColSize[0], self.Size[0] - self.ColSize[0] - self.ColSize[2], self.ColSize[2]]
         # Draw plain rectangle for representing the action block