Adding support for execution order in PLCGenerator
authorlbessard
Thu, 06 Dec 2007 18:05:29 +0100
changeset 125 394d9f168258
parent 124 635d0817508c
child 126 4a76987f8a40
Adding support for execution order in PLCGenerator
Adding support for derived data types (struct not supported yet)
Fixed refresh bug with windows on Viewers
DataTypeEditor.py
PLCControler.py
PLCGenerator.py
PLCOpenEditor.py
TextViewer.py
Viewer.py
plcopen/plcopen.py
plcopen/structures.py
xmlclass/xmlclass.py
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/DataTypeEditor.py	Thu Dec 06 18:05:29 2007 +0100
@@ -0,0 +1,488 @@
+#!/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
+
+import wx
+import wx.grid
+import wx.gizmos
+from plcopen.structures import GetDataTypeRange, IEC_KEYWORDS
+
+if wx.VERSION >= (2, 8, 0):
+    import wx.aui
+
+    class MDIDataTypeEditor(wx.aui.AuiMDIChildFrame):
+        def __init__(self, parent, tagname, window, controler):
+            wx.aui.AuiMDIChildFrame.__init__(self, parent, -1, title = "")
+            
+            sizer = wx.BoxSizer(wx.HORIZONTAL)
+            
+            self.Viewer = DataTypeEditor(self, tagname, window, controler)
+            
+            sizer.AddWindow(self.Viewer, 1, border=0, flag=wx.GROW)
+            
+            self.SetSizer(sizer)
+        
+        def GetViewer(self):
+            return self.Viewer
+
+#-------------------------------------------------------------------------------
+#                          Configuration Editor class
+#-------------------------------------------------------------------------------
+
+[ID_DATATYPEEDITOR, ID_DATATYPEEDITORSTATICBOX,
+ ID_DATATYPEEDITORDERIVATIONTYPE, ID_DATATYPEEDITORDIRECTLYPANEL,
+ ID_DATATYPEEDITORSUBRANGEPANEL, ID_DATATYPEEDITORDIRECTLYBASETYPE, 
+ ID_DATATYPEEDITORSUBRANGEBASETYPE, ID_DATATYPEEDITORSUBRANGEMINIMUM, 
+ ID_DATATYPEEDITORSUBRANGEMAXIMUM, ID_DATATYPEEDITORDIRECTLYINITIALVALUE, 
+ ID_DATATYPEEDITORSUBRANGEINITIALVALUE, ID_DATATYPEEDITORENUMERATEDPANEL,
+ ID_DATATYPEEDITORENUMERATEDVALUES, ID_DATATYPEEDITORENUMERATEDINITIALVALUE,
+ ID_DATATYPEEDITORARRAYPANEL, ID_DATATYPEEDITORARRAYBASETYPE, 
+ ID_DATATYPEEDITORARRAYDIMENSIONS, ID_DATATYPEEDITORARRAYINITIALVALUE, 
+ ID_DATATYPEEDITORSTATICTEXT1, ID_DATATYPEEDITORSTATICTEXT2, 
+ ID_DATATYPEEDITORSTATICTEXT3, ID_DATATYPEEDITORSTATICTEXT4, 
+ ID_DATATYPEEDITORSTATICTEXT5, ID_DATATYPEEDITORSTATICTEXT6, 
+ ID_DATATYPEEDITORSTATICTEXT7, ID_DATATYPEEDITORSTATICTEXT8,
+ ID_DATATYPEEDITORSTATICTEXT9, ID_DATATYPEEDITORSTATICTEXT10, 
+] = [wx.NewId() for _init_ctrls in range(28)]
+
+class DataTypeEditor(wx.Panel):
+    
+    def _init_coll_MainSizer_Items(self, parent):
+        parent.AddSizer(self.TopSizer, 0, border=5, flag=wx.GROW|wx.TOP|wx.LEFT|wx.RIGHT)
+        parent.AddSizer(self.TypeInfosSizer, 0, border=5, flag=wx.GROW|wx.BOTTOM|wx.LEFT|wx.RIGHT)
+
+    def _init_coll_MainSizer_Growables(self, parent):
+        parent.AddGrowableCol(0)
+        parent.AddGrowableRow(1)
+
+    def _init_coll_TopSizer_Items(self, parent):
+        parent.AddWindow(self.staticText1, 0, border=5, flag=wx.GROW|wx.LEFT)
+        parent.AddWindow(self.DerivationType, 0, border=0, flag=wx.GROW)
+
+    def _init_coll_TypeInfosSizer_Items(self, parent):
+        parent.AddWindow(self.DirectlyPanel, 1, border=0, flag=wx.ALL)
+        parent.AddWindow(self.SubrangePanel, 1, border=0, flag=wx.ALL)
+        parent.AddWindow(self.EnumeratedPanel, 1, border=0, flag=wx.GROW|wx.ALL)
+        parent.AddWindow(self.ArrayPanel, 1, border=0, flag=wx.ALL)
+
+    def _init_coll_DirectlyPanelSizer_Items(self, parent):
+        parent.AddWindow(self.staticText2, 1, border=5, flag=wx.GROW|wx.ALL)
+        parent.AddWindow(self.DirectlyBaseType, 1, border=5, flag=wx.GROW|wx.ALL)
+        parent.AddWindow(self.staticText3, 1, border=5, flag=wx.GROW|wx.ALL)
+        parent.AddWindow(self.DirectlyInitialValue, 1, border=5, flag=wx.GROW|wx.ALL)
+
+    def _init_coll_SubrangePanelSizer_Items(self, parent):
+        parent.AddWindow(self.staticText4, 1, border=5, flag=wx.GROW|wx.ALL)
+        parent.AddWindow(self.SubrangeBaseType, 1, border=5, flag=wx.GROW|wx.ALL)
+        parent.AddWindow(self.staticText5, 1, border=5, flag=wx.GROW|wx.ALL)
+        parent.AddWindow(self.SubrangeInitialValue, 1, border=5, flag=wx.GROW|wx.ALL)
+        parent.AddWindow(self.staticText6, 1, border=5, flag=wx.GROW|wx.ALL)
+        parent.AddWindow(self.SubrangeMinimum, 1, border=5, flag=wx.GROW|wx.ALL)
+        parent.AddWindow(wx.Size(0, 0), 1, border=5, flag=wx.GROW|wx.ALL)
+        parent.AddWindow(wx.Size(0, 0), 1, border=5, flag=wx.GROW|wx.ALL)
+        parent.AddWindow(self.staticText7, 1, border=5, flag=wx.GROW|wx.ALL)
+        parent.AddWindow(self.SubrangeMaximum, 1, border=5, flag=wx.GROW|wx.ALL)
+        
+    def _init_coll_EnumeratedPanelSizer_Items(self, parent):
+        parent.AddWindow(self.EnumeratedValues, 2, border=5, flag=wx.GROW|wx.ALL)
+        parent.AddWindow(self.staticText8, 1, border=5, flag=wx.GROW|wx.ALL)
+        parent.AddWindow(self.EnumeratedInitialValue, 1, border=5, flag=wx.ALL)
+
+    def _init_coll_ArrayPanelSizer_Items(self, parent):
+        parent.AddSizer(self.ArrayPanelLeftSizer, 0, border=0, flag=wx.GROW)
+        parent.AddSizer(self.ArrayPanelRightSizer, 0, border=0, flag=wx.GROW)
+        parent.AddWindow(self.ArrayDimensions, 0, border=5, flag=wx.GROW|wx.ALL)
+        
+    def _init_coll_ArrayPanelSizer_Growables(self, parent):
+        parent.AddGrowableCol(0)
+        parent.AddGrowableCol(1)
+        parent.AddGrowableRow(1)
+
+    def _init_coll_ArrayPanelLeftSizer_Items(self, parent):
+        parent.AddWindow(self.staticText9, 1, border=5, flag=wx.GROW|wx.ALL)
+        parent.AddWindow(self.ArrayBaseType, 1, border=5, flag=wx.GROW|wx.ALL)
+    
+    def _init_coll_ArrayPanelRightSizer_Items(self, parent):
+        parent.AddWindow(self.staticText10, 1, border=5, flag=wx.GROW|wx.ALL)
+        parent.AddWindow(self.ArrayInitialValue, 1, border=5, flag=wx.GROW|wx.ALL)    
+
+    def _init_sizers(self):
+        self.MainSizer = wx.FlexGridSizer(cols=1, hgap=0, rows=2, vgap=10)
+        self.TopSizer = wx.BoxSizer(wx.HORIZONTAL)
+        self.TypeInfosSizer = wx.StaticBoxSizer(self.staticbox, wx.HORIZONTAL)
+        self.DirectlyPanelSizer = wx.BoxSizer(wx.HORIZONTAL)
+        self.SubrangePanelSizer = wx.GridSizer(cols=4, hgap=0, rows=3, vgap=0)
+        self.EnumeratedPanelSizer = wx.BoxSizer(wx.HORIZONTAL)
+        self.ArrayPanelSizer = wx.FlexGridSizer(cols=2, hgap=0, rows=2, vgap=0)
+        self.ArrayPanelLeftSizer = wx.BoxSizer(wx.HORIZONTAL)
+        self.ArrayPanelRightSizer = wx.BoxSizer(wx.HORIZONTAL)
+        
+        self._init_coll_MainSizer_Items(self.MainSizer)
+        self._init_coll_MainSizer_Growables(self.MainSizer)
+        self._init_coll_TopSizer_Items(self.TopSizer)
+        self._init_coll_TypeInfosSizer_Items(self.TypeInfosSizer)
+        self._init_coll_DirectlyPanelSizer_Items(self.DirectlyPanelSizer)
+        self._init_coll_SubrangePanelSizer_Items(self.SubrangePanelSizer)
+        self._init_coll_EnumeratedPanelSizer_Items(self.EnumeratedPanelSizer)
+        self._init_coll_ArrayPanelSizer_Items(self.ArrayPanelSizer)
+        self._init_coll_ArrayPanelSizer_Growables(self.ArrayPanelSizer)
+        self._init_coll_ArrayPanelLeftSizer_Items(self.ArrayPanelLeftSizer)
+        self._init_coll_ArrayPanelRightSizer_Items(self.ArrayPanelRightSizer)
+        
+        self.SetSizer(self.MainSizer)
+        self.DirectlyPanel.SetSizer(self.DirectlyPanelSizer)
+        self.SubrangePanel.SetSizer(self.SubrangePanelSizer)
+        self.EnumeratedPanel.SetSizer(self.EnumeratedPanelSizer)
+        self.ArrayPanel.SetSizer(self.ArrayPanelSizer)
+    
+    def _init_ctrls(self, prnt):
+        wx.Panel.__init__(self, id=ID_DATATYPEEDITOR, name='', parent=prnt,
+              size=wx.Size(0, 0), style=wx.SUNKEN_BORDER)
+
+        self.staticbox = wx.StaticBox(id=ID_DATATYPEEDITORSTATICBOX,
+              label='Type infos:', name='staticBox1', parent=self,
+              pos=wx.Point(0, 0), size=wx.Size(0, 0), style=0)
+
+        self.staticText1 = wx.StaticText(id=ID_DATATYPEEDITORSTATICTEXT1,
+              label='Derivation Type:', name='staticText1', parent=self,
+              pos=wx.Point(0, 0), size=wx.Size(150, 17), style=0)
+
+        self.DerivationType = wx.Choice(id=ID_DATATYPEEDITORDERIVATIONTYPE,
+              name='DerivationType', parent=self, pos=wx.Point(0, 0),
+              size=wx.Size(200, 24), style=0)
+        self.Bind(wx.EVT_CHOICE, self.OnDerivationTypeChanged, id=ID_DATATYPEEDITORDERIVATIONTYPE)
+
+        # Panel for Directly derived data types
+
+        self.DirectlyPanel = wx.Panel(id=ID_DATATYPEEDITORDIRECTLYPANEL,
+              name='DirectlyPanel', parent=self, pos=wx.Point(0, 0),
+              size=wx.Size(0, 0), style=wx.TAB_TRAVERSAL)
+
+        self.staticText2 = wx.StaticText(id=ID_DATATYPEEDITORSTATICTEXT2,
+              label='Base Type:', name='staticText2', parent=self.DirectlyPanel,
+              pos=wx.Point(0, 0), size=wx.Size(150, 17), style=0)
+
+        self.DirectlyBaseType = wx.Choice(id=ID_DATATYPEEDITORDIRECTLYBASETYPE, 
+              name='DirectlyBaseType', parent=self.DirectlyPanel, pos=wx.Point(0, 0),
+              size=wx.Size(0, 24), style=wx.TAB_TRAVERSAL)
+        self.Bind(wx.EVT_CHOICE, self.OnInfosChanged, id=ID_DATATYPEEDITORDIRECTLYBASETYPE)
+
+        self.staticText3 = wx.StaticText(id=ID_DATATYPEEDITORSTATICTEXT3,
+              label='Initial Value:', name='staticText3', parent=self.DirectlyPanel,
+              pos=wx.Point(0, 0), size=wx.Size(150, 17), style=0)
+
+        self.DirectlyInitialValue = wx.TextCtrl(id=ID_DATATYPEEDITORDIRECTLYINITIALVALUE, 
+              name='DirectlyInitialValue', parent=self.DirectlyPanel, pos=wx.Point(0, 0),
+              size=wx.Size(0, 24), style=wx.TAB_TRAVERSAL|wx.TE_PROCESS_ENTER)
+        self.Bind(wx.EVT_TEXT_ENTER, self.OnInfosChanged, id=ID_DATATYPEEDITORDIRECTLYINITIALVALUE)
+
+        # Panel for Subrange data types
+
+        self.SubrangePanel = wx.Panel(id=ID_DATATYPEEDITORSUBRANGEPANEL,
+              name='SubrangePanel', parent=self, pos=wx.Point(0, 0),
+              size=wx.Size(0, 0), style=wx.TAB_TRAVERSAL)
+
+        self.staticText4 = wx.StaticText(id=ID_DATATYPEEDITORSTATICTEXT4,
+              label='Base Type:', name='staticText4', parent=self.SubrangePanel,
+              pos=wx.Point(0, 0), size=wx.Size(150, 17), style=0)
+
+        self.SubrangeBaseType = wx.Choice(id=ID_DATATYPEEDITORSUBRANGEBASETYPE, 
+              name='SubrangeBaseType', parent=self.SubrangePanel, pos=wx.Point(0, 0),
+              size=wx.Size(0, 24), style=wx.TAB_TRAVERSAL)
+        self.Bind(wx.EVT_CHOICE, self.OnSubrangeBaseTypeChanged, id=ID_DATATYPEEDITORSUBRANGEBASETYPE)
+
+        self.staticText5 = wx.StaticText(id=ID_DATATYPEEDITORSTATICTEXT5,
+              label='Initial Value:', name='staticText5', parent=self.SubrangePanel,
+              pos=wx.Point(0, 0), size=wx.Size(150, 17), style=0)
+
+        self.SubrangeInitialValue = wx.SpinCtrl(id=ID_DATATYPEEDITORSUBRANGEINITIALVALUE, 
+              name='SubrangeInitialValue', parent=self.SubrangePanel, pos=wx.Point(0, 0),
+              size=wx.Size(0, 24), style=wx.TAB_TRAVERSAL)
+        self.Bind(wx.EVT_SPINCTRL, self.OnInfosChanged, id=ID_DATATYPEEDITORSUBRANGEINITIALVALUE)
+
+        self.staticText6 = wx.StaticText(id=ID_DATATYPEEDITORSTATICTEXT6,
+              label='Minimum:', name='staticText6', parent=self.SubrangePanel,
+              pos=wx.Point(0, 0), size=wx.Size(150, 17), style=0)
+
+        self.SubrangeMinimum = wx.SpinCtrl(id=ID_DATATYPEEDITORSUBRANGEMINIMUM, 
+              name='SubrangeMinimum', parent=self.SubrangePanel, pos=wx.Point(0, 0),
+              size=wx.Size(0, 24), style=wx.TAB_TRAVERSAL)
+        self.Bind(wx.EVT_SPINCTRL, self.OnSubrangeMinimumChanged, id=ID_DATATYPEEDITORSUBRANGEMINIMUM)
+
+        self.staticText7 = wx.StaticText(id=ID_DATATYPEEDITORSTATICTEXT7,
+              label='Maximum:', name='staticText7', parent=self.SubrangePanel,
+              pos=wx.Point(0, 0), size=wx.Size(150, 17), style=0)
+
+        self.SubrangeMaximum = wx.SpinCtrl(id=ID_DATATYPEEDITORSUBRANGEMAXIMUM, 
+              name='SubrangeMaximum', parent=self.SubrangePanel, pos=wx.Point(0, 0),
+              size=wx.Size(0, 24), style=wx.TAB_TRAVERSAL)
+        self.Bind(wx.EVT_SPINCTRL, self.OnSubrangeMaximumChanged, id=ID_DATATYPEEDITORSUBRANGEMAXIMUM)
+
+        # Panel for Enumerated data types
+
+        self.EnumeratedPanel = wx.Panel(id=ID_DATATYPEEDITORENUMERATEDPANEL,
+              name='EnumeratedPanel', parent=self, pos=wx.Point(0, 0),
+              size=wx.Size(0, 0), style=wx.TAB_TRAVERSAL)
+
+        self.EnumeratedValues = wx.gizmos.EditableListBox(id=ID_DATATYPEEDITORENUMERATEDVALUES, 
+              name='EnumeratedValues', parent=self.EnumeratedPanel, label="Values:", pos=wx.Point(0, 0),
+              size=wx.Size(0, 0), style=wx.gizmos.EL_ALLOW_NEW | wx.gizmos.EL_ALLOW_EDIT | wx.gizmos.EL_ALLOW_DELETE)
+        self.EnumeratedValues.GetListCtrl().Bind(wx.EVT_LIST_END_LABEL_EDIT, self.OnEnumeratedValueEndEdit)
+        self.EnumeratedValues.GetNewButton().Bind(wx.EVT_BUTTON, self.OnEnumeratedValuesChanged)
+        self.EnumeratedValues.GetDelButton().Bind(wx.EVT_BUTTON, self.OnEnumeratedValuesChanged)
+        self.EnumeratedValues.GetUpButton().Bind(wx.EVT_BUTTON, self.OnEnumeratedValuesChanged)
+        self.EnumeratedValues.GetDownButton().Bind(wx.EVT_BUTTON, self.OnEnumeratedValuesChanged)
+        
+        self.staticText8 = wx.StaticText(id=ID_DATATYPEEDITORSTATICTEXT8,
+              label='Initial Value:', name='staticText8', parent=self.EnumeratedPanel,
+              pos=wx.Point(0, 0), size=wx.Size(150, 17), style=0)
+
+        self.EnumeratedInitialValue = wx.Choice(id=ID_DATATYPEEDITORENUMERATEDINITIALVALUE, 
+              name='EnumeratedInitialValue', parent=self.EnumeratedPanel, pos=wx.Point(0, 0),
+              size=wx.Size(0, 24), style=wx.TAB_TRAVERSAL|wx.TE_PROCESS_ENTER)
+        self.Bind(wx.EVT_CHOICE, self.OnInfosChanged, id=ID_DATATYPEEDITORENUMERATEDINITIALVALUE)
+        
+        # Panel for Array data types
+
+        self.ArrayPanel = wx.Panel(id=ID_DATATYPEEDITORARRAYPANEL,
+              name='ArrayPanel', parent=self, pos=wx.Point(0, 0),
+              size=wx.Size(0, 0), style=wx.TAB_TRAVERSAL)
+
+        self.staticText9 = wx.StaticText(id=ID_DATATYPEEDITORSTATICTEXT9,
+              label='Base Type:', name='staticText9', parent=self.ArrayPanel,
+              pos=wx.Point(0, 0), size=wx.Size(150, 17), style=0)
+
+        self.ArrayBaseType = wx.Choice(id=ID_DATATYPEEDITORARRAYBASETYPE, 
+              name='SubrangeBaseType', parent=self.ArrayPanel, pos=wx.Point(0, 0),
+              size=wx.Size(0, 24), style=wx.TAB_TRAVERSAL)
+        self.Bind(wx.EVT_CHOICE, self.OnInfosChanged, id=ID_DATATYPEEDITORARRAYBASETYPE)
+
+        self.ArrayDimensions = wx.gizmos.EditableListBox(id=ID_DATATYPEEDITORARRAYDIMENSIONS, 
+              name='ArrayDimensions', parent=self.ArrayPanel, label="Dimensions:", pos=wx.Point(0, 0),
+              size=wx.Size(0, 24), style=wx.gizmos.EL_ALLOW_NEW | wx.gizmos.EL_ALLOW_EDIT | wx.gizmos.EL_ALLOW_DELETE)
+        self.ArrayDimensions.GetListCtrl().Bind(wx.EVT_LIST_END_LABEL_EDIT, self.OnDimensionsChanged)
+        self.ArrayDimensions.GetNewButton().Bind(wx.EVT_BUTTON, self.OnDimensionsChanged)
+        self.ArrayDimensions.GetDelButton().Bind(wx.EVT_BUTTON, self.OnDimensionsChanged)
+        self.ArrayDimensions.GetUpButton().Bind(wx.EVT_BUTTON, self.OnDimensionsChanged)
+        self.ArrayDimensions.GetDownButton().Bind(wx.EVT_BUTTON, self.OnDimensionsChanged)
+
+        self.staticText10 = wx.StaticText(id=ID_DATATYPEEDITORSTATICTEXT10,
+              label='Initial Value:', name='staticText10', parent=self.ArrayPanel,
+              pos=wx.Point(0, 0), size=wx.Size(150, 17), style=0)
+
+        self.ArrayInitialValue = wx.TextCtrl(id=ID_DATATYPEEDITORARRAYINITIALVALUE, 
+              name='ArrayInitialValue', parent=self.ArrayPanel, pos=wx.Point(0, 0),
+              size=wx.Size(0, 24), style=wx.TAB_TRAVERSAL|wx.TE_PROCESS_ENTER)
+        self.Bind(wx.EVT_TEXT_ENTER, self.OnInfosChanged, id=ID_DATATYPEEDITORARRAYINITIALVALUE)
+        
+        self._init_sizers()
+
+    def __init__(self, parent, tagname, window, controler):
+        self._init_ctrls(parent)
+        
+        self.DerivationType.Append("Directly")
+        self.DerivationType.Append("Subrange")
+        self.DerivationType.Append("Enumerated")
+        self.DerivationType.Append("Array")
+        self.SubrangePanel.Hide()
+        self.EnumeratedPanel.Hide()
+        self.ArrayPanel.Hide()
+        self.CurrentPanel = "Directly"
+        
+        self.ParentWindow = window
+        self.Controler = controler
+        self.TagName = tagname
+    
+    def OnEnumeratedValueEndEdit(self, event):
+        text = event.GetText()
+        if text.upper() in [string.upper() for string in self.EnumeratedValues.GetStrings()]:
+            message = wx.MessageDialog(self, "\"%s\" value already defined!"%text, "Error", wx.OK|wx.ICON_ERROR)
+            message.ShowModal()
+            message.Destroy()
+            event.Veto()
+        elif text.upper() in IEC_KEYWORDS:
+            message = wx.MessageDialog(self, "\"%s\" is a keyword. It can't be used!"%text, "Error", wx.OK|wx.ICON_ERROR)
+            message.ShowModal()
+            message.Destroy()
+        else:
+            wx.CallAfter(self.RefreshEnumeratedValues)
+            wx.CallAfter(self.RefreshTypeInfos)
+            event.Skip()
+    
+    def OnEnumeratedValuesChanged(self, event):
+        wx.CallAfter(self.RefreshEnumeratedValues)
+        wx.CallAfter(self.RefreshTypeInfos)
+        event.Skip()
+    
+    def SetTagName(self, tagname):
+        self.TagName = tagname
+        
+    def GetTagName(self):
+        return self.TagName
+    
+    def IsViewing(self, tagname):
+        return self.TagName == tagname
+
+    def SetMode(self, mode):
+        pass
+
+    def ResetBuffer(self):
+        pass
+
+    def RefreshView(self):
+        self.DirectlyBaseType.Clear()
+        self.ArrayBaseType.Clear()
+        for datatype in self.Controler.GetDataTypes(self.TagName):
+            self.DirectlyBaseType.Append(datatype)
+            self.ArrayBaseType.Append(datatype)
+        self.DirectlyBaseType.SetSelection(0)
+        self.SubrangeBaseType.Clear()
+        words = self.TagName.split("::")
+        for base_type in self.Controler.GetSubrangeTypes():
+            self.SubrangeBaseType.Append(base_type)
+        self.SubrangeBaseType.SetSelection(0)
+        self.RefreshBoundsRange()
+        type_infos = self.Controler.GetDataTypeInfos(self.TagName)
+        if type_infos is not None:
+            self.DerivationType.SetStringSelection(type_infos["type"])
+            if type_infos["type"] == "Directly":
+                self.DirectlyBaseType.SetStringSelection(type_infos["base_type"])
+                self.DirectlyInitialValue.SetValue(type_infos["initial"])
+            elif type_infos["type"] == "Subrange":
+                self.SubrangeBaseType.SetStringSelection(type_infos["base_type"])
+                self.RefreshBoundsRange()
+                self.SubrangeMinimum.SetValue(type_infos["min"])
+                self.SubrangeMaximum.SetValue(type_infos["max"])
+                self.RefreshSubrangeInitialValueRange()
+                if type_infos["initial"] != "":
+                    self.SubrangeInitialValue.SetValue(int(type_infos["initial"]))
+                else:
+                    self.SubrangeInitialValue.SetValue(type_infos["min"])
+            elif type_infos["type"] == "Enumerated":
+                self.EnumeratedValues.SetStrings(type_infos["values"])
+                self.RefreshEnumeratedValues()
+                self.EnumeratedInitialValue.SetStringSelection(type_infos["initial"])
+            elif type_infos["type"] == "Array":
+                self.ArrayBaseType.SetStringSelection(type_infos["base_type"])
+                self.ArrayDimensions.SetStrings(type_infos["dimensions"])
+                self.ArrayInitialValue.SetValue(type_infos["initial"])
+            self.RefreshDisplayedInfos()
+
+    def OnDerivationTypeChanged(self, event):
+        self.RefreshDisplayedInfos()
+        self.RefreshTypeInfos()
+        event.Skip()
+
+    def OnInfosChanged(self, event):
+        self.RefreshTypeInfos()
+        event.Skip()
+
+    def OnSubrangeBaseTypeChanged(self, event):
+        self.RefreshBoundsRange()
+        self.RefreshTypeInfos()
+        event.Skip()
+
+    def OnSubrangeMinimumChanged(self, event):
+        wx.CallAfter(self.SubrangeMinimum.SetValue, min(self.SubrangeMaximum.GetValue(), self.SubrangeMinimum.GetValue()))
+        wx.CallAfter(self.RefreshSubrangeInitialValueRange)
+        wx.CallAfter(self.RefreshTypeInfos)
+        event.Skip()
+
+    def OnSubrangeMaximumChanged(self, event):
+        wx.CallAfter(self.SubrangeMaximum.SetValue, max(self.SubrangeMinimum.GetValue(), self.SubrangeMaximum.GetValue()))
+        wx.CallAfter(self.RefreshSubrangeInitialValueRange)
+        wx.CallAfter(self.RefreshTypeInfos)
+        event.Skip()
+
+    def OnDimensionsChanged(self, event):
+        wx.CallAfter(self.RefreshTypeInfos)
+        event.Skip()
+
+    def RefreshDisplayedInfos(self):
+        selected = self.DerivationType.GetStringSelection()
+        if selected != self.CurrentPanel:
+            if self.CurrentPanel == "Directly":
+                self.DirectlyPanel.Hide()
+            elif self.CurrentPanel == "Subrange":
+                self.SubrangePanel.Hide()
+            elif self.CurrentPanel == "Enumerated":
+                self.EnumeratedPanel.Hide()
+            elif self.CurrentPanel == "Array":
+                self.ArrayPanel.Hide()
+            self.CurrentPanel = selected
+            if selected == "Directly":
+                self.DirectlyPanel.Show()
+            elif selected == "Subrange":
+                self.SubrangePanel.Show()
+            elif selected == "Enumerated":
+                self.EnumeratedPanel.Show()
+            elif selected == "Array":
+                self.ArrayPanel.Show()
+            self.MainSizer.Layout()
+
+    def RefreshEnumeratedValues(self):
+        selected = self.EnumeratedInitialValue.GetStringSelection()
+        self.EnumeratedInitialValue.Clear()
+        self.EnumeratedInitialValue.Append("")
+        for value in self.EnumeratedValues.GetStrings():
+            self.EnumeratedInitialValue.Append(value)
+        self.EnumeratedInitialValue.SetStringSelection(selected)
+
+    def RefreshBoundsRange(self):
+        range = GetDataTypeRange(self.SubrangeBaseType.GetStringSelection())
+        if range is not None:
+            min_value, max_value = range
+            self.SubrangeMinimum.SetRange(min_value, max_value)
+            self.SubrangeMinimum.SetValue(min(max(min_value, self.SubrangeMinimum.GetValue()), max_value))
+            self.SubrangeMaximum.SetRange(min_value, max_value)
+            self.SubrangeMaximum.SetValue(min(max(min_value, self.SubrangeMaximum.GetValue()), max_value))
+
+    def RefreshSubrangeInitialValueRange(self):
+        self.SubrangeInitialValue.SetRange(self.SubrangeMinimum.GetValue(), self.SubrangeMaximum.GetValue())
+
+    def RefreshTypeInfos(self):
+        selected = self.DerivationType.GetStringSelection()
+        infos = {"type" : selected}
+        if selected == "Directly":
+            infos["base_type"] = self.DirectlyBaseType.GetStringSelection()
+            infos["initial"] = self.DirectlyInitialValue.GetValue()
+        elif selected == "Subrange":
+            infos["base_type"] = self.SubrangeBaseType.GetStringSelection()
+            infos["min"] = self.SubrangeMinimum.GetValue()
+            infos["max"] = self.SubrangeMaximum.GetValue()
+            initial_value = self.SubrangeInitialValue.GetValue()
+            if initial_value == infos["min"]:
+                infos["initial"] = ""
+            else:
+                infos["initial"] = str(initial_value)
+        elif selected == "Enumerated":
+            infos["values"] = self.EnumeratedValues.GetStrings()
+            infos["initial"] = self.EnumeratedInitialValue.GetStringSelection()
+        elif selected == "Array":
+            infos["base_type"] = self.ArrayBaseType.GetStringSelection()
+            infos["dimensions"] = self.ArrayDimensions.GetStrings()
+            infos["initial"] = self.ArrayInitialValue.GetValue()
+        self.Controler.SetDataTypeInfos(self.TagName, infos)
+        self.ParentWindow.RefreshTitle()
+        self.ParentWindow.RefreshEditMenu()
+
--- a/PLCControler.py	Tue Nov 27 12:58:34 2007 +0100
+++ b/PLCControler.py	Thu Dec 06 18:05:29 2007 +0100
@@ -37,7 +37,10 @@
 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)?")
 
 [ITEM_UNEDITABLE, ITEM_PROJECT, ITEM_POU, ITEM_CLASS, ITEM_VARIABLE,
- ITEM_TRANSITION, ITEM_ACTION, ITEM_CONFIGURATION, ITEM_RESOURCE] = range(9)
+ ITEM_TRANSITION, ITEM_ACTION, ITEM_CONFIGURATION, ITEM_RESOURCE, 
+ ITEM_DATATYPE] = range(10)
+
+ScriptDirectory = os.path.split(os.path.realpath(__file__))[0]
 
 """
 pyxsval is not complete and the parts that are not supported print some error
@@ -167,6 +170,8 @@
         self.FilePath = ""
         self.FileName = ""
         self.ProgramFilePath = ""
+        self.RefreshDataTypeUsingTree()
+        self.RefreshDataTypes()
         self.RefreshPouUsingTree()
         self.RefreshBlockTypes()
 
@@ -192,6 +197,12 @@
         self.ProjectBuffer = UndoBuffer(self.Copy(self.Project), False)
         self.Buffering = False
     
+    # Return project data type names
+    def GetProjectDataTypeNames(self):
+        if self.Project:
+            return [datatype.getName() for datatype in self.Project.getDataTypes()]
+        return []
+    
     # Return project pou names
     def GetProjectPouNames(self):
         if self.Project:
@@ -263,6 +274,9 @@
     def GetProjectInfos(self):
         if self.Project:
             infos = {"name": self.Project.getName(), "type": ITEM_PROJECT}
+            datatypes = {"name": "Data Types", "type": ITEM_UNEDITABLE, "values":[]}
+            for datatype in self.Project.getDataTypes():
+                datatypes["values"].append({"name": datatype.getName(), "type": ITEM_DATATYPE, "values": []})
             pou_types = {"function": {"name": "Functions", "type": ITEM_UNEDITABLE, "values":[]},
                          "functionBlock": {"name": "Function Blocks", "type": ITEM_UNEDITABLE, "values":[]},
                          "program": {"name": "Programs", "type": ITEM_UNEDITABLE, "values":[]}}
@@ -294,11 +308,38 @@
                 config_infos["values"] = [resources]
                 configurations["values"].append(config_infos)
             infos["values"] = [{"name": "Properties", "type": ITEM_UNEDITABLE, "values": []},
-                               pou_types["function"], pou_types["functionBlock"], 
+                               datatypes, pou_types["function"], pou_types["functionBlock"], 
                                pou_types["program"], configurations]
             return infos
         return None
 
+    # Refresh the tree of user-defined data type cross-use
+    def RefreshDataTypeUsingTree(self):
+        # Reset the tree of user-defined pou cross-use
+        self.DataTypeUsingTree = {}
+        if self.Project:
+            datatypes = self.Project.getDataTypes()
+            # Reference all the user-defined data type names and initialize the tree of 
+            # user-defined data type cross-use
+            datatypenames = [datatype.getName() for datatype in datatypes]
+            for name in datatypenames:
+                self.DataTypeUsingTree[name] = []
+            # Analyze each data type
+            for datatype in datatypes:
+                name = datatype.getName()
+                basetype_content = datatype.getBaseType().getContent()
+                if basetype_content["value"] is not None:
+                    if basetype_content["name"] == "derived":
+                        basetype_name = basetype_content["value"].getName()
+                        if basetype_name in datatypenames and name not in self.DataTypeUsingTree[basetype_name]:
+                            self.DataTypeUsingTree[basetype_name].append(name)
+                    elif basetype_content["name"] in ["subrangeSigned", "subrangeUnsigned", "array"]:
+                        base_type = basetype_content["value"].baseType.getContent()
+                        if base_type["value"] is not None:
+                            basetype_name = base_type["value"].getName()
+                            if basetype_name in datatypenames and name not in self.DataTypeUsingTree[basetype_name]:
+                                self.DataTypeUsingTree[basetype_name].append(name)
+    
     # Refresh the tree of user-defined pou cross-use
     def RefreshPouUsingTree(self):
         # Reset the tree of user-defined pou cross-use
@@ -317,9 +358,9 @@
                     # Extract variables from every varLists
                     for type, varlist in pou.getVars():
                         for var in varlist.getVariable():
-                            var_type = var.getType().getValue()
-                            if not isinstance(var_type, (StringType, UnicodeType)):
-                                typename = var_type.getName()
+                            vartype_content = var.getType().getContent()
+                            if vartype_content["value"] is not None:
+                                typename = vartype_content["value"].getName()
                                 if typename in pounames and name not in self.PouUsingTree[typename]:
                                     self.PouUsingTree[typename].append(name)
                 bodytype = pou.getBodyType()
@@ -342,12 +383,34 @@
                         if typename != name and typename_model.search(text):
                             self.PouUsingTree[typename].append(name)
 
+    # Return if data type given by name is used by another data type or pou
+    def DataTypeIsUsed(self, name):
+        if name in self.DataTypeUsingTree:
+            return len(self.DataTypeUsingTree[name]) > 0
+        return False
+
     # Return if pou given by name is used by another pou
     def PouIsUsed(self, name):
         if name in self.PouUsingTree:
             return len(self.PouUsingTree[name]) > 0
         return False
 
+    # Return if data type given by name is directly or undirectly used by the reference data type
+    def DataTypeIsUsedBy(self, name, reference):
+        if name in self.DataTypeUsingTree:
+            list = self.DataTypeUsingTree[name]
+            # Test if data type is directly used by reference
+            if reference in list:
+                return True
+            else:
+                # Test if data type is undirectly used by reference, by testing if data types 
+                # that directly use data type is directly or undirectly used by reference
+                used = False
+                for element in list:
+                    used |= self.DataTypeIsUsedBy(element, reference)
+                return used
+        return False
+
     # Return if pou given by name is directly or undirectly used by the reference pou
     def PouIsUsedBy(self, name, reference):
         if name in self.PouUsingTree:
@@ -380,7 +443,22 @@
 #-------------------------------------------------------------------------------
 #                        Project Pous management functions
 #-------------------------------------------------------------------------------
-        
+    
+    # Add a Data Type to Project
+    def ProjectAddDataType(self, datatype_name):
+        # Add the pou to project
+        self.Project.appendDataType(datatype_name)
+        self.RefreshDataTypeUsingTree()
+        self.RefreshDataTypes()
+        self.BufferProject()
+    
+    # Remove a Data Type from project
+    def ProjectRemoveDataType(self, datatype_name):
+        self.Project.removeDataType(datatype_name)
+        self.RefreshDataTypeUsingTree()
+        self.RefreshDataTypes()
+        self.BufferProject()
+    
     # Add a Pou to Project
     def ProjectAddPou(self, pou_name, pou_type, body_type):
         # Add the pou to project
@@ -391,7 +469,7 @@
         self.RefreshBlockTypes()
         self.BufferProject()
     
-    # Remove a pou from project
+    # Remove a Pou from project
     def ProjectRemovePou(self, pou_name):
         self.Project.removePou(pou_name)
         self.RefreshPouUsingTree()
@@ -401,8 +479,6 @@
     # Add a configuration to Project
     def ProjectAddConfiguration(self, config_name):
         self.Project.addConfiguration(config_name)
-        self.RefreshPouUsingTree()
-        self.RefreshBlockTypes()
         self.BufferProject()
     
     # Remove a configuration from project
@@ -413,8 +489,6 @@
     # Add a resource to a configuration of the Project
     def ProjectAddConfigurationResource(self, config_name, resource_name):
         self.Project.addConfigurationResource(config_name, resource_name)
-        self.RefreshPouUsingTree()
-        self.RefreshBlockTypes()
         self.BufferProject()
     
     # Remove a resource from a configuration of the project
@@ -450,7 +524,15 @@
         pou = self.Project.getPou(pou_name)
         pou.removeAction(action_name)
         self.BufferProject()
-        
+    
+    # Change the name of a pou
+    def ChangeDataTypeName(self, old_name, new_name):
+        # Found the pou corresponding to old name and change its name to new name
+        datatype = self.Project.getDataType(old_name)
+        datatype.setName(new_name)
+        self.Project.updateElementName(old_name, new_name)
+        self.BufferProject()
+    
     # Change the name of a pou
     def ChangePouName(self, old_name, new_name):
         # Found the pou corresponding to old name and change its name to new name
@@ -575,12 +657,12 @@
             tempvar = plcopen.varListPlain_variable()
             tempvar.setName(var["Name"])
             var_type = plcopen.dataType()
-            if GetBlockType(var["Type"]) != None:
+            if var["Type"] not in var_type.getChoices():
                 derived_type = plcopen.derived()
                 derived_type.setName(var["Type"])
-                var_type.setValue(derived_type)
+                var_type.setContent("derived", derived_type)
             else:
-                var_type.setValue(var["Type"])
+                var_type.setContent(var["Type"], None)
             tempvar.setType(var_type)
             if var["Initial Value"] != "":
                 value = plcopen.value()
@@ -615,11 +697,11 @@
             for varlist in configuration.getGlobalVars():
                 for var in varlist.getVariable():
                     tempvar = {"Name":var.getName(),"Class":"Global"}
-                    var_type = var.getType().getValue()
-                    if isinstance(var_type, (StringType, UnicodeType)):
-                        tempvar["Type"] = var_type
+                    vartype_content = var.getType().getContent()
+                    if vartype_content["value"] is None:
+                        tempvar["Type"] = vartype_content["name"]
                     else:
-                        tempvar["Type"] = var_type.getName()
+                        tempvar["Type"] = vartype_content["value"].getName()
                     tempvar["Edit"] = True
                     initial = var.getInitialValue()
                     if initial:
@@ -663,11 +745,11 @@
             for varlist in resource.getGlobalVars():
                 for var in varlist.getVariable():
                     tempvar = {"Name":var.getName(),"Class":"Global"}
-                    var_type = var.getType().getValue()
-                    if isinstance(var_type, (StringType, UnicodeType)):
-                        tempvar["Type"] = var_type
+                    vartype_content = var.getType().getContent()
+                    if vartype_content["value"] is None:
+                        tempvar["Type"] = vartype_content["name"]
                     else:
-                        tempvar["Type"] = var_type.getName()
+                        tempvar["Type"] = vartype_content["value"].getName()
                     tempvar["Edit"] = True
                     initial = var.getInitialValue()
                     if initial:
@@ -704,12 +786,12 @@
             for type, varlist in pou.getVars():
                 for var in varlist.getVariable():
                     tempvar = {"Name":var.getName(),"Class":type}
-                    var_type = var.getType().getValue()
-                    if isinstance(var_type, (StringType, UnicodeType)):
-                        tempvar["Type"] = var_type
+                    vartype_content = var.getType().getContent()
+                    if vartype_content["value"] is None:
+                        tempvar["Type"] = vartype_content["name"]
                         tempvar["Edit"] = True
                     else:
-                        tempvar["Type"] = var_type.getName()
+                        tempvar["Type"] = vartype_content["value"].getName()
                         tempvar["Edit"] = not pou.hasBlock(tempvar["Name"])
                     initial = var.getInitialValue()
                     if initial:
@@ -754,7 +836,12 @@
             return_type = plcopen.dataType()
             pou.interface.setReturnType(return_type)
         # Change return type
-        return_type.setValue(type)
+        if type in self.GetBaseTypes():
+            return_type.setContent(type, None)
+        else:
+            derived_type = plcopen.derived()
+            derived_type.setName(type)
+            return_type.setContent("derived", derived_type)
         self.RefreshBlockTypes()
     
     def UpdateProjectUsedPous(self, old_name, new_name):
@@ -778,9 +865,39 @@
             # Return the return type if there is one
             return_type = pou.interface.getReturnType()
             if return_type:
-                return return_type.getValue()
+                returntype_content = return_type.getContent()
+                if returntype_content["value"] is None:
+                    return returntype_content["name"]
+                else:
+                    return returntype_content["value"].getName()
         return None
-
+    
+    # Update data types with user-defined data types added
+    def RefreshDataTypes(self):
+        ResetTypeHierarchy()
+        ResetEnumeratedDataValues()
+        if self.Project:
+            for datatype in self.Project.getDataTypes():
+                name = datatype.getName()
+                basetype_content = datatype.getBaseType().getContent()
+                if basetype_content["value"] is None:
+                    AddDataTypeHierarchy(name, basetype_content["name"])
+                elif basetype_content["name"] == "derived":
+                    AddDataTypeHierarchy(name, basetype_content["value"].getName())
+                elif basetype_content["name"] in ["subrangeSigned", "subrangeUnsigned"]:
+                    base_type = basetype_content["value"].baseType.getContent()
+                    if base_type["value"] is None:
+                        AddDataTypeHierarchy(name, base_type["name"])
+                    else:
+                        AddDataTypeHierarchy(name, base_type["value"].getName())
+                else:
+                    if basetype_content["name"] == "enum":
+                        values = []
+                        for value in basetype_content["value"].values.getValue():
+                            values.append(value.getName())
+                        AddEnumeratedDataValues(values)
+                    AddDataTypeHierarchy(name, "ANY_DERIVED")
+    
     # Update Block types with user-defined pou added
     def RefreshBlockTypes(self):
         if BlockTypes[-1]["name"] == "User-defined POUs":
@@ -799,17 +916,34 @@
                         for type, varlist in pou.getVars():
                             if type == "InOut":
                                 for var in varlist.getVariable():
-                                    block_infos["inputs"].append((var.getName(),var.getType().getValue(),"none"))
-                                    block_infos["outputs"].append((var.getName(),var.getType().getValue(),"none"))
+                                    var_type = var.type.getContent()
+                                    if var_type["value"] is None:
+                                        block_infos["inputs"].append((var.getName(),var_type["name"],"none"))
+                                        block_infos["outputs"].append((var.getName(),var_type["name"],"none"))
+                                    else:
+                                        block_infos["inputs"].append((var.getName(),var_type["value"].getName(),"none"))
+                                        block_infos["outputs"].append((var.getName(),var_type["value"].getName(),"none"))
                             elif type == "Input":
                                 for var in varlist.getVariable():
-                                    block_infos["inputs"].append((var.getName(),var.getType().getValue(),"none"))
+                                    var_type = var.type.getContent()
+                                    if var_type["value"] is None:
+                                        block_infos["inputs"].append((var.getName(),var_type["name"],"none"))
+                                    else:
+                                        block_infos["inputs"].append((var.getName(),var_type["value"].getName(),"none"))
                             elif type == "Output":
                                 for var in varlist.getVariable():
-                                    block_infos["outputs"].append((var.getName(),var.getType().getValue(),"none"))
+                                    var_type = var.type.getContent()
+                                    if var_type["value"] is None:
+                                        block_infos["outputs"].append((var.getName(),var_type["name"],"none"))
+                                    else:
+                                        block_infos["outputs"].append((var.getName(),var_type["value"].getName(),"none"))
                         return_type = pou.interface.getReturnType()
                         if return_type:
-                            block_infos["outputs"].append(("",return_type.getValue(),"none"))
+                            var_type = return_type.getContent()
+                            if var_type["value"] is None:
+                                block_infos["outputs"].append(("",var_type["name"],"none"))
+                            else:
+                                block_infos["outputs"].append(("",var_type["value"].getName(),"none"))
                     if pou.getBodyType() in ["FBD","LD","SFC"]:
                         for instance in pou.getInstances():
                             if isinstance(instance, plcopen.comment):
@@ -818,16 +952,13 @@
     
     # Return Block types checking for recursion
     def GetBlockTypes(self, tagname = ""):
+        name = ""
+        type = None
         if self.Project:
             words = tagname.split("::")
-            if len(words) == 1:
-                name = current_name
-            else:
+            if words[0] in ["P","T","A"]:
                 name = words[1]
-            type = self.GetPouType(name)
-        else:
-            name = ""
-            type = None
+                type = self.GetPouType(name)
         if type == "function":
             blocktypes = []
             for category in BlockTypes[:-1] + PluginTypes:
@@ -853,10 +984,7 @@
         if self.Project:
             words = tagname.split("::")
             if words[0] in ["P","T","A"]:
-                if len(words) == 1:
-                    name = current_name
-                else:
-                    name = words[1]
+                name = words[1]
                 type = self.GetPouType(name)
         blocktypes = []
         for category in BlockTypes[:-1]:
@@ -882,11 +1010,45 @@
                     blocktypes.append(pou.getName())
         return blocktypes
 
+    # Return Data Types checking for recursion
+    def GetDataTypes(self, tagname = "", basetypes = True):
+        if basetypes:
+            datatypes = self.GetBaseTypes()
+        else:
+            datatypes = []
+        if self.Project:
+            words = tagname.split("::")
+            if words[0] in ["D"]:
+                name = words[1]
+            else:
+                name = ""
+            for datatype in self.Project.getDataTypes():
+                datatype_name = datatype.getName()
+                if datatype_name != name and not self.DataTypeIsUsedBy(name, datatype_name):
+                    datatypes.append(datatype_name)
+        return datatypes
+
+    # Return Base Types
+    def GetBaseTypes(self):
+        return [value for value, parent in TypeHierarchy_list if not value.startswith("ANY")]
+
+    # Return Subrange types
+    def GetSubrangeTypes(self):
+        return [value for value, range in DataTypeRange_list]
+
+    # Return Enumerated Values
+    def GetEnumeratedDataValues(self):
+        return EnumeratedDataValues
+
 #-------------------------------------------------------------------------------
 #                   Project Element tag name computation functions
 #-------------------------------------------------------------------------------
     
-    # Compute a pou transition name
+    # 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
     
@@ -905,6 +1067,112 @@
     # Compute a pou  name
     def ComputeConfigurationResourceName(self, config, resource):
         return "R::%s::%s" % (config, resource)
+
+#-------------------------------------------------------------------------------
+#                    Project opened Data types management functions
+#-------------------------------------------------------------------------------
+
+    # Return the data type informations
+    def GetDataTypeInfos(self, tagname):
+        words = tagname.split("::")
+        if words[0] == "D":
+            infos = {}
+            datatype = self.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(str(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()
+            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 words[0] == "D":
+            datatype = self.Project.getDataType(words[1])
+            if infos["type"] == "Directly":
+                if infos["base_type"] in self.GetBaseTypes():
+                    datatype.baseType.setContent(infos["base_type"], None)
+                else:
+                    derived_datatype = plcopen.derived()
+                    derived_datatype.setName(infos["base_type"])
+                    datatype.baseType.setContent("derived", derived_datatype)
+            elif infos["type"] == "Subrange":
+                if infos["base_type"] in GetSubTypes("ANY_UINT"):
+                    subrange = plcopen.subrangeUnsigned()
+                    datatype.baseType.setContent("subrangeUnsigned", subrange)
+                else:
+                    subrange = plcopen.subrangeSigned()
+                    datatype.baseType.setContent("subrangeSigned", subrange)
+                subrange.range.setLower(infos["min"])
+                subrange.range.setUpper(infos["max"])
+                if infos["base_type"] in self.GetBaseTypes():
+                    subrange.baseType.setContent(infos["base_type"], None)
+                else:
+                    derived_datatype = plcopen.derived()
+                    derived_datatype.setName(infos["base_type"])
+                    subrange.baseType.setContent("derived", derived_datatype)
+            elif infos["type"] == "Enumerated":
+                enumerated = plcopen.enum()
+                for enum_value in infos["values"]:
+                    value = plcopen.values_value()
+                    value.setName(enum_value)
+                    enumerated.values.appendValue(value)
+                datatype.baseType.setContent("enum", enumerated)
+            elif infos["type"] == "Array":
+                array = plcopen.array()
+                for dimension in infos["dimensions"]:
+                    dimension_range = plcopen.rangeSigned()
+                    dimension_range.setLower(1)
+                    dimension_range.setUpper(int(dimension))
+                    array.appendDimension(dimension_range)
+                if infos["base_type"] in self.GetBaseTypes():
+                    array.baseType.setContent(infos["base_type"], None)
+                else:
+                    derived_datatype = plcopen.derived()
+                    derived_datatype.setName(infos["base_type"])
+                    array.baseType.setContent("derived", derived_datatype)
+                datatype.baseType.setContent("array", array)
+            if infos["initial"] != "":
+                if datatype.initialValue is None:
+                    datatype.initialValue = plcopen.value()
+                datatype.initialValue.setValue(infos["initial"])
+            else:
+                datatype.initialValue = None
+            self.RefreshDataTypeUsingTree()
+            self.RefreshDataTypes()
+            self.BufferProject()
     
 #-------------------------------------------------------------------------------
 #                       Project opened Pous management functions
@@ -972,7 +1240,7 @@
             return "BOOL"
         return None
     
-    # Change the edited element taxt
+    # Change the edited element text
     def SetEditedElementText(self, tagname, text):
         element = self.GetEditedElement(tagname)
         if element != None:
@@ -1331,7 +1599,11 @@
             for type, varlist in pou.getVars():
                 for var in varlist.getVariable():
                     if var.getName() == varname:
-                        return var.getType().getValue()
+                        vartype_content = var.getType().getContent()
+                        if vartype_content["value"] is None:
+                            return vartype_content["name"]
+                        else:
+                            return vartype_content["value"].getName()
         return None
     
     def SetConnectionWires(self, connection, connector):
@@ -1998,7 +2270,7 @@
         if self.VerifyXML:
             if sys:
                 sys.stdout = HolePseudoFile()
-            result = pyxsval.parseAndValidate(filepath, os.path.join(sys.path[0], "plcopen/TC6_XML_V10_B.xsd"))
+            result = pyxsval.parseAndValidate(filepath, os.path.join(ScriptDirectory, "plcopen", "TC6_XML_V10_B.xsd"))
             if sys:
                 sys.stdout = sys.__stdout__
             tree = result.getTree()
@@ -2016,6 +2288,8 @@
                 self.Buffering = False
                 self.ElementsOpened = []
                 self.CurrentElementEditing = None
+                self.RefreshDataTypeUsingTree()
+                self.RefreshDataTypes()
                 self.RefreshPouUsingTree()
                 self.RefreshBlockTypes()
                 return None
@@ -2031,11 +2305,11 @@
                       "xmlns:xsi" : "http://www.w3.org/2001/XMLSchema-instance",
                       "xsi:schemaLocation" : "http://www.plcopen.org/xml/tc6.xsd http://www.plcopen.org/xml/tc6.xsd"}
             text += self.Project.generateXMLText("project", 0, extras)
-
+            
             if self.VerifyXML:
                 if sys:
                     sys.stdout = HolePseudoFile()
-                pyxsval.parseAndValidateString(text, open(os.path.join(sys.path[0], "plcopen/TC6_XML_V10_B.xsd"),"r").read())
+                pyxsval.parseAndValidateString(text, open(os.path.join(ScriptDirectory, "plcopen", "TC6_XML_V10_B.xsd"),"r").read())
                 if sys:
                     sys.stdout = sys.__stdout__
             
--- a/PLCGenerator.py	Tue Nov 27 12:58:34 2007 +0100
+++ b/PLCGenerator.py	Thu Dec 06 18:05:29 2007 +0100
@@ -34,6 +34,7 @@
 
 currentProject = None
 currentProgram = ""
+datatypeComputed = {}
 pouComputed = {}
 
 def ReIndentText(text, nb_spaces):
@@ -53,6 +54,49 @@
                 compute += "\n"
     return compute
 
+def GenerateDataType(datatype_name):
+    if not datatypeComputed.get(datatype_name, True):
+        datatypeComputed[datatype_name] = True
+        global currentProject, currentProgram
+        datatype = currentProject.getDataType(datatype_name)
+        datatype_def = "  %s :"%datatype.getName()
+        basetype_content = datatype.baseType.getContent()
+        if basetype_content["value"] is None:
+            datatype_def += " %s"%basetype_content["name"]
+        elif basetype_content["name"] == "derived":
+            basetype_name = basetype_content["value"].getName()
+            GenerateDataType(basetype_name)
+            datatype_def += " %s"%basetype_name
+        elif basetype_content["name"] in ["subrangeSigned", "subrangeUnsigned"]:
+            base_type = basetype_content["value"].baseType.getContent()
+            if base_type["value"] is None:
+                basetype_name = base_type["name"]
+            else:
+                basetype_name = base_type["value"].getName()
+                GenerateDataType(basetype_name)
+            min_value = basetype_content["value"].range.getLower()
+            max_value = basetype_content["value"].range.getUpper()
+            datatype_def += " %s (%d..%d)"%(basetype_name, min_value, max_value)
+        elif basetype_content["name"] == "enum":
+            values = []
+            for value in basetype_content["value"].values.getValue():
+                values.append(value.getName())
+            datatype_def += " (%s)"%", ".join(values)
+        elif basetype_content["name"] == "array":
+            base_type = basetype_content["value"].baseType.getContent()
+            if base_type["value"] is None:
+                basetype_name = base_type["name"]
+            else:
+                basetype_name = base_type["value"].getName()
+                GenerateDataType(basetype_name)
+            dimensions = []
+            for dimension in basetype_content["value"].getDimension():
+                dimensions.append("0..%d"%(dimension.getUpper() - 1))
+            datatype_def += " ARRAY [%s] OF %s"%(",".join(dimensions), basetype_name)
+        if datatype.initialValue is not None:
+            datatype_def += " := %s"%str(datatype.initialValue.getValue())
+        currentProgram += "%s;\n"%datatype_def
+
 def GeneratePouProgram(pou_name):
     if not pouComputed.get(pou_name, True):
         pouComputed[pou_name] = True
@@ -64,6 +108,8 @@
         else:
             raise ValueError, "Undefined pou type"
         pou_program.GenerateInterface(pou.getInterface())
+        pou_program.GenerateConnectionTypes(pou)
+        #print pou.getName(), pou_program.ConnectionTypes, pou_program.RelatedConnections
         pou_program.GenerateProgram(pou)
         currentProgram += pou_program.GenerateSTProgram()
 
@@ -77,7 +123,11 @@
             config += " CONSTANT"
         config += "\n"
         for var in varlist.getVariable():
-            var_type = var.getType().getValue()
+            vartype_content = var.getType().getContent()
+            if vartype_content["value"] is None:
+                var_type = vartype_content["name"]
+            else:
+                var_type = vartype_content["value"].getName()
             config += "    %s "%var.getName()
             address = var.getAddress()
             if address:
@@ -110,12 +160,16 @@
             resrce += " CONSTANT"
         resrce += "\n"
         for var in varlist.getVariable():
-            var_type = var.getType().getValue()
+            vartype_content = var.getType().getContent()
+            if vartype_content["value"] is None:
+                var_type = vartype_content["name"]
+            else:
+                var_type = vartype_content["value"].getName()
             resrce += "      %s "%var.getName()
             address = var.getAddress()
             if address:
                 resrce += "AT %s "%address
-            resrce += ": %s"%var.getType().getValue()
+            resrce += ": %s"%var_type
             initial = var.getInitialValue()
             if initial:
                 value = str(initial.getValue())
@@ -171,6 +225,8 @@
         self.InitialSteps = []
         self.ComputedBlocks = {}
         self.ComputedConnectors = {}
+        self.ConnectionTypes = {}
+        self.RelatedConnections = []
         self.SFCNetworks = {"Steps":{}, "Transitions":{}, "Actions":{}}
         self.SFCComputedBlocks = ""
         self.ActionNumber = 0
@@ -187,22 +243,71 @@
                     return True
         return False
     
+    def GetVariableType(self, name):
+        for list_type, retain, constant, located, vars in self.Interface:
+            for var_type, var_name, var_address, var_initial in vars:
+                if name == var_name:
+                    return var_type
+        return None
+    
+    def GetConnectedConnection(self, connection, body):
+        links = connection.getConnections()
+        if links and len(links) == 1:
+            return self.GetLinkedConnection(links[0], body)
+        return None
+        
+    def GetLinkedConnection(self, link, body):
+        parameter = link.getFormalParameter()
+        instance = body.getContentInstance(link.getRefLocalId())
+        if isinstance(instance, (plcopen.inVariable, plcopen.inOutVariable, plcopen.continuation, plcopen.contact, plcopen.coil)):
+            return instance.connectionPointOut
+        elif isinstance(instance, plcopen.block):
+            outputvariables = instance.outputVariables.getVariable()
+            if len(outputvariables) == 1:
+                return outputvariables[0].connectionPointOut
+            elif parameter:
+                for variable in outputvariables:
+                    if variable.getFormalParameter() == parameter:
+                        return variable.connectionPointOut
+            else:
+                point = link.getPosition()[-1]
+                for variable in outputvariables:
+                    relposition = variable.connectionPointOut.getRelPosition()
+                    blockposition = instance.getPosition()
+                    if point.x == blockposition.x + relposition[0] and point.y == blockposition.y + relposition[1]:
+                        return variable.connectionPointOut
+        elif isinstance(instance, plcopen.leftPowerRail):
+            outputconnections = instance.getConnectionPointOut()
+            if len(outputconnections) == 1:
+                return outputconnections[0]
+            else:
+                point = link.getPosition()[-1]
+                for outputconnection in outputconnections:
+                    relposition = outputconnection.getRelPosition()
+                    powerrailposition = instance.getPosition()
+                    if point.x == powerrailposition.x + relposition[0] and point.y == powerrailposition.y + relposition[1]:
+                        return outputconnection
+        return None
+        
+    def ExtractRelatedConnections(self, connection):
+        for i, related in enumerate(self.RelatedConnections):
+            if connection in related:
+                return self.RelatedConnections.pop(i)
+        return [connection]
+    
     def GenerateInterface(self, interface):
         if self.Type == "FUNCTION":
-            self.ReturnType = interface.getReturnType().getValue()
+            returntype_content = interface.getReturnType().getContent()
+            if returntype_content["value"] is None:
+                self.ReturnType = returntype_content["name"]
+            else:
+                self.ReturnType = returntype_content["value"].getName()
         for varlist in interface.getContent():
             variables = []
             located = False
             for var in varlist["value"].getVariable():
-                type = var.getType().getValue()
-                if not isinstance(type, (StringType, UnicodeType)):
-                    type = type.getName()
-                    GeneratePouProgram(type)
-                    blocktype = GetBlockType(type)
-                    if blocktype:
-                        variables.extend(blocktype["initialise"](type, var.getName()))
-                        located = False
-                else:
+                vartype_content = var.getType().getContent()
+                if vartype_content["value"] is None:
                     initial = var.getInitialValue()
                     if initial:
                         initial_value = initial.getValue()
@@ -211,11 +316,120 @@
                     address = var.getAddress()
                     if address:
                         located = True
-                    variables.append((type, var.getName(), address, initial_value))
+                    variables.append((vartype_content["name"], var.getName(), address, initial_value))
+                else:
+                    var_type = vartype_content["value"].getName()
+                    GeneratePouProgram(var_type)
+                    blocktype = GetBlockType(var_type)
+                    if blocktype is not None:
+                        variables.extend(blocktype["initialise"](var_type, var.getName()))
+                        located = False
+                    else:
+                        initial = var.getInitialValue()
+                        if initial:
+                            initial_value = initial.getValue()
+                        else:
+                            initial_value = None
+                        address = var.getAddress()
+                        if address:
+                            located = True
+                        variables.append((vartype_content["value"].getName(), var.getName(), address, initial_value))
             if len(variables) > 0:
                 self.Interface.append((varTypeNames[varlist["name"]], varlist["value"].getRetain(), 
                             varlist["value"].getConstant(), located, variables))
     
+    def GenerateConnectionTypes(self, pou):
+        body = pou.getBody()
+        body_content = body.getContent()
+        body_type = body_content["name"]
+        if body_type in ["FBD", "LD", "SFC"]:
+            for instance in body.getContentInstances():
+                if isinstance(instance, (plcopen.inVariable, plcopen.outVariable, plcopen.inOutVariable)):
+                    expression = instance.getExpression()
+                    var_type = self.GetVariableType(expression)
+                    if expression == pou.getName():
+                        returntype_content = pou.interface.getReturnType().getContent()
+                        if returntype_content["value"] is None:
+                            var_type = returntype_content["name"]
+                        else:
+                            var_type = returntype_content["value"].getName()
+                    elif var_type is None:
+                        var_type = expression.split("#")[0]
+                    if isinstance(instance, (plcopen.inVariable, plcopen.inOutVariable)):
+                        self.ConnectionTypes[instance.connectionPointOut] = var_type
+                    if isinstance(instance, (plcopen.outVariable, plcopen.inOutVariable)):
+                        self.ConnectionTypes[instance.connectionPointIn] = var_type
+                        connected = self.GetConnectedConnection(instance.connectionPointIn, body)
+                        if connected and connected not in self.ConnectionTypes:
+                            for connection in self.ExtractRelatedConnections(connected):
+                                self.ConnectionTypes[connection] = var_type
+                elif isinstance(instance, (plcopen.contact, plcopen.coil)):
+                    self.ConnectionTypes[instance.connectionPointOut] = "BOOL"
+                    self.ConnectionTypes[instance.connectionPointIn] = "BOOL"
+                    connected = self.GetConnectedConnection(instance.connectionPointIn, body)
+                    if connected and connected not in self.ConnectionTypes:
+                        for connection in self.ExtractRelatedConnections(connected):
+                            self.ConnectionTypes[connection] = "BOOL"
+                elif isinstance(instance, plcopen.leftPowerRail):
+                    for connection in instance.getConnectionPointOut():
+                        self.ConnectionTypes[connection] = "BOOL"
+                elif isinstance(instance, plcopen.rightPowerRail):
+                    for connection in instance.getConnectionPointIn():
+                        self.ConnectionTypes[connection] = "BOOL"
+                        connected = self.GetConnectedConnection(connection, body)
+                        if connected and connected not in self.ConnectionTypes:
+                            for connection in self.ExtractRelatedConnections(connected):
+                                self.ConnectionTypes[connection] = "BOOL"
+                elif isinstance(instance, plcopen.transition):
+                    content = instance.condition.getContent()
+                    if content["name"] == "connection" and len(content["value"]) == 1:
+                        connected = self.GetLinkedConnection(content["value"][0], body)
+                        if connected and connected not in self.ConnectionTypes:
+                            for connection in self.ExtractRelatedConnections(connected):
+                                self.ConnectionTypes[connection] = "BOOL"
+                elif isinstance(instance, plcopen.block):
+                    block_infos = GetBlockType(instance.getTypeName())
+                    undefined = {}
+                    for variable in instance.outputVariables.getVariable():
+                        output_name = variable.getFormalParameter()
+                        for oname, otype, oqualifier in block_infos["outputs"]:
+                            if output_name == oname and variable.connectionPointOut not in self.ConnectionTypes:
+                                if otype.startswith("ANY"):
+                                    if otype not in undefined:
+                                        undefined[otype] = []
+                                    undefined[otype].append(variable.connectionPointOut)
+                                else:
+                                    for connection in self.ExtractRelatedConnections(variable.connectionPointOut):
+                                        self.ConnectionTypes[connection] = otype
+                    for variable in instance.inputVariables.getVariable():
+                        input_name = variable.getFormalParameter()
+                        for iname, itype, iqualifier in block_infos["inputs"]:
+                            if input_name == iname:
+                                connected = self.GetConnectedConnection(variable.connectionPointIn, body)
+                                if itype.startswith("ANY"):
+                                    if itype not in undefined:
+                                        undefined[itype] = []
+                                    undefined[itype].append(variable.connectionPointIn)
+                                    if connected:
+                                        undefined[itype].append(connected)
+                                else:
+                                    self.ConnectionTypes[variable.connectionPointIn] = itype
+                                    if connected and connected not in self.ConnectionTypes:
+                                        for connection in self.ExtractRelatedConnections(connected):
+                                            self.ConnectionTypes[connection] = itype
+                    for var_type, connections in undefined.items():
+                        related = []
+                        for connection in connections:
+                            if connection in self.ConnectionTypes:
+                                var_type = self.ConnectionTypes[connection]
+                            else:
+                                related.extend(self.ExtractRelatedConnections(connection))
+                        if var_type.startswith("ANY") and len(related) > 0:
+                            self.RelatedConnections.append(related)
+                        else:
+                            for connection in related:
+                                self.ConnectionTypes[connection] = var_type
+                                    
     def GenerateProgram(self, pou):
         body = pou.getBody()
         body_content = body.getContent()
@@ -223,7 +437,20 @@
         if body_type in ["IL","ST"]:
             self.Program = ReIndentText(body_content["value"].getText(), 2)
         elif body_type == "FBD":
+            orderedInstances = []
+            otherInstances = []
             for instance in body.getContentInstances():
+                if isinstance(instance, (plcopen.outVariable, plcopen.inOutVariable, plcopen.block)):
+                    executionOrderId = instance.getExecutionOrderId()
+                    if executionOrderId > 0:
+                        orderedInstances.append((executionOrderId, instance))
+                    else:
+                        otherInstances.append(instance)
+                elif isinstance(instance, plcopen.connector):
+                    otherInstances.append(instance)
+            orderedInstances.sort()
+            instances = [instance for (executionOrderId, instance) in orderedInstances] + otherInstances
+            for instance in instances:
                 if isinstance(instance, (plcopen.outVariable, plcopen.inOutVariable)):
                     var = instance.getExpression()
                     connections = instance.connectionPointIn.getConnections()
@@ -231,8 +458,9 @@
                         expression = self.ComputeFBDExpression(body, connections[0])
                         self.Program += "  %s := %s;\n"%(var, expression)
                 elif isinstance(instance, plcopen.block):
-                    type = instance.getTypeName()
-                    block_infos = GetBlockType(type)
+                    block_type = instance.getTypeName()
+                    self.GeneratePouProgram(block_type)
+                    block_infos = GetBlockType(block_type)
                     block_infos["generate"](self, instance, body, None)
                 elif isinstance(instance, plcopen.connector):
                     connector = instance.getName()
@@ -271,15 +499,16 @@
             for initialstep in self.InitialSteps:
                 self.ComputeSFCStep(initialstep)
     
-    def ComputeFBDExpression(self, body, link):
+    def ComputeFBDExpression(self, body, link, order = False):
         localid = link.getRefLocalId()
         instance = body.getContentInstance(localid)
         if isinstance(instance, (plcopen.inVariable, plcopen.inOutVariable)):
             return instance.getExpression()
         elif isinstance(instance, plcopen.block):
             block_type = instance.getTypeName()
+            self.GeneratePouProgram(block_type)
             block_infos = GetBlockType(block_type)
-            return block_infos["generate"](self, instance, body, link)
+            return block_infos["generate"](self, instance, body, link, order)
         elif isinstance(instance, plcopen.continuation):
             name = instance.getName()
             computed_value = self.ComputedConnectors.get(name, None)
@@ -290,7 +519,7 @@
                     if tmp_instance.getName() == name:
                         connections = tmp_instance.connectionPointIn.getConnections()
                         if connections and len(connections) == 1:
-                            expression = self.ComputeFBDExpression(body, connections[0])
+                            expression = self.ComputeFBDExpression(body, connections[0], order)
                             self.ComputedConnectors[name] = expression
                             return expression
             raise ValueError, "No connector found"
@@ -304,6 +533,7 @@
                 paths.append(None)
             elif isinstance(next, plcopen.block):
                 block_type = next.getTypeName()
+                self.GeneratePouProgram(block_type)
                 block_infos = GetBlockType(block_type)
                 paths.append(block_infos["generate"](self, next, body, connection))
             else:
@@ -662,8 +892,15 @@
     global currentProject, currentProgram
     currentProject = project
     currentProgram = ""
+    for datatype in project.getDataTypes():
+        datatypeComputed[datatype.getName()] = False
     for pou in project.getPous():
         pouComputed[pou.getName()] = False
+    if len(datatypeComputed) > 0:
+        currentProgram += "TYPE\n"
+        for datatype_name in datatypeComputed.keys():
+            GenerateDataType(datatype_name)
+        currentProgram += "END_TYPE\n\n"
     for pou_name in pouComputed.keys():
         GeneratePouProgram(pou_name)
     for config in project.getConfigurations():
--- a/PLCOpenEditor.py	Tue Nov 27 12:58:34 2007 +0100
+++ b/PLCOpenEditor.py	Thu Dec 06 18:05:29 2007 +0100
@@ -34,6 +34,7 @@
 from Viewer import *
 from TextViewer import *
 from RessourceEditor import *
+from DataTypeEditor import *
 from PLCControler import *
 from plcopen import OpenPDFDoc
 from plcopen.structures import LOCATIONDATATYPES
@@ -42,7 +43,7 @@
 
 __version__ = "$Revision$"
 
-CWD = os.path.split(__file__)[0]
+CWD = os.path.split(os.path.realpath(__file__))[0]
 
 [ID_PLCOPENEDITOR, ID_PLCOPENEDITORPROJECTTREE, 
  ID_PLCOPENEDITORMAINSPLITTER, ID_PLCOPENEDITORSECONDSPLITTER, 
@@ -71,11 +72,12 @@
 ] = [wx.NewId() for _init_coll_HelpMenu_Items in range(2)]
 
 [ID_PLCOPENEDITOREDITMENUITEMS0, ID_PLCOPENEDITOREDITMENUITEMS1, 
- ID_PLCOPENEDITOREDITMENUITEMS11, ID_PLCOPENEDITOREDITMENUITEMS12, 
  ID_PLCOPENEDITOREDITMENUITEMS2, ID_PLCOPENEDITOREDITMENUITEMS4, 
  ID_PLCOPENEDITOREDITMENUITEMS5, ID_PLCOPENEDITOREDITMENUITEMS6, 
  ID_PLCOPENEDITOREDITMENUITEMS8, ID_PLCOPENEDITOREDITMENUITEMS9, 
-] = [wx.NewId() for _init_coll_EditMenu_Items in range(10)]
+ ID_PLCOPENEDITOREDITMENUITEMS11, ID_PLCOPENEDITOREDITMENUITEMS12, 
+ ID_PLCOPENEDITOREDITMENUITEMS14, ID_PLCOPENEDITOREDITMENUITEMS15, 
+] = [wx.NewId() for _init_coll_EditMenu_Items in range(12)]
 
 [ID_PLCOPENEDITORSFCMENUITEMS0, ID_PLCOPENEDITORSFCMENUITEMS1, 
  ID_PLCOPENEDITORSFCMENUITEMS2, ID_PLCOPENEDITORSFCMENUITEMS3, 
@@ -171,13 +173,18 @@
               kind=wx.ITEM_NORMAL, text=u'Paste\tCTRL+V')
         parent.AppendSeparator()
         AppendMenu(parent, help='', id=ID_PLCOPENEDITOREDITMENUITEMS8,
+              kind=wx.ITEM_NORMAL, text=u'Add Data Type')
+        AppendMenu(parent, help='', id=ID_PLCOPENEDITOREDITMENUITEMS9,
+              kind=wx.ITEM_NORMAL, text=u'Remove Data Type')
+        parent.AppendSeparator()
+        AppendMenu(parent, help='', id=ID_PLCOPENEDITOREDITMENUITEMS11,
               kind=wx.ITEM_NORMAL, text=u'Add POU')
-        AppendMenu(parent, help='', id=ID_PLCOPENEDITOREDITMENUITEMS9,
+        AppendMenu(parent, help='', id=ID_PLCOPENEDITOREDITMENUITEMS12,
               kind=wx.ITEM_NORMAL, text=u'Remove POU')
         parent.AppendSeparator()
-        AppendMenu(parent, help='', id=ID_PLCOPENEDITOREDITMENUITEMS11,
+        AppendMenu(parent, help='', id=ID_PLCOPENEDITOREDITMENUITEMS14,
               kind=wx.ITEM_NORMAL, text=u'Add Configuration')
-        AppendMenu(parent, help='', id=ID_PLCOPENEDITOREDITMENUITEMS12,
+        AppendMenu(parent, help='', id=ID_PLCOPENEDITOREDITMENUITEMS15,
               kind=wx.ITEM_NORMAL, text=u'Remove Configuration')
         self.Bind(wx.EVT_MENU, self.OnRefreshMenu,
               id=ID_PLCOPENEDITOREDITMENUITEMS0)
@@ -191,14 +198,18 @@
               id=ID_PLCOPENEDITOREDITMENUITEMS5)
         self.Bind(wx.EVT_MENU, self.OnPasteMenu,
               id=ID_PLCOPENEDITOREDITMENUITEMS6)
+        self.Bind(wx.EVT_MENU, self.OnAddDataTypeMenu,
+              id=ID_PLCOPENEDITOREDITMENUITEMS8)
+        self.Bind(wx.EVT_MENU, self.OnRemoveDataTypeMenu,
+              id=ID_PLCOPENEDITOREDITMENUITEMS9)
         self.Bind(wx.EVT_MENU, self.OnAddPouMenu,
-              id=ID_PLCOPENEDITOREDITMENUITEMS8)
+              id=ID_PLCOPENEDITOREDITMENUITEMS11)
         self.Bind(wx.EVT_MENU, self.OnRemovePouMenu,
-              id=ID_PLCOPENEDITOREDITMENUITEMS9)
+              id=ID_PLCOPENEDITOREDITMENUITEMS12)
         self.Bind(wx.EVT_MENU, self.OnAddConfigurationMenu,
-              id=ID_PLCOPENEDITOREDITMENUITEMS11)
+              id=ID_PLCOPENEDITOREDITMENUITEMS14)
         self.Bind(wx.EVT_MENU, self.OnRemoveConfigurationMenu,
-              id=ID_PLCOPENEDITOREDITMENUITEMS12)
+              id=ID_PLCOPENEDITOREDITMENUITEMS15)
 
     def _init_coll_menuBar1_Menus(self, parent):
         if self.ModeSolo:
@@ -416,7 +427,7 @@
             self.VariablePanelIndexer = VariablePanelIndexer(self, self.Controler)
             self.AUIManager.AddPane(self.VariablePanelIndexer, wx.aui.AuiPaneInfo().Caption("Variable Panel").Bottom().Layer(0).BestSize(wx.Size(800, 200)).CloseButton(False))
             
-            self.AUIManager.Update(); 
+            self.AUIManager.Update()
         
     def __init__(self, parent, controler = None, fileOpen = None):
         self.ModeSolo = controler == None
@@ -594,10 +605,18 @@
                     self.EditMenu.Enable(ID_PLCOPENEDITOREDITMENUITEMS0, False)
                 self.EditMenu.Enable(ID_PLCOPENEDITOREDITMENUITEMS8, True)
                 self.EditMenu.Enable(ID_PLCOPENEDITOREDITMENUITEMS9, True)
+                self.EditMenu.Enable(ID_PLCOPENEDITOREDITMENUITEMS11, True)
+                self.EditMenu.Enable(ID_PLCOPENEDITOREDITMENUITEMS12, True)
+                self.EditMenu.Enable(ID_PLCOPENEDITOREDITMENUITEMS14, True)
+                self.EditMenu.Enable(ID_PLCOPENEDITOREDITMENUITEMS15, True)
             else:
                 self.EditMenu.Enable(ID_PLCOPENEDITOREDITMENUITEMS0, False)
                 self.EditMenu.Enable(ID_PLCOPENEDITOREDITMENUITEMS8, False)
                 self.EditMenu.Enable(ID_PLCOPENEDITOREDITMENUITEMS9, False)
+                self.EditMenu.Enable(ID_PLCOPENEDITOREDITMENUITEMS11, False)
+                self.EditMenu.Enable(ID_PLCOPENEDITOREDITMENUITEMS12, False)
+                self.EditMenu.Enable(ID_PLCOPENEDITOREDITMENUITEMS14, False)
+                self.EditMenu.Enable(ID_PLCOPENEDITOREDITMENUITEMS15, False)
             self.EditMenu.Enable(ID_PLCOPENEDITOREDITMENUITEMS4, True)
             self.EditMenu.Enable(ID_PLCOPENEDITOREDITMENUITEMS5, True)
             if self.CopyBuffer is not None:
@@ -798,7 +817,7 @@
         tool.SetToggle(False)
 
     def OnSelectionTool(self, event):
-        self.GetPageSelection()
+        selected = self.GetPageSelection()
         if selected != -1:
             self.GetPage(selected).SetMode(MODE_SELECTION)
         event.Skip()
@@ -1018,6 +1037,14 @@
                 itemtype = self.ProjectTree.GetPyData(item)
                 if itemtype == ITEM_PROJECT:
                     self.Controler.SetProjectProperties(name = new_name)
+                elif itemtype == ITEM_DATATYPE:
+                    if new_name.upper() in [name.upper() for name in self.Controler.GetProjectDataTypeNames() if name != old_name]:
+                        message = "\"%s\" data type already exists!"%new_name
+                        abort = True
+                    if not abort:
+                        self.Controler.ChangeDataTypeName(old_name, new_name)
+                        self.RefreshEditorNames(itemtype, old_name, new_name)
+                        self.RefreshPageTitles()
                 elif itemtype == ITEM_POU:
                     if new_name.upper() in [name.upper() for name in self.Controler.GetProjectPouNames() if name != old_name]:
                         message = "\"%s\" pou already exists!"%new_name
@@ -1135,7 +1162,9 @@
         data = self.ProjectTree.GetPyData(selected)
         if name == "Properties":
             self.ShowProperties()
-        if data == ITEM_POU:
+        if data == ITEM_DATATYPE:
+            self.EditProjectElement(data, self.Controler.ComputeDataTypeName(name))
+        elif data == ITEM_POU:
             self.EditProjectElement(data, self.Controler.ComputePouName(name))
         elif data == ITEM_CONFIGURATION:
             self.EditProjectElement(data, self.Controler.ComputeConfigurationName(name))
@@ -1162,11 +1191,12 @@
     
     def OnProjectTreeLeftUp(self, event):
         if self.SelectedItem is not None:
-            print "LeftUp", self.ProjectTree.GetItemText(self.SelectedItem)
             self.ProjectTree.SelectItem(self.SelectedItem)
             name = self.ProjectTree.GetItemText(self.SelectedItem)
             data = self.ProjectTree.GetPyData(self.SelectedItem)
-            if data == ITEM_POU:
+            if data == ITEM_DATATYPE:
+                self.EditProjectElement(data, self.Controler.ComputeDataTypeName(name), True)
+            elif data == ITEM_POU:
                 self.EditProjectElement(data, self.Controler.ComputePouName(name), True)
             elif data == ITEM_CONFIGURATION:
                 self.EditProjectElement(data, self.Controler.ComputeConfigurationName(name), True)
@@ -1191,15 +1221,9 @@
                     tagname = self.Controler.ComputePouActionName(pou_name, name)
                 self.EditProjectElement(data, tagname, True)
             wx.CallAfter(self.ResetSelectedItem)
-        else:
-            print "LeftUp", None
         event.Skip()
     
     def OnProjectTreeItemChanging(self, event):
-        if self.SelectedItem is not None:
-            print "Changing", self.ProjectTree.GetItemText(event.GetItem()), self.ProjectTree.GetItemText(self.SelectedItem)
-        else:
-            print "Changing", None, self.ProjectTree.GetItemText(event.GetItem())
         if self.ProjectTree.GetPyData(event.GetItem()) != ITEM_UNEDITABLE and self.SelectedItem is None:
             self.SelectedItem = event.GetItem()
             event.Veto()
@@ -1230,10 +1254,8 @@
             if wx.VERSION >= (2, 8, 0):
                 if elementtype == ITEM_CONFIGURATION:
                     new_window = MDIConfigurationEditor(self, tagname, self, self.Controler)
-                    self.VariablePanelIndexer.AddVariablePanel(tagname, "config")
                 elif elementtype == ITEM_RESOURCE:
                     new_window = MDIResourceEditor(self, tagname, self, self.Controler)
-                    self.VariablePanelIndexer.AddVariablePanel(tagname, "resource")
                 elif elementtype in [ITEM_POU, ITEM_TRANSITION, ITEM_ACTION]:
                     bodytype = self.Controler.GetEditedElementBodyType(tagname)
                     if bodytype == "FBD":
@@ -1250,17 +1272,18 @@
                             viewer.SetKeywords(IL_KEYWORDS)
                         else:
                             viewer.SetKeywords(ST_KEYWORDS)
+                elif elementtype == ITEM_DATATYPE:
+                    new_window = MDIDataTypeEditor(self, tagname, self, self.Controler)
                 new_window.Bind(wx.EVT_ACTIVATE, self.OnPouSelectedChanged)
                 new_window.Bind(wx.EVT_CLOSE, self.OnPageClose)
+                new_window.Layout()
             else:
                 if elementtype == ITEM_CONFIGURATION:
                     new_window = ConfigurationEditor(self.TabsOpened, tagname, self, self.Controler)
                     self.TabsOpened.AddPage(new_window, "")
-                    self.VariablePanelIndexer.AddVariablePanel(tagname, "config")
                 elif elementtype == ITEM_RESOURCE:
                     new_window = ResourceEditor(self.TabsOpened, tagname, self, self.Controler)
                     self.TabsOpened.AddPage(new_window, "")
-                    self.VariablePanelIndexer.AddVariablePanel(tagname, "resource")
                 elif elementtype in [ITEM_POU, ITEM_TRANSITION, ITEM_ACTION]:
                     bodytype = self.Controler.GetEditedElementBodyType(tagname)
                     if bodytype == "FBD":
@@ -1277,13 +1300,16 @@
                         else:
                             new_window.SetKeywords(ST_KEYWORDS)
                     self.TabsOpened.AddPage(new_window, "")
-            if elementtype == ITEM_POU:
+                elif elementtype == ITEM_DATATYPE:
+                    new_window = DataTypeEditor(self.TabsOpened, tagname, self, self.Controler)
+                    self.TabsOpened.AddPage(new_window, "")
+            if elementtype == ITEM_CONFIGURATION:
+                self.VariablePanelIndexer.AddVariablePanel(tagname, "config")
+            elif elementtype == ITEM_RESOURCE:
+                self.VariablePanelIndexer.AddVariablePanel(tagname, "resource")
+            elif elementtype in [ITEM_POU, ITEM_TRANSITION, ITEM_ACTION]:
                 words = tagname.split("::")
                 self.VariablePanelIndexer.AddVariablePanel(tagname, self.Controler.GetPouType(words[1]))
-            elif elementtype == ITEM_TRANSITION:
-                self.VariablePanelIndexer.AddVariablePanel(tagname, "transition")
-            elif elementtype == ITEM_TRANSITION:
-                self.VariablePanelIndexer.AddVariablePanel(tagname, "action")
             self.VariablePanelIndexer.ChangeVariablePanel(tagname)
             openedidx = self.IsOpened(tagname)
             old_selected = self.GetPageSelection()
@@ -1457,6 +1483,38 @@
                 pass
         event.Skip()
     
+    def OnAddDataTypeMenu(self, event):
+        dialog = DataTypeDialog(self, "Add a new data type", "Please enter data type name", "", wx.OK|wx.CANCEL)
+        dialog.SetDataTypeNames(self.Controler.GetProjectDataTypeNames())
+        if dialog.ShowModal() == wx.ID_OK:
+            self.Controler.ProjectAddDataType(dialog.GetValue())
+            self.RefreshTitle()
+            self.RefreshEditMenu()
+            self.RefreshProjectTree()
+        dialog.Destroy()
+        event.Skip()
+        
+    def OnRemoveDataTypeMenu(self, event):
+        datatypes = self.Controler.GetProjectDataTypeNames()
+        dialog = wx.SingleChoiceDialog(self, "Select Data Type to remove:", "Data Type Remove", datatypes, wx.OK|wx.CANCEL)
+        if dialog.ShowModal() == wx.ID_OK:
+            selected = dialog.GetStringSelection()
+            if not self.Controler.DataTypeIsUsed(selected):
+                self.Controler.ProjectRemoveDataType(selected)
+                tagname = self.Controler.ComputeDataTypeName(selected)
+                idx = self.IsOpened(tagname)
+                if idx is not None:
+                    self.DeletePage(idx)
+                self.RefreshTitle()
+                self.RefreshEditMenu()
+                self.RefreshProjectTree()
+                self.RefreshToolBar()
+            else:
+                message = wx.MessageDialog(self, "%s is used by one or more POUs. It can't be removed!"%selected, "Error", wx.OK|wx.ICON_ERROR)
+                message.ShowModal()
+                message.Destroy()
+        event.Skip()
+    
     def OnAddPouMenu(self, event):
         dialog = PouDialog(self)
         dialog.SetPouNames(self.Controler.GetProjectPouNames())
@@ -1840,6 +1898,59 @@
         return values
 
 #-------------------------------------------------------------------------------
+#                          Edit Step Name Dialog
+#-------------------------------------------------------------------------------
+
+class DataTypeDialog(wx.TextEntryDialog):
+
+    if wx.VERSION < (2, 6, 0):
+        def Bind(self, event, function, id = None):
+            if id is not None:
+                event(self, id, function)
+            else:
+                event(self, function)
+    
+
+    def __init__(self, parent, message, caption = "Please enter text", defaultValue = "", 
+                       style = wx.OK|wx.CANCEL|wx.CENTRE, pos = wx.DefaultPosition):
+        wx.TextEntryDialog.__init__(self, parent, message, caption, defaultValue, style, pos)
+        
+        self.DataTypeNames = []
+        if wx.VERSION >= (2, 8, 0):
+            self.Bind(wx.EVT_BUTTON, self.OnOK, id=self.GetSizer().GetItem(2).GetSizer().GetItem(1).GetSizer().GetAffirmativeButton().GetId())
+        elif wx.VERSION >= (2, 6, 0):
+            self.Bind(wx.EVT_BUTTON, self.OnOK, id=self.GetSizer().GetItem(3).GetSizer().GetAffirmativeButton().GetId())
+        else:
+            self.Bind(wx.EVT_BUTTON, self.OnOK, id=self.GetSizer().GetItem(3).GetSizer().GetChildren()[0].GetSizer().GetChildren()[0].GetWindow().GetId())
+    
+    def OnOK(self, event):
+        datatype_name = self.GetSizer().GetItem(1).GetWindow().GetValue()
+        if datatype_name == "":
+            message = wx.MessageDialog(self, "You must type a name!", "Error", wx.OK|wx.ICON_ERROR)
+            message.ShowModal()
+            message.Destroy()
+        elif not TestIdentifier(datatype_name):
+            message = wx.MessageDialog(self, "\"%s\" is not a valid identifier!"%datatype_name, "Error", wx.OK|wx.ICON_ERROR)
+            message.ShowModal()
+            message.Destroy()
+        elif datatype_name.upper() in IEC_KEYWORDS:
+            message = wx.MessageDialog(self, "\"%s\" is a keyword. It can't be used!"%datatype_name, "Error", wx.OK|wx.ICON_ERROR)
+            message.ShowModal()
+            message.Destroy()
+        elif datatype_name.upper() in self.DataTypeNames:
+            message = wx.MessageDialog(self, "\"%s\" data type already exists!"%datatype_name, "Error", wx.OK|wx.ICON_ERROR)
+            message.ShowModal()
+            message.Destroy()
+        else:
+            self.EndModal(wx.ID_OK)
+
+    def SetDataTypeNames(self, datatype_names):
+        self.DataTypeNames = [datatype_name.upper() for datatype_name in datatype_names]
+
+    def GetValue(self):
+        return self.GetSizer().GetItem(1).GetWindow().GetValue()
+
+#-------------------------------------------------------------------------------
 #                            Create Pou Dialog
 #-------------------------------------------------------------------------------
 
@@ -2462,12 +2573,18 @@
                 self.CurrentPanel = new_tagname
                 
     def ChangeVariablePanel(self, tagname):
-        if tagname in self.VariablePanelList and tagname != self.CurrentPanel:
+        if tagname in self.VariablePanelList:
+            if tagname != self.CurrentPanel:
+                if self.CurrentPanel is not None:
+                    self.VariablePanelList[self.CurrentPanel].Hide()
+                self.CurrentPanel = tagname
+                self.VariablePanelList[self.CurrentPanel].RefreshView()
+                self.VariablePanelList[self.CurrentPanel].Show()
+                self.MainSizer.Layout()
+        else:
             if self.CurrentPanel is not None:
                 self.VariablePanelList[self.CurrentPanel].Hide()
-            self.CurrentPanel = tagname
-            self.VariablePanelList[self.CurrentPanel].RefreshView()
-            self.VariablePanelList[self.CurrentPanel].Show()
+            self.CurrentPanel = None
             self.MainSizer.Layout()
 
     def RefreshVariablePanel(self, tagname):
@@ -2874,12 +2991,10 @@
         self.RefreshTypeList()
 
         self.OptionList = "Yes,No"
-        self.TypeList = [value for value, parent in TypeHierarchy_list if not value.startswith("ANY")]
         
         if element_type == "function":
-            for value, parent in TypeHierarchy_list:
-                if not value.startswith("ANY"):
-                    self.ReturnType.Append(value)
+            for base_type in self.Controler.GetBaseTypes():
+                self.ReturnType.Append(base_type)
             self.ReturnType.Enable(True)
         else:
             self.ReturnType.Enable(False)
@@ -3033,11 +3148,17 @@
         if self.Table.GetColLabelValue(col) == "Type":
             type_menu = wx.Menu(title='')
             base_menu = wx.Menu(title='')
-            for base_type in self.TypeList:
+            for base_type in self.Controler.GetBaseTypes():
                 new_id = wx.NewId()
                 AppendMenu(base_menu, help='', id=new_id, kind=wx.ITEM_NORMAL, text=base_type)
                 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):
+                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():
                 new_id = wx.NewId()
@@ -3072,7 +3193,7 @@
             data = wx.TextDataObject(str((var_name, var_class, var_type, self.TagName)))
             dragSource = wx.DropSource(self.VariablesGrid)
             dragSource.SetData(data)
-            dragSource.DoDragDrop()
+            print dragSource.DoDragDrop()
         event.Skip()
     
     def OnVariablesGridSelectCell(self, event):
--- a/TextViewer.py	Tue Nov 27 12:58:34 2007 +0100
+++ b/TextViewer.py	Thu Dec 06 18:05:29 2007 +0100
@@ -160,6 +160,7 @@
         self.Variables = []
         self.Functions = []
         self.Jumps = []
+        self.EnumeratedValues = []
         self.TextChanged = False
         self.DisableEvents = True
         self.TextSyntax = "ST"
@@ -287,6 +288,10 @@
                 if blocktype["type"] == "function" and blocktype["name"] not in self.Keywords and blocktype["name"] not in self.Variables:
                     self.Functions.append(blocktype["name"].upper())
         
+        self.EnumeratedValues = []
+        for value in self.Controler.GetEnumeratedDataValues():
+            self.EnumeratedValues.append(value.upper())
+        
         self.Colourise(0, -1)
     
     def OnStyleNeeded(self, event):
@@ -320,6 +325,8 @@
                         self.SetStyling(i - start_pos, STC_PLC_FUNCTION)
                     elif word in self.Jumps:
                         self.SetStyling(i - start_pos, STC_PLC_JUMP)
+                    elif word in self.EnumeratedValues:
+                        self.SetStyling(i - start_pos, STC_PLC_NUMBER)
                     else:
                         self.SetStyling(i - start_pos, 31)
                         if self.GetCurrentPos() < start_pos or self.GetCurrentPos() > i:
@@ -368,6 +375,8 @@
                         self.SetStyling(i - start_pos, STC_PLC_FUNCTION)
                     elif word in self.Jumps:
                         self.SetStyling(i - start_pos, STC_PLC_JUMP)
+                    elif word in self.EnumeratedValues:
+                        self.SetStyling(i - start_pos, STC_PLC_NUMBER)
                     else:
                         self.SetStyling(i - start_pos, 31)
                         if self.GetCurrentPos() < start_pos or self.GetCurrentPos() > i:
@@ -395,6 +404,8 @@
                 self.SetStyling(i - start_pos, STC_PLC_FUNCTION)
             elif word in self.Jumps:
                 self.SetStyling(i - start_pos, STC_PLC_JUMP)
+            elif word in self.EnumeratedValues:
+                self.SetStyling(i - start_pos, STC_PLC_NUMBER)
             else:
                 self.SetStyling(i - start_pos, 31)
         else:
--- a/Viewer.py	Tue Nov 27 12:58:34 2007 +0100
+++ b/Viewer.py	Thu Dec 06 18:05:29 2007 +0100
@@ -113,7 +113,7 @@
                 width, height = variable.GetMinSize()
                 variable.SetSize(width, height)
                 self.ParentWindow.AddBlock(variable)
-                self.ParentWindow.Controler.AddEditedElementVariable(self.TagName, id, var_type)
+                self.ParentWindow.Controler.AddEditedElementVariable(self.ParentWindow.TagName, id, var_type)
                 self.ParentWindow.RefreshVariableModel(variable)
                 self.ParentWindow.RefreshBuffer()
                 self.ParentWindow.RefreshScrollBars()
--- a/plcopen/plcopen.py	Tue Nov 27 12:58:34 2007 +0100
+++ b/plcopen/plcopen.py	Thu Dec 06 18:05:29 2007 +0100
@@ -46,39 +46,6 @@
 
 PLCOpenClasses, PLCOpenTypes = GenerateClassesFromXSD(os.path.join(os.path.split(__file__)[0], "TC6_XML_V10_B.xsd"))
 
-cls = PLCOpenClasses.get("dataType", None)
-if cls:
-    cls.value = None
-    
-    def getValue(self):
-        return self.value
-    setattr(cls, "getValue", getValue)
-    
-    def setValue(self, value):
-        self.value = value
-    setattr(cls, "setValue", setValue)
-    
-    def loadXMLTree(self, tree):
-        node = tree.childNodes[1]
-        if node.nodeName == "derived":
-            self.value = PLCOpenClasses["derived"]()
-            self.value.loadXMLTree(node)
-        else:
-            self.value = node.nodeName
-    setattr(cls, "loadXMLTree", loadXMLTree)
-    
-    def generateXMLText(self, name, indent, extras = {}):
-        ind1, ind2 = getIndent(indent, name)
-        text = ind1 + "<%s>\n"%name
-        if isinstance(self.value, (StringType, UnicodeType)):
-            ind3, ind4 = getIndent(indent + 1, self.value)
-            text += ind3 + "<%s/>\n"%self.value
-        else:
-            text += self.value.generateXMLText("derived", indent + 1)
-        text += ind1 + "</%s>\n"%name
-        return text
-    setattr(cls, "generateXMLText", generateXMLText)
-
 cls = PLCOpenClasses.get("formattedText", None)
 if cls:
     cls.text = ""
@@ -159,6 +126,26 @@
     def getName(self):
         return self.contentHeader.getName()
     setattr(cls, "getName", getName)
+
+    def getDataTypes(self):
+        return self.types.getDataTypeElements()
+    setattr(cls, "getDataTypes", getDataTypes)
+    
+    def getDataType(self, name):
+        return self.types.getDataTypeElement(name)
+    setattr(cls, "getDataType", getDataType)
+    
+    def appendDataType(self, name):
+        self.types.appendDataTypeElement(name)
+    setattr(cls, "appendDataType", appendDataType)
+        
+    def insertDataType(self, index, datatype):
+        self.types.insertDataTypeElement(index, datatype)
+    setattr(cls, "insertDataType", insertDataType)
+    
+    def removeDataType(self, name):
+        self.types.removeDataTypeElement(name)
+    setattr(cls, "removeDataType", removeDataType)
     
     def getPous(self):
         return self.types.getPouElements()
@@ -293,6 +280,43 @@
 
 cls = PLCOpenClasses.get("project_types", None)
 if cls:
+    def getDataTypeElements(self):
+        return self.dataTypes.getDataType()
+    setattr(cls, "getDataTypeElements", getDataTypeElements)
+    
+    def getDataTypeElement(self, name):
+        elements = self.dataTypes.getDataType()
+        for element in elements:
+            if element.getName() == name:
+                return element
+        return None
+    setattr(cls, "getDataTypeElement", getDataTypeElement)
+
+    def appendDataTypeElement(self, name):
+        for element in self.dataTypes.getDataType():
+            if element.getName() == name:
+                raise ValueError, "\"%s\" Data Type already exists !!!"%name
+        new_datatype = PLCOpenClasses["dataTypes_dataType"]()
+        new_datatype.setName(name)
+        new_datatype.baseType.setContent("BOOL", None)
+        self.dataTypes.appendDataType(new_datatype)
+    setattr(cls, "appendDataTypeElement", appendDataTypeElement)
+        
+    def insertDataTypeElement(self, index, datatype):
+        self.dataTypes.insertDataType(index, datatype)
+    setattr(cls, "insertDataTypeElement", insertDataTypeElement)
+    
+    def removeDataTypeElement(self, name):
+        found = False
+        for idx, element in enumerate(self.dataTypes.getDataType()):
+            if element.getName() == name:
+                self.dataTypes.removeDataType(idx)
+                found = True
+                break
+        if not found:
+            raise ValueError, "\"%s\" Data Type doesn't exist !!!"%name
+    setattr(cls, "removeDataTypeElement", removeDataTypeElement)
+    
     def getPouElements(self):
         return self.pous.getPou()
     setattr(cls, "getPouElements", getPouElements)
@@ -440,10 +464,10 @@
             variables = varlist["value"].getVariable()
             for var in variables:
                 if var.getName() == old_name:
-                    var_type = var.getType().getValue()
-                    if isinstance(var_type, PLCOpenClasses["derived"]) and var_type.getName() == old_type:
+                    vartype_content = var.getType().getContent()
+                    if vartype_content["value"] is not None and vartype_content["value"].getName() == old_type:
                         var.setName(new_name)
-                        var.getType().getValue().setName(new_type)
+                        vartype_content["value"].setName(new_type)
                         return
     setattr(cls, "changePouVar", changePouVar)
     
@@ -1415,61 +1439,71 @@
 cls = PLCOpenClasses.get("value", None)
 if cls:
     def setValue(self, value):
-        try:
-            value = eval(value)
-        except:
-            pass
-        if type(value) == ListType:
+        if value.startswith("[") and value.endswith("]"):
             arrayValue = PLCOpenClasses["value_arrayValue"]()
-            arrayValue.setValue(value)
             self.content = {"name":"arrayValue","value":arrayValue}
-        elif type(value) == DictType:
+        elif value.startswith("(") and value.endswith(")"):
             structValue = PLCOpenClasses["value_structValue"]()
-            structValue.setValue(value)
             self.content = {"name":"structValue","value":structValue}
         else:
             simpleValue = PLCOpenClasses["value_simpleValue"]()
-            simpleValue.setValue(str(value))
             self.content = {"name":"simpleValue","value":simpleValue}
+        self.content["value"].setValue(value)
     setattr(cls, "setValue", setValue)
     
     def getValue(self):
-        value = self.content["value"].getValue()
-        try:
-            value = eval(value)
-        except:
-            pass
-        return value
+        return self.content["value"].getValue()
     setattr(cls, "getValue", getValue)
 
 cls = PLCOpenClasses.get("value_arrayValue", None)
 if cls:
-    def setValue(self, array):
+    arrayValue_model = re.compile("([0-9]*)\((.*)\)")
+    
+    def setValue(self, value):
         self.value = []
-        for value in array:
+        for item in value[1:-1].split(","):
+            item = item.strip()
             element = PLCOpenClasses["arrayValue_value"]()
-            element.setValue(value)
+            result = arrayValue_model.match(item)
+            if result is not None:
+                groups = result.groups()
+                element.setRepetitionValue(int(groups[0]))
+                element.setValue(groups[1].strip())
+            else:
+                element.setValue(item)
             self.value.append(element)
     setattr(cls, "setValue", setValue)
     
     def getValue(self):
-        return [element.getValue() for element in self.value]
+        values = []
+        for element in self.value:
+            repetition = element.getRepetitionValue()
+            if repetition is not None and repetition > 1:
+                values.append("%d(%s)"%(repetition, element.getValue()))
+            else:
+                values.append(element.getValue())
+        return "[%s]"%", ".join(values)
     setattr(cls, "getValue", getValue)
 
 cls = PLCOpenClasses.get("value_structValue", None)
 if cls:
-    def setValue(self, dict):
+    structValue_model = re.compile("(.*):=(.*)")
+    
+    def setValue(self, value):
         self.value = []
-        for name,value in dict.items():
-            element = PLCOpenClasses["structValue_value"]()
-            element.setMember(name)
-            element.setValue(value)
+        for item in value[1:-1].split(","):
+            result = arrayValue_model.match(item)
+            if result is not None:
+                groups = result.groups()
+                element = PLCOpenClasses["structValue_value"]()
+                element.setMember(groups[0].strip())
+                element.setValue(groups[1].strip())
             self.value.append(element)
     setattr(cls, "setValue", setValue)
     
     def getValue(self):
-        value = {}
+        values = []
         for element in self.value:
-            value[element.getMember()] = element.getValue()
-        return value
+            values.append("%s := %s"%(element.getMember(), element.getValue()))
+        return "(%s)"%", ".join(values)
     setattr(cls, "getValue", getValue)
--- a/plcopen/structures.py	Tue Nov 27 12:58:34 2007 +0100
+++ b/plcopen/structures.py	Thu Dec 06 18:05:29 2007 +0100
@@ -33,38 +33,48 @@
                      "D" : ["DINT", "UDINT", "REAL", "DWORD"],
                      "L" : ["LINT", "ULINT", "LREAL", "LWORD"]} 
 
-def generate_block(generator, block, body, link):
+def generate_block(generator, block, body, link, order=False):
     body_type = body.getContent()["name"]
     name = block.getInstanceName()
     type = block.getTypeName()
+    executionOrderId = block.getExecutionOrderId()
     block_infos = GetBlockType(type)
-    if block_infos["type"] == "function" and link:
-        generator.GeneratePouProgram(type)
-        vars = []
-        for variable in block.inputVariables.getVariable():
-            connections = variable.connectionPointIn.getConnections()
-            if connections and len(connections) == 1:
-                if body_type == "FBD" or body_type == "SFC":
-                    value = generator.ComputeFBDExpression(body, connections[0])
-                elif body_type == "LD":
-                    paths = generator.GenerateLDPaths(variable.connectionPointIn.getConnections(), body)
-                    if len(paths) > 0:
-                        paths = tuple(paths)
-                    else:
-                        paths = paths[0] 
-                    value = generator.ComputeLDExpression(paths, True)
-                vars.append(generator.ExtractModifier(variable, value))
-        variable = block.outputVariables.getVariable()[0]
-        return generator.ExtractModifier(variable, "%s(%s)"%(type, ", ".join(vars)))
+    if block_infos["type"] == "function":
+        output_variable = block.outputVariables.getVariable()[0]
+        output_name = "%s%d_OUT"%(type, block.getLocalId())
+        if not generator.ComputedBlocks.get(block, False) and not order:
+            if generator.Interface[-1][0] != "VAR" or generator.Interface[-1][1] or generator.Interface[-1][2] or generator.Interface[-1][3]:
+                generator.Interface.append(("VAR", False, False, False, []))
+            if output_variable.connectionPointOut in generator.ConnectionTypes:
+                generator.Interface[-1][4].append((generator.ConnectionTypes[output_variable.connectionPointOut], output_name, None, None))
+            else:
+                generator.Interface[-1][4].append(("ANY", output_name, None, None))
+            vars = []
+            for variable in block.inputVariables.getVariable():
+                connections = variable.connectionPointIn.getConnections()
+                if connections and len(connections) == 1:
+                    if body_type == "FBD" or body_type == "SFC":
+                        value = generator.ComputeFBDExpression(body, connections[0], executionOrderId > 0)
+                    elif body_type == "LD":
+                        paths = generator.GenerateLDPaths(variable.connectionPointIn.getConnections(), body)
+                        if len(paths) > 0:
+                            paths = tuple(paths)
+                        else:
+                            paths = paths[0] 
+                        value = generator.ComputeLDExpression(paths, True)
+                    vars.append(generator.ExtractModifier(variable, value))
+            generator.Program += "  %s := %s(%s);\n"%(output_name, type, ", ".join(vars))
+            generator.ComputedBlocks[block] = True
+        return generator.ExtractModifier(output_variable, output_name)
     elif block_infos["type"] == "functionBlock":
-        if not generator.ComputedBlocks.get(name, False):
+        if not generator.ComputedBlocks.get(block, False) and not order:
             vars = []
             for variable in block.inputVariables.getVariable():
                 connections = variable.connectionPointIn.getConnections()
                 if connections and len(connections) == 1:
                     parameter = variable.getFormalParameter()
                     if body_type == "FBD" or body_type == "SFC":
-                        value = generator.ComputeFBDExpression(body, connections[0])
+                        value = generator.ComputeFBDExpression(body, connections[0], executionOrderId > 0)
                         vars.append("%s := %s"%(parameter, generator.ExtractModifier(variable, value)))
                     elif body_type == "LD":
                         paths = generator.GenerateLDPaths(variable.connectionPointIn.getConnections(), body)
@@ -75,7 +85,7 @@
                         value = generator.ComputeLDExpression(paths, True)
                     vars.append("%s := %s"%(parameter, generator.ExtractModifier(variable, value)))
             generator.Program += "  %s(%s);\n"%(name, ", ".join(vars))
-            generator.ComputedBlocks[name] = True
+            generator.ComputedBlocks[block] = True
         if link:
             connectionPoint = link.getPosition()[-1]
         else:
@@ -281,6 +291,31 @@
 
 TypeHierarchy = dict(TypeHierarchy_list)
 
+def ResetTypeHierarchy():
+    TypeHierarchy = dict(TypeHierarchy_list)
+    
+def AddDataTypeHierarchy(name, reference):
+    TypeHierarchy[name] = reference
+
+DataTypeRange_list = [
+    ("SINT", (-2**7, 2**7 - 1)),
+    ("INT", (-2**15, 2**15 - 1)),
+    ("DINT", (-2**31, 2**31 - 1)),
+    ("LINT", (-2**31, 2**31 - 1)),
+    ("USINT", (0, 2**8 - 1)),
+    ("UINT", (0, 2**16 - 1)),
+    ("UDINT", (0, 2**31 - 1)),
+    ("ULINT", (0, 2**31 - 1))
+]
+
+DataTypeRange = dict(DataTypeRange_list)
+
+def ResetDataTypeRange():
+    DataTypeRange = dict(DataTypeRange_list)
+    
+def AddDataTypeRange(name, range):
+    DataTypeRange[name] = range
+
 """
 returns true if the given data type is the same that "reference" meta-type or one of its types.
 """
@@ -296,16 +331,32 @@
 
 def IsEndType(reference):
     if reference is not None:
-        return len([typename for typename, parenttype in TypeHierarchy_list if parenttype == reference]) == 0
+        return not reference.startswith("ANY")
     else:
         return True
 
+def GetDataTypeRange(reference):
+    while reference is not None:
+        if reference in DataTypeRange:
+            return DataTypeRange[reference]
+        else:
+            reference = TypeHierarchy[reference]
+    return None
+
 """
 returns list of all types that correspont to the ANY* meta type
 """
 def GetSubTypes(reference):
-    return [typename for typename, parenttype in TypeHierarchy_list if typename[:3] != "ANY" and IsOfType(typename, reference)]
-
+    return [typename for typename, parenttype in TypeHierarchy.items() if not typename.startswith("ANY") and IsOfType(typename, reference)]
+
+
+EnumeratedDataValues = []
+
+def ResetEnumeratedDataValues():
+    EnumeratedDataValues = []
+    
+def AddEnumeratedDataValues(values):
+    EnumeratedDataValues.extend(values)
 
 #-------------------------------------------------------------------------------
 #                             Test identifier
@@ -489,7 +540,7 @@
                 else:
                     input_ovrloading_types = [None]
                     output_types = [None]
-                    
+                
                 funcdeclname_orig = Function_decl["name"]
                 funcdeclname = Function_decl["name"].strip('*_')
                 fdc = Function_decl["inputs"][:]
--- a/xmlclass/xmlclass.py	Tue Nov 27 12:58:34 2007 +0100
+++ b/xmlclass/xmlclass.py	Thu Dec 06 18:05:29 2007 +0100
@@ -212,8 +212,11 @@
                             classname = "%s_%s"%(parent, nodename)
                         else:
                             classname = nodename
-                        self.GenerateXSDClasses(node, classname)
-                        nodetype = "cls:%s"%classname
+                        if len(node.childNodes) > 0:
+                            self.GenerateXSDClasses(node, classname)
+                            nodetype = "cls:%s"%classname
+                        else:
+                            nodetype = classname
                     if name == "attribute":
                         if "use" in node._attrs:
                             use = GetAttributeValue(node._attrs["use"])
@@ -683,8 +686,10 @@
                         elif attr_type.startswith("cls:"):
                             val = classes[attr_type[4:].replace("[]","")]()
                             val.loadXMLTree(node)
+                        else:
+                            val = None
                         # Stock value in content attribute
-                        if val:
+                        if val is not None:
                             if attr_type.endswith("[]"):
                                 if self.content:
                                     self.content["value"].append(val)
@@ -692,6 +697,8 @@
                                     self.content = {"name":name,"value":[val]}
                             else:
                                 self.content = {"name":name,"value":val}
+                        else:
+                            self.content = {"name":name,"value":None}
                     
                     # Class has a list of attributes that can have different value types
                     elif "multichoice_content" in members.keys() and name in members["multichoice_content"].keys():
@@ -803,8 +810,11 @@
                     elif value_type.endswith("[]"):
                         for content in self.content["value"]:
                             text += content.generateXMLText(self.content["name"], indent + 1)
+                    elif self.content["value"] is not None:
+                        text += self.content["value"].generateXMLText(self.content["name"], indent + 1)
                     else:
-                        text += self.content["value"].generateXMLText(self.content["name"], indent + 1)
+                        ind5, ind6 = getIndent(indent + 1, self.content["name"])
+                        text += ind5 + "<%s/>\n"%self.content["name"]
             elif attr == "multichoice_content":
                 if len(self.content) > 0:
                     for element in self.content: