# HG changeset patch # User Edouard Tisserant # Date 1520006485 -3600 # Node ID cc7a469534712c604a0cb64453c6d5603bc8414b # Parent b4a1ba9dbaf32f8201a063c42912c985ba012e1f# Parent 38e912c8bd317b30c5db18f91d9f0a4083cdcd5d merged Sergeys changes diff -r b4a1ba9dbaf3 -r cc7a46953471 CodeFileTreeNode.py --- 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()])) + None, name, format, + exclude=dict([(var.getname().upper(), True) + for var in self.CodeFile.variables.getvariable()])) def SetVariables(self, variables): self.CodeFile.variables.setvariable([]) diff -r b4a1ba9dbaf3 -r cc7a46953471 PLCControler.py --- 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 @@ 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: diff -r b4a1ba9dbaf3 -r cc7a46953471 controls/DebugVariablePanel/DebugVariableGraphicViewer.py --- 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 @@ # 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 diff -r b4a1ba9dbaf3 -r cc7a46953471 controls/DebugVariablePanel/DebugVariableItem.py --- 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 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( diff -r b4a1ba9dbaf3 -r cc7a46953471 controls/DebugVariablePanel/DebugVariablePanel.py --- 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 @@ 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) diff -r b4a1ba9dbaf3 -r cc7a46953471 controls/ProjectPropertiesPanel.py --- 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 @@ 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, diff -r b4a1ba9dbaf3 -r cc7a46953471 controls/VariablePanel.py --- 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.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) diff -r b4a1ba9dbaf3 -r cc7a46953471 editors/CodeFileEditor.py --- 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 = { - "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) diff -r b4a1ba9dbaf3 -r cc7a46953471 editors/ResourceEditor.py --- 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}) 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 diff -r b4a1ba9dbaf3 -r cc7a46953471 plcopen/plcopen.py --- 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 @@ 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) diff -r b4a1ba9dbaf3 -r cc7a46953471 runtime/typemapping.py --- 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 @@ 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)) diff -r b4a1ba9dbaf3 -r cc7a46953471 xmlclass/xmlclass.py --- 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 )?)")