# HG changeset patch # User lbessard # Date 1196960729 -3600 # Node ID 394d9f168258e2d26e47a6e5dca7f1b80b5ecc7a # Parent 635d0817508c48cdf6d748ef99b7247775cd3fed Adding support for execution order in PLCGenerator Adding support for derived data types (struct not supported yet) Fixed refresh bug with windows on Viewers diff -r 635d0817508c -r 394d9f168258 DataTypeEditor.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() + diff -r 635d0817508c -r 394d9f168258 PLCControler.py --- 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__ diff -r 635d0817508c -r 394d9f168258 PLCGenerator.py --- 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(): diff -r 635d0817508c -r 394d9f168258 PLCOpenEditor.py --- 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): diff -r 635d0817508c -r 394d9f168258 TextViewer.py --- 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: diff -r 635d0817508c -r 394d9f168258 Viewer.py --- 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() diff -r 635d0817508c -r 394d9f168258 plcopen/plcopen.py --- 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 + "\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) diff -r 635d0817508c -r 394d9f168258 plcopen/structures.py --- 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"][:] diff -r 635d0817508c -r 394d9f168258 xmlclass/xmlclass.py --- 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: