--- a/CodeFileTreeNode.py Mon Mar 19 16:18:17 2018 +0100
+++ b/CodeFileTreeNode.py Sun Mar 25 16:22:36 2018 +0200
@@ -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()]))
+ None, name, format,
+ exclude=dict([(var.getname().upper(), True)
+ for var in self.CodeFile.variables.getvariable()]))
def SetVariables(self, variables):
self.CodeFile.variables.setvariable([])
--- a/PLCControler.py Mon Mar 19 16:18:17 2018 +0100
+++ b/PLCControler.py Sun Mar 25 16:22:36 2018 +0200
@@ -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 @@
return text
def GenerateNewName(self, tagname, name, format, start_idx=0, exclude=None, debug=False):
+ if name is not None:
+ result = re.search(VARIABLE_NAME_SUFFIX_MODEL, name)
+ if result is not None:
+ format = name[:result.start(1)] + '%d'
+ start_idx = int(result.group(1))
+ else:
+ format = name + '%d'
+
names = {} if exclude is None else exclude.copy()
if tagname is not None:
names.update(dict([(varname.upper(), True)
@@ -1829,6 +1838,14 @@
PLCOpenParser.GetElementClass("connector", "commonObjects"),
PLCOpenParser.GetElementClass("continuation", "commonObjects"))):
names[instance.getname().upper()] = True
+ elif words[0] == 'R':
+ 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
else:
project = self.GetProject(debug)
if project is not None:
--- a/controls/DebugVariablePanel/DebugVariableGraphicViewer.py Mon Mar 19 16:18:17 2018 +0100
+++ b/controls/DebugVariablePanel/DebugVariableGraphicViewer.py Sun Mar 25 16:22:36 2018 +0200
@@ -48,7 +48,7 @@
# Canvas height
[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 Mon Mar 19 16:18:17 2018 +0100
+++ b/controls/DebugVariablePanel/DebugVariableItem.py Sun Mar 25 16:22:36 2018 +0200
@@ -24,6 +24,7 @@
from __future__ import absolute_import
+from datetime import timedelta
import binascii
import numpy
from graphics.DebugDataConsumer import DebugDataConsumer, TYPE_TRANSLATOR
@@ -219,18 +220,20 @@
else:
self.Data = None
-
+ self.MinValue = None
+ self.MaxValue = None
# Init variable value
self.Value = ""
def IsNumVariable(self):
"""
Return if variable data type is numeric. String variables are
- considered as numeric (string CRC)
+ considered as numeric (string CRC). Time variables are considered
+ as number of seconds
@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"]
- else float(value))
+ 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())
+ else:
+ 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"] \
else self.Data[idx, 1:3]
+ if self.VariableType in ["TIME", "TOD", "DT", "DATE"]:
+ value = timedelta(seconds=value)
+
# Get raw value if asked
if not raw:
value = TYPE_TRANSLATOR.get(
--- a/controls/DebugVariablePanel/DebugVariablePanel.py Mon Mar 19 16:18:17 2018 +0100
+++ b/controls/DebugVariablePanel/DebugVariablePanel.py Sun Mar 25 16:22:36 2018 +0200
@@ -237,7 +237,7 @@
default_range_idx = 0
for idx, (text, _value) in enumerate(RANGE_VALUES):
self.CanvasRange.Append(text)
- if text == "1s":
+ if _value == 1000000000:
default_range_idx = idx
self.CanvasRange.SetSelection(default_range_idx)
--- a/controls/ProjectPropertiesPanel.py Mon Mar 19 16:18:17 2018 +0100
+++ b/controls/ProjectPropertiesPanel.py Sun Mar 25 16:22:36 2018 +0200
@@ -27,6 +27,8 @@
import wx
from wx.lib.scrolledpanel import ScrolledPanel
+from xmlclass.xmlclass import URI_model
+
# -------------------------------------------------------------------------------
# Helpers
# -------------------------------------------------------------------------------
@@ -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 == "":
new_value = None
+ if name == 'companyURL':
+ if not URI_model.match(new_value):
+ new_value = None
+ dialog = wx.MessageDialog(self, _('Invalid URL!\n'
+ 'Please enter correct URL address.'),
+ _("Error"), wx.OK | wx.ICON_ERROR)
+ dialog.ShowModal()
+ dialog.Destroy()
if old_value != new_value:
self.Controller.SetProjectProperties(properties={name: new_value})
self.ParentWindow._Refresh(TITLE, FILEMENU, EDITMENU,
--- a/controls/VariablePanel.py Mon Mar 19 16:18:17 2018 +0100
+++ b/controls/VariablePanel.py Sun Mar 25 16:22:36 2018 +0200
@@ -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.FilterChoices = []
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()
if new_row > 0:
- row_content = self.Values[new_row - 1].copy()
-
- result = VARIABLE_NAME_SUFFIX_MODEL.search(row_content.Name)
- if result is not None:
- name = row_content.Name[:result.start(1)]
- suffix = result.group(1)
- if suffix != "":
- start_idx = int(suffix)
- else:
- start_idx = 0
- else:
- name = row_content.Name
- start_idx = 0
- else:
- row_content = None
- start_idx = 0
- name = "LocalVar"
-
- if row_content is not None and row_content.Edit:
- row_content = self.Values[new_row - 1].copy()
- else:
- row_content = self.DefaultValue.copy()
- if self.Filter in self.DefaultTypes:
- row_content.Class = self.DefaultTypes[self.Filter]
- else:
- 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 Mon Mar 19 16:18:17 2018 +0100
+++ b/editors/CodeFileEditor.py Sun Mar 25 16:22:36 2018 +0200
@@ -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 = {
- "Name": "",
+ "Name": "LocalVar0",
"Type": DefaultType,
"Initial": "",
"Description": "",
@@ -694,19 +693,9 @@
def _AddVariable(new_row):
if new_row > 0:
row_content = self.Table.data[new_row - 1].copy()
- result = VARIABLE_NAME_SUFFIX_MODEL.search(row_content["Name"])
- if result is not None:
- name = row_content["Name"][:result.start(1)]
- suffix = result.group(1)
- if suffix != "":
- start_idx = int(suffix)
- else:
- start_idx = 0
- else:
- name = row_content["Name"]
- start_idx = 0
- row_content["Name"] = self.Controler.GenerateNewName(
- name + "%d", start_idx)
+ old_name = row_content['Name']
+ row_content['Name'] =\
+ self.Controler.GenerateNewName(old_name, old_name+'%d')
else:
row_content = self.VariablesDefaultValue.copy()
self.Table.InsertRow(new_row, row_content)
--- a/editors/ResourceEditor.py Mon Mar 19 16:18:17 2018 +0100
+++ b/editors/ResourceEditor.py Sun Mar 25 16:22:36 2018 +0200
@@ -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})
def _AddTask(new_row):
- self.TasksTable.InsertRow(new_row, self.TasksDefaultValue.copy())
+ if new_row > 0:
+ row_content = self.TasksTable.data[new_row-1].copy()
+ old_name = row_content['Name']
+ row_content['Name'] =\
+ self.Controler.GenerateNewName(self.TagName, old_name, old_name+'%d')
+ else:
+ row_content = self.TasksDefaultValue.copy()
+
+ self.TasksTable.InsertRow(new_row, row_content)
self.RefreshModel()
self.RefreshView()
return new_row
@@ -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())
+ if new_row > 0:
+ row_content = self.InstancesTable.data[new_row - 1].copy()
+ old_name = row_content['Name']
+ row_content['Name'] =\
+ self.Controler.GenerateNewName(self.TagName, old_name, old_name+'%d')
+ else:
+ row_content = self.InstancesDefaultValue.copy()
+
+ self.InstancesTable.InsertRow(new_row, row_content)
self.RefreshModel()
self.RefreshView()
return new_row
--- a/plcopen/plcopen.py Mon Mar 19 16:18:17 2018 +0100
+++ b/plcopen/plcopen.py Sun Mar 25 16:22:36 2018 +0200
@@ -152,12 +152,12 @@
test_result = []
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():
break
return test_result
@@ -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:
+ if func is not None and value is not None:
func(value)
elif attr in ["modificationDateTime", "organization", "language"]:
setattr(contentheader_obj, attr, value)
--- a/runtime/typemapping.py Mon Mar 19 16:18:17 2018 +0100
+++ b/runtime/typemapping.py Sun Mar 25 16:22:36 2018 +0200
@@ -35,7 +35,7 @@
def _ttime():
return (IEC_TIME,
- 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 Mon Mar 19 16:18:17 2018 +0100
+++ b/xmlclass/xmlclass.py Sun Mar 25 16:22:36 2018 +0200
@@ -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 )?)")