--- a/CodeFileTreeNode.py Thu Mar 01 14:28:55 2018 +0100
+++ b/CodeFileTreeNode.py Fri Mar 02 17:01:25 2018 +0100
@@ -139,11 +139,11 @@
def GetDataTypes(self, basetypes=False):
return self.GetCTRoot().GetDataTypes(basetypes=basetypes)
- def GenerateNewName(self, format, start_idx):
+ def GenerateNewName(self, name, format): return self.GetCTRoot().GenerateNewName(
- None, None, format, start_idx,
- dict([(var.getname().upper(), True)
- for var in self.CodeFile.variables.getvariable()]))
+ exclude=dict([(var.getname().upper(), True) + for var in self.CodeFile.variables.getvariable()])) def SetVariables(self, variables):
self.CodeFile.variables.setvariable([])
--- a/PLCControler.py Thu Mar 01 14:28:55 2018 +0100
+++ b/PLCControler.py Fri Mar 02 17:01:25 2018 +0100
@@ -44,6 +44,7 @@
from PLCGenerator import *
duration_model = re.compile("(?:([0-9]{1,2})h)?(?:([0-9]{1,2})m(?!s))?(?:([0-9]{1,2})s)?(?:([0-9]{1,3}(?:\.[0-9]*)?)ms)?")
+VARIABLE_NAME_SUFFIX_MODEL = re.compile('(\d+)$') ScriptDirectory = paths.AbsDir(__file__)
@@ -1814,6 +1815,14 @@
def GenerateNewName(self, tagname, name, format, start_idx=0, exclude=None, debug=False):
+ result = re.search(VARIABLE_NAME_SUFFIX_MODEL, name) + format = name[:result.start(1)] + '%d' + start_idx = int(result.group(1)) names = {} if exclude is None else exclude.copy()
names.update(dict([(varname.upper(), True)
@@ -1829,6 +1838,14 @@
PLCOpenParser.GetElementClass("connector", "commonObjects"),
PLCOpenParser.GetElementClass("continuation", "commonObjects"))):
names[instance.getname().upper()] = True
+ element = self.GetEditedElement(tagname, debug) + for task in element.gettask(): + names[task.getname().upper()] = True + for instance in task.getpouInstance(): + names[instance.getname().upper()] = True + for instance in element.getpouInstance(): + names[instance.getname().upper()] = True project = self.GetProject(debug)
--- a/controls/DebugVariablePanel/DebugVariableGraphicViewer.py Thu Mar 01 14:28:55 2018 +0100
+++ b/controls/DebugVariablePanel/DebugVariableGraphicViewer.py Fri Mar 02 17:01:25 2018 +0100
@@ -48,7 +48,7 @@
[SIZE_MINI, SIZE_MIDDLE, SIZE_MAXI] = [0, 100, 200]
-CANVAS_BORDER = (20., 10.) # Border height on at bottom and top of graph
+CANVAS_BORDER = (30., 20.) # Border height on at bottom and top of graph CANVAS_PADDING = 8.5 # Border inside graph where no label is drawn
VALUE_LABEL_HEIGHT = 17. # Height of variable label in graph
AXES_LABEL_HEIGHT = 12.75 # Height of variable value in graph
--- a/controls/DebugVariablePanel/DebugVariableItem.py Thu Mar 01 14:28:55 2018 +0100
+++ b/controls/DebugVariablePanel/DebugVariableItem.py Fri Mar 02 17:01:25 2018 +0100
@@ -24,6 +24,7 @@
from __future__ import absolute_import
+from datetime import timedelta from graphics.DebugDataConsumer import DebugDataConsumer, TYPE_TRANSLATOR
@@ -219,18 +220,20 @@
Return if variable data type is numeric. String variables are
- considered as numeric (string CRC)
+ considered as numeric (string CRC). Time variables are considered @return: True if data type is numeric
return (self.Parent.IsNumType(self.VariableType) or
- self.VariableType in ["STRING", "WSTRING"])
+ self.VariableType in ["STRING", "WSTRING", "TIME", "TOD", "DT", "DATE"]) def NewValues(self, ticks, values):
@@ -253,10 +256,15 @@
# Translate forced flag to float for storing in Data table
forced_value = float(forced)
- # String data value is CRC
- num_value = (binascii.crc32(value) & STRING_CRC_MASK
- if self.VariableType in ["STRING", "WSTRING"]
+ if self.VariableType in ["STRING", "WSTRING"]: + # String data value is CRC + num_value = (binascii.crc32(value) & STRING_CRC_MASK) + elif self.VariableType in ["TIME", "TOD", "DT", "DATE"]: + # Numeric value of time type variables + # is represented in seconds + num_value = float(value.total_seconds()) + num_value = float(value) # Update variable range values
self.MinValue = (min(self.MinValue, num_value)
@@ -341,6 +349,9 @@
if self.VariableType in ["STRING", "WSTRING"] \
+ if self.VariableType in ["TIME", "TOD", "DT", "DATE"]: + value = timedelta(seconds=value) value = TYPE_TRANSLATOR.get(
--- a/controls/DebugVariablePanel/DebugVariablePanel.py Thu Mar 01 14:28:55 2018 +0100
+++ b/controls/DebugVariablePanel/DebugVariablePanel.py Fri Mar 02 17:01:25 2018 +0100
@@ -237,7 +237,7 @@
for idx, (text, _value) in enumerate(RANGE_VALUES):
self.CanvasRange.Append(text)
+ if _value == 1000000000: self.CanvasRange.SetSelection(default_range_idx)
--- a/controls/ProjectPropertiesPanel.py Thu Mar 01 14:28:55 2018 +0100
+++ b/controls/ProjectPropertiesPanel.py Fri Mar 02 17:01:25 2018 +0100
@@ -27,6 +27,8 @@
from wx.lib.scrolledpanel import ScrolledPanel
+from xmlclass.xmlclass import URI_model # -------------------------------------------------------------------------------
# -------------------------------------------------------------------------------
@@ -294,8 +296,16 @@
if self.Controller is not None and self.Values is not None:
old_value = self.Values.get(name)
new_value = textctrl.GetValue()
- if name not in REQUIRED_PARAMS and new_value == "":
+ if name in REQUIRED_PARAMS and new_value == "": + if name == 'companyURL': + if not URI_model.match(new_value): + dialog = wx.MessageDialog(self, _('Invalid URL!\n' + 'Please enter correct URL address.'), + _("Error"), wx.OK | wx.ICON_ERROR) if old_value != new_value:
self.Controller.SetProjectProperties(properties={name: new_value})
self.ParentWindow._Refresh(TITLE, FILEMENU, EDITMENU,
--- a/controls/VariablePanel.py Thu Mar 01 14:28:55 2018 +0100
+++ b/controls/VariablePanel.py Fri Mar 02 17:01:25 2018 +0100
@@ -105,7 +105,6 @@
LOCATION_MODEL = re.compile("((?:%[IQM](?:\*|(?:[XBWLD]?[0-9]+(?:\.[0-9]+)*)))?)$")
-VARIABLE_NAME_SUFFIX_MODEL = re.compile("([0-9]*)$")
# -------------------------------------------------------------------------------
@@ -514,7 +513,7 @@
self.FilterChoiceTransfer = GetFilterChoiceTransfer()
- self.DefaultValue = _VariableInfos("", "", "", "", "", True, "", DefaultType, ([], []), 0)
+ self.DefaultValue = _VariableInfos("LocalVar0", "", "", "", "", True, "", DefaultType, ([], []), 0) if element_type in ["config", "resource"]:
self.DefaultTypes = {"All": "Global"}
@@ -590,36 +589,16 @@
self.VariablesGrid.SetEditable(not self.Debug)
def _AddVariable(new_row):
+ row_content = self.DefaultValue.copy() - row_content = self.Values[new_row - 1].copy()
- result = VARIABLE_NAME_SUFFIX_MODEL.search(row_content.Name)
- name = row_content.Name[:result.start(1)]
- suffix = result.group(1)
- start_idx = int(suffix)
- name = row_content.Name
- if row_content is not None and row_content.Edit:
- row_content = self.Values[new_row - 1].copy()
- row_content = self.DefaultValue.copy()
- if self.Filter in self.DefaultTypes:
- row_content.Class = self.DefaultTypes[self.Filter]
- row_content.Class = self.Filter
- row_content.Name = self.Controler.GenerateNewName(
- self.TagName, None, name + "%d", start_idx)
+ # doesn't copy values of previous var if it's non-editable (like a FB) + if self.Values[new_row-1].Edit: + row_content = self.Values[new_row-1].copy() + old_name = self.Values[new_row-1].Name + row_content.Name = self.Controler.GenerateNewName( + self.TagName, old_name, old_name+'%d') + if not row_content.Class: + row_content.Class = self.DefaultTypes.get(self.Filter, self.Filter) if self.Filter == "All" and len(self.Values) > 0:
self.Values.insert(new_row, row_content)
--- a/editors/CodeFileEditor.py Thu Mar 01 14:28:55 2018 +0100
+++ b/editors/CodeFileEditor.py Fri Mar 02 17:01:25 2018 +0100
@@ -35,7 +35,6 @@
from plcopen.structures import TestIdentifier, IEC_KEYWORDS, DefaultType
from controls import CustomGrid, CustomTable
from controls.CustomStyledTextCtrl import CustomStyledTextCtrl, faces, GetCursorPos, NAVIGATION_KEYS
-from controls.VariablePanel import VARIABLE_NAME_SUFFIX_MODEL
from editors.ConfTreeNodeEditor import ConfTreeNodeEditor
from util.BitmapLibrary import GetBitmap
from util.TranslationCatalogs import NoTranslate
@@ -674,7 +673,7 @@
self.Controler = controler
self.VariablesDefaultValue = {
@@ -694,19 +693,9 @@
def _AddVariable(new_row):
row_content = self.Table.data[new_row - 1].copy()
- result = VARIABLE_NAME_SUFFIX_MODEL.search(row_content["Name"])
- name = row_content["Name"][:result.start(1)]
- suffix = result.group(1)
- start_idx = int(suffix)
- name = row_content["Name"]
- row_content["Name"] = self.Controler.GenerateNewName(
- name + "%d", start_idx)
+ old_name = row_content['Name'] + self.Controler.GenerateNewName(old_name, old_name+'%d') row_content = self.VariablesDefaultValue.copy()
self.Table.InsertRow(new_row, row_content)
--- a/editors/ResourceEditor.py Thu Mar 01 14:28:55 2018 +0100
+++ b/editors/ResourceEditor.py Fri Mar 02 17:01:25 2018 +0100
@@ -303,7 +303,8 @@
self.RefreshHighlightsTimer = wx.Timer(self, -1)
self.Bind(wx.EVT_TIMER, self.OnRefreshHighlightsTimer, self.RefreshHighlightsTimer)
- self.TasksDefaultValue = {"Name": "", "Triggering": "", "Single": "", "Interval": "", "Priority": 0}
+ self.TasksDefaultValue = {"Name": "task0", "Triggering": "Cyclic", + "Single": "", "Interval": "T#20ms", "Priority": 0} self.TasksTable = ResourceTable(self, [], GetTasksTableColnames())
self.TasksTable.SetColAlignements([wx.ALIGN_LEFT, wx.ALIGN_LEFT, wx.ALIGN_LEFT, wx.ALIGN_RIGHT, wx.ALIGN_RIGHT])
self.TasksTable.SetColSizes([200, 100, 100, 150, 100])
@@ -314,7 +315,15 @@
"Down": self.DownTaskButton})
- self.TasksTable.InsertRow(new_row, self.TasksDefaultValue.copy())
+ row_content = self.TasksTable.data[new_row-1].copy() + old_name = row_content['Name'] + self.Controler.GenerateNewName(self.TagName, old_name, old_name+'%d') + row_content = self.TasksDefaultValue.copy() + self.TasksTable.InsertRow(new_row, row_content) @@ -338,7 +347,7 @@
self.TasksTable.ResetView(self.TasksGrid)
self.TasksGrid.RefreshButtons()
- self.InstancesDefaultValue = {"Name": "", "Type": "", "Task": ""}
+ self.InstancesDefaultValue = {"Name": "instance0", "Type": "", "Task": ""} self.InstancesTable = ResourceTable(self, [], GetInstancesTableColnames())
self.InstancesTable.SetColAlignements([wx.ALIGN_LEFT, wx.ALIGN_LEFT, wx.ALIGN_LEFT])
self.InstancesTable.SetColSizes([200, 150, 150])
@@ -349,7 +358,15 @@
"Down": self.DownInstanceButton})
def _AddInstance(new_row):
- self.InstancesTable.InsertRow(new_row, self.InstancesDefaultValue.copy())
+ row_content = self.InstancesTable.data[new_row - 1].copy() + old_name = row_content['Name'] + self.Controler.GenerateNewName(self.TagName, old_name, old_name+'%d') + row_content = self.InstancesDefaultValue.copy() + self.InstancesTable.InsertRow(new_row, row_content) --- a/plcopen/plcopen.py Thu Mar 01 14:28:55 2018 +0100
+++ b/plcopen/plcopen.py Fri Mar 02 17:01:25 2018 +0100
@@ -152,12 +152,12 @@
result = criteria["pattern"].search(text)
while result is not None:
- prev_pos = result.endpos
+ prev_pos = result.span()[1] start = TextLenInRowColumn(text[:result.start()])
end = TextLenInRowColumn(text[:result.end() - 1])
test_result.append((start, end, "\n".join(lines[start[0]:end[0] + 1])))
result = criteria["pattern"].search(text, result.end())
- if result is not None and prev_pos == result.endpos:
+ if result is not None and prev_pos == result.end(): @@ -441,7 +441,7 @@
"authorName": contentheader_obj.setauthor,
"pageSize": lambda v: contentheader_obj.setpageSize(*v),
"scaling": contentheader_obj.setscaling}.get(attr)
+ if func is not None and value is not None: elif attr in ["modificationDateTime", "organization", "language"]:
setattr(contentheader_obj, attr, value)
--- a/runtime/typemapping.py Thu Mar 01 14:28:55 2018 +0100
+++ b/runtime/typemapping.py Fri Mar 02 17:01:25 2018 +0100
@@ -35,7 +35,7 @@
- lambda x: td(0, x.s, x.ns/1000),
+ lambda x: td(0, x.s, x.ns/1000.0), lambda t, x: t(x.days * 24 * 3600 + x.seconds, x.microseconds*1000))
--- a/xmlclass/xmlclass.py Thu Mar 01 14:28:55 2018 +0100
+++ b/xmlclass/xmlclass.py Fri Mar 02 17:01:25 2018 +0100
@@ -67,7 +67,7 @@
QName_model = re.compile('((?:[a-zA-Z_][\w]*:)?[a-zA-Z_][\w]*)$')
QNames_model = re.compile('((?:[a-zA-Z_][\w]*:)?[a-zA-Z_][\w]*(?: (?:[a-zA-Z_][\w]*:)?[a-zA-Z_][\w]*)*)$')
NCName_model = re.compile('([a-zA-Z_][\w]*)$')
-URI_model = re.compile('((?:http://|/)?(?:[\w.-]*/?)*)$')
+URI_model = re.compile('((?:htt(p|ps)://|/)?(?:[\w.-]*/?)*)$') LANGUAGE_model = re.compile('([a-zA-Z]{1,8}(?:-[a-zA-Z0-9]{1,8})*)$')
ONLY_ANNOTATION = re.compile("((?:annotation )?)")