diff -r 6431f26aa501 -r 3291024e00da plcopen/plcopen.py --- a/plcopen/plcopen.py Thu Feb 16 14:34:40 2017 +0500 +++ b/plcopen/plcopen.py Thu Feb 16 14:35:12 2017 +0500 @@ -1,26 +1,26 @@ #!/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. +# This file is part of Beremiz, a Integrated Development Environment for +# programming IEC 61131-3 automates supporting plcopen standard and CanFestival. # -#Copyright (C) 2007: Edouard TISSERANT and Laurent BESSARD +# Copyright (C) 2007: Edouard TISSERANT and Laurent BESSARD # -#See COPYING file for copyrights details. +# 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 program 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 +# 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. +# This program 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 +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. from xmlclass import * from types import * @@ -111,10 +111,15 @@ lines = text.split("\n") return len(lines) - 1, len(lines[-1]) +def CompilePattern(criteria): + flag = 0 if criteria["case_sensitive"] else re.IGNORECASE + find_pattern = criteria["find_pattern"] + if not criteria["regular_expression"]: + find_pattern = re.escape(find_pattern) + criteria["pattern"] = re.compile(find_pattern, flag) + def TestTextElement(text, criteria): lines = text.splitlines() - if not criteria["case_sensitive"]: - text = text.upper() test_result = [] result = criteria["pattern"].search(text) while result is not None: @@ -124,6 +129,9 @@ result = criteria["pattern"].search(text, result.end()) return test_result +def TextMatched(str1, str2): + return str1 and str2 and (str1.upper() == str2.upper()) + PLCOpenParser = GenerateParserFromXSD(os.path.join(os.path.split(__file__)[0], "tc6_xml_v201.xsd")) PLCOpen_XPath = lambda xpath: etree.XPath(xpath, namespaces=PLCOpenParser.NSMAP) @@ -285,15 +293,8 @@ if cls: def updateElementName(self, old_name, new_name): text = self.getanyText() - index = text.find(old_name) - while index != -1: - if index > 0 and (text[index - 1].isalnum() or text[index - 1] == "_"): - index = text.find(old_name, index + len(old_name)) - elif index < len(text) - len(old_name) and (text[index + len(old_name)].isalnum() or text[index + len(old_name)] == "_"): - index = text.find(old_name, index + len(old_name)) - else: - text = text[:index] + new_name + text[index + len(old_name):] - index = text.find(old_name, index + len(new_name)) + pattern = re.compile('\\b' + old_name + '\\b', re.IGNORECASE) + text = pattern.sub(new_name, text) self.setanyText(text) setattr(cls, "updateElementName", updateElementName) @@ -311,14 +312,9 @@ setattr(cls, "updateElementAddress", updateElementAddress) def hasblock(self, block_type): - text = self.getanyText().upper() - index = text.find(block_type.upper()) - while index != -1: - if (not (index > 0 and (text[index - 1].isalnum() or text[index - 1] == "_")) and - not (index < len(text) - len(block_type) and text[index + len(block_type)] != "(")): - return True - index = text.find(block_type.upper(), index + len(block_type)) - return False + text = self.getanyText() + pattern = re.compile('\\b' + block_type + '\\b', re.IGNORECASE) + return pattern.search(text) is not None setattr(cls, "hasblock", hasblock) def Search(self, criteria, parent_infos): @@ -492,7 +488,8 @@ def addconfigurationResource(self, config_name, name): if self.getconfigurationResource(config_name, name) is not None: - raise ValueError, _("\"%s\" resource already exists in \"%s\" configuration !!!") % (name, config_name) + msg = _("\"{a1}\" resource already exists in \"{a2}\" configuration !!!").format(a1 = name, a2 = config_name) + raise ValueError, msg configuration = self.getconfiguration(config_name) if configuration is not None: new_resource = PLCOpenParser.CreateElement("resource", "configuration") @@ -509,7 +506,8 @@ configuration.remove(resource) found = True if not found: - raise ValueError, _("\"%s\" resource doesn't exist in \"%s\" configuration !!!")%(name, config_name) + msg = _("\"{a1}\" resource doesn't exist in \"{a2}\" configuration !!!").format(a1 = name, a2 = config_name) + raise ValueError, msg setattr(cls, "removeconfigurationResource", removeconfigurationResource) def updateElementName(self, old_name, new_name): @@ -633,9 +631,9 @@ for var in varlist.getvariable(): var_address = var.getaddress() if var_address is not None: - if var_address == old_name: + if TextMatched(var_address, old_name): var.setaddress(new_name) - if var.getname() == old_name: + if TextMatched(var.getname(), old_name): var.setname(new_name) def _updateConfigurationResourceElementAddress(self, address_model, new_leading): @@ -769,9 +767,9 @@ cls = PLCOpenParser.GetElementClass("task", "resource") if cls: def updateElementName(self, old_name, new_name): - if self.single == old_name: + if TextMatched(self.single, old_name): self.single = new_name - if self.interval == old_name: + if TextMatched(self.interval, old_name): self.interval = new_name for instance in self.getpouInstance(): instance.updateElementName(old_name, new_name) @@ -794,7 +792,7 @@ cls = PLCOpenParser.GetElementClass("pouInstance") if cls: def updateElementName(self, old_name, new_name): - if self.typeName == old_name: + if TextMatched(self.typeName, old_name): self.typeName = new_name setattr(cls, "updateElementName", updateElementName) @@ -856,7 +854,7 @@ def getdataTypeElement(self, name): elements = self.dataTypes.getdataType() for element in elements: - if element.getname() == name: + if TextMatched(element.getname(), name): return element return None setattr(cls, "getdataTypeElement", getdataTypeElement) @@ -875,7 +873,7 @@ def removedataTypeElement(self, name): found = False for element in self.dataTypes.getdataType(): - if element.getname() == name: + if TextMatched(element.getname(), name): self.dataTypes.remove(element) found = True break @@ -890,14 +888,14 @@ def getpouElement(self, name): elements = self.pous.getpou() for element in elements: - if element.getname() == name: + if TextMatched(element.getname(), name): return element return None setattr(cls, "getpouElement", getpouElement) def appendpouElement(self, name, pou_type, body_type): for element in self.pous.getpou(): - if element.getname() == name: + if TextMatched(element.getname(), name): raise ValueError, _("\"%s\" POU already exists !!!")%name new_pou = PLCOpenParser.CreateElement("pou", "pous") self.pous.appendpou(new_pou) @@ -914,7 +912,7 @@ def removepouElement(self, name): found = False for element in self.pous.getpou(): - if element.getname() == name: + if TextMatched(element.getname(), name): self.pous.remove(element) found = True break @@ -981,7 +979,7 @@ cls = PLCOpenParser.GetElementClass("derived", "dataType") if cls: def updateElementName(self, old_name, new_name): - if self.name == old_name: + if TextMatched(self.name, old_name): self.name = new_name setattr(cls, "updateElementName", updateElementName) @@ -1226,9 +1224,9 @@ for varlist in content: variables = varlist.getvariable() for var in variables: - if var.getname() == old_name: + if TextMatched(var.getname(), old_name): vartype_content = var.gettype().getcontent() - if vartype_content.getLocalTag() == "derived" and vartype_content.getname() == old_type: + if vartype_content.getLocalTag() == "derived" and TextMatched(vartype_content.getname(), old_type): var.setname(new_name) vartype_content.setname(new_type) return @@ -1239,21 +1237,28 @@ content = self.interface.getcontent() for varlist in content: for var in varlist.getvariable(): - if var.getname() == name: + if TextMatched(var.getname(), name): vartype_content = var.gettype().getcontent() - if vartype_content.getLocalTag() == "derived" and vartype_content.getname() == var_type: + if vartype_content.getLocalTag() == "derived" and TextMatched(vartype_content.getname(), var_type): varlist.remove(var) if len(varlist.getvariable()) == 0: self.interface.remove(varlist) break setattr(cls, "removepouVar", removepouVar) + + def hasstep(self, name=None): + if self.getbodyType() in ["SFC"]: + for instance in self.getinstances(): + if isinstance(instance, PLCOpenParser.GetElementClass("step", "sfcObjects")) and TextMatched(instance.getname(), name): + return True + return False + setattr(cls, "hasstep", hasstep) def hasblock(self, name=None, block_type=None): if self.getbodyType() in ["FBD", "LD", "SFC"]: for instance in self.getinstances(): if (isinstance(instance, PLCOpenParser.GetElementClass("block", "fbdObjects")) and - (name and instance.getinstanceName() == name or - block_type and instance.gettypeName() == block_type)): + (TextMatched(instance.getinstanceName(), name) or TextMatched(instance.gettypeName(), block_type))): return True if self.transitions: for transition in self.transitions.gettransition(): @@ -1279,15 +1284,13 @@ transition.setname(name) transition.setbodyType(body_type) if body_type == "ST": - transition.setanyText(":= ;") - elif body_type == "IL": - transition.setanyText("\tST\t%s"%name) + transition.settext(":= ;") setattr(cls, "addtransition", addtransition) def gettransition(self, name): if self.transitions is not None: for transition in self.transitions.gettransition(): - if transition.getname() == name: + if TextMatched(transition.getname(), name): return transition return None setattr(cls, "gettransition", gettransition) @@ -1302,7 +1305,7 @@ if self.transitions is not None: removed = False for transition in self.transitions.gettransition(): - if transition.getname() == name: + if TextMatched(transition.getname(), name): if transition.getbodyType() in ["FBD", "LD", "SFC"]: for instance in transition.getinstances(): if isinstance(instance, PLCOpenParser.GetElementClass("block", "fbdObjects")): @@ -1328,7 +1331,7 @@ def getaction(self, name): if self.actions is not None: for action in self.actions.getaction(): - if action.getname() == name: + if TextMatched(action.getname(), name): return action return None setattr(cls, "getaction", getaction) @@ -1343,7 +1346,7 @@ if self.actions is not None: removed = False for action in self.actions.getaction(): - if action.getname() == name: + if TextMatched(action.getname(), name): if action.getbodyType() in ["FBD", "LD", "SFC"]: for instance in action.getinstances(): if isinstance(instance, PLCOpenParser.GetElementClass("block", "fbdObjects")): @@ -1362,13 +1365,13 @@ for var in content.getvariable(): var_address = var.getaddress() if var_address is not None: - if var_address == old_name: + if TextMatched(var_address, old_name): var.setaddress(new_name) - if var.getname() == old_name: + if TextMatched(var.getname(), old_name): var.setname(new_name) var_type_content = var.gettype().getcontent() if var_type_content.getLocalTag() == "derived": - if var_type_content.getname() == old_name: + if TextMatched(var_type_content.getname(), old_name): var_type_content.setname(new_name) self.body[0].updateElementName(old_name, new_name) for action in self.getactionList(): @@ -1395,7 +1398,7 @@ if self.interface is not None: for content in self.interface.getcontent(): for variable in content.getvariable(): - if variable.getaddress() == address: + if TextMatched(variable.getaddress(), address): content.remove(variable) setattr(cls, "removeVariableByAddress", removeVariableByAddress) @@ -1414,7 +1417,8 @@ search_result = [] filter = criteria["filter"] if filter == "all" or self.getpouType() in filter: - parent_infos = parent_infos + ["P::%s" % self.getname()] + if parent_infos == []: + parent_infos = parent_infos + ["P::%s" % self.getname()] search_result.extend(_Search([("name", self.getname())], criteria, parent_infos)) if self.interface is not None: var_number = 0 @@ -1486,8 +1490,7 @@ if self.getbodyType() in ["FBD", "LD", "SFC"]: for instance in self.getinstances(): if (isinstance(instance, PLCOpenParser.GetElementClass("block", "fbdObjects")) and - (name and instance.getinstanceName() == name or - block_type and instance.gettypeName() == block_type)): + (TextMatched(instance.getinstanceName(), name) or TextMatched(instance.gettypeName(), block_type))): return True elif block_type is not None: return self.body.hasblock(block_type) @@ -1559,7 +1562,7 @@ cls = PLCOpenParser.GetElementClass("body") if cls: cls.currentExecutionOrderId = 0 - + cls.checkedBlocksDict = {} def resetcurrentExecutionOrderId(self): object.__setattr__(self, "currentExecutionOrderId", 0) setattr(cls, "resetcurrentExecutionOrderId", resetcurrentExecutionOrderId) @@ -1576,6 +1579,7 @@ PLCOpenParser.GetElementClass("connector", "commonObjects"), PLCOpenParser.GetElementClass("continuation", "commonObjects"))): element.setexecutionOrderId(0) + self.checkedBlocksDict.clear() else: raise TypeError, _("Can only generate execution order on FBD networks!") setattr(cls, "resetexecutionOrder", resetexecutionOrder) @@ -1598,16 +1602,19 @@ if self.content.getLocalTag() == "FBD": localid = link.getrefLocalId() instance = self.getcontentInstance(localid) + self.checkedBlocksDict[localid] = True if isinstance(instance, PLCOpenParser.GetElementClass("block", "fbdObjects")) and instance.getexecutionOrderId() == 0: for variable in instance.inputVariables.getvariable(): connections = variable.connectionPointIn.getconnections() if connections and len(connections) == 1: - self.compileelementExecutionOrder(connections[0]) - instance.setexecutionOrderId(self.getnewExecutionOrderId()) + if (self.checkedBlocksDict.has_key(connections[0].getrefLocalId()) == False): + self.compileelementExecutionOrder(connections[0]) + if instance.getexecutionOrderId() == 0: + instance.setexecutionOrderId(self.getnewExecutionOrderId()) elif isinstance(instance, PLCOpenParser.GetElementClass("continuation", "commonObjects")) and instance.getexecutionOrderId() == 0: - name = instance.getname() for tmp_instance in self.getcontentInstances(): - if isinstance(tmp_instance, PLCOpenParser.GetElementClass("connector", "commonObjects")) and tmp_instance.getname() == name and tmp_instance.getexecutionOrderId() == 0: + if (isinstance(tmp_instance, PLCOpenParser.GetElementClass("connector", "commonObjects")) and + TextMatched(tmp_instance.getname(), instance.getname()) and tmp_instance.getexecutionOrderId() == 0): connections = tmp_instance.connectionPointIn.getconnections() if connections and len(connections) == 1: self.compileelementExecutionOrder(connections[0]) @@ -1907,7 +1914,7 @@ setattr(cls, "getBoundingBox", getBoundingBox) def updateElementName(self, old_name, new_name): - if self.typeName == old_name: + if TextMatched(self.typeName, old_name): self.typeName = new_name setattr(cls, "updateElementName", updateElementName) @@ -1947,7 +1954,7 @@ _initElementClass("rightPowerRail", "ldObjects", "multiple") def _UpdateLDElementName(self, old_name, new_name): - if self.variable == old_name: + if TextMatched(self.variable, old_name): self.variable = new_name def _UpdateLDElementAddress(self, address_model, new_leading): @@ -2053,7 +2060,7 @@ content = self.condition.getcontent() content_name = content.getLocalTag() if content_name == "reference": - if content.getname() == old_name: + if TextMatched(content.getname(), old_name): content.setname(new_name) elif content_name == "inline": content.updateElementName(old_name, new_name) @@ -2125,7 +2132,7 @@ setattr(cls, "getinlineContent", getinlineContent) def updateElementName(self, old_name, new_name): - if self.reference is not None and self.reference.getname() == old_name: + if self.reference is not None and TextMatched(self.reference.getname(), old_name): self.reference.setname(new_name) if self.inline is not None: self.inline.updateElementName(old_name, new_name) @@ -2215,7 +2222,7 @@ return _Search([("expression", self.expression)], criteria, parent_infos + ["io_variable", self.getlocalId()]) def _UpdateIOElementName(self, old_name, new_name): - if self.expression == old_name: + if TextMatched(self.expression, old_name): self.expression = new_name def _UpdateIOElementAddress(self, address_model, new_leading): @@ -2248,7 +2255,7 @@ setattr(cls, "Search", _SearchInConnector) def updateElementName(self, old_name, new_name): - if self.name == old_name: + if TextMatched(self.name, old_name): self.name = new_name setattr(cls, "updateElementName", updateElementName) @@ -2257,7 +2264,7 @@ setattr(cls, "Search", _SearchInConnector) def updateElementName(self, old_name, new_name): - if self.name == old_name: + if TextMatched(self.name, old_name): self.name = new_name setattr(cls, "updateElementName", updateElementName)