merged Sergeys changes
authorEdouard Tisserant <edouard.tisserant@gmail.com>
Fri, 02 Mar 2018 17:01:25 +0100
changeset 1973 cc7a46953471
parent 1961 b4a1ba9dbaf3 (current diff)
parent 1972 38e912c8bd31 (diff)
child 1975 6b357f1e3134
child 2012 6e3f757280dc
merged Sergeys changes
PLCControler.py
controls/DebugVariablePanel/DebugVariableGraphicViewer.py
controls/ProjectPropertiesPanel.py
controls/VariablePanel.py
plcopen/BlockInstanceCollector.py
plcopen/POUVariablesCollector.py
runtime/loglevels.py
runtime/typemapping.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([])
--- 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:
--- 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
--- 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(
--- 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)
 
--- 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,
--- 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)
--- 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)
--- 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
--- 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)
--- 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))
 
 
--- 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 )?)")