# HG changeset patch # User Laurent Bessard # Date 1377879030 -7200 # Node ID 9ffc49bfdf9d1aa6e9ef5f91ac395755ee72a87f # Parent f034fb2b1aabd709d37b9780178de1724ad7c659 Fixed copy/paste with xmlclass refactoring diff -r f034fb2b1aab -r 9ffc49bfdf9d PLCControler.py --- a/PLCControler.py Fri Aug 30 10:59:06 2013 +0200 +++ b/PLCControler.py Fri Aug 30 18:10:30 2013 +0200 @@ -25,12 +25,12 @@ from xml.dom import minidom from types import StringType, UnicodeType, TupleType import cPickle +from copy import deepcopy import os,sys,re import datetime from time import localtime -from plcopen import PLCOpenParser, LoadProject, SaveProject, QualifierList, rect -from plcopen.structures import * +from plcopen import* from graphics.GraphicCommons import * from PLCGenerator import * @@ -247,7 +247,7 @@ self.CreateProjectBuffer(False) self.ProgramChunks = [] self.ProgramOffset = 0 - self.NextCompiledProject = self.Project #self.Copy(self.Project) + self.NextCompiledProject = self.Copy(self.Project) self.CurrentCompiledProject = None self.Buffering = False @@ -758,7 +758,7 @@ if self.Project is not None: try: self.ProgramChunks = GenerateCurrentProgram(self, self.Project, errors, warnings) - self.NextCompiledProject = self.Project #self.Copy(self.Project) + self.NextCompiledProject = self.Copy(self.Project) program_text = "".join([item[0] for item in self.ProgramChunks]) if filepath is not None: programfile = open(filepath, "w") @@ -844,7 +844,7 @@ if self.Project is not None: pou = self.Project.getpou(pou_name) if pou is not None: - return pou.generateXMLText('pou', 0) + return pou.tostring() return None def PastePou(self, pou_type, pou_xml): @@ -852,47 +852,40 @@ Adds the POU defined by 'pou_xml' to the current project with type 'pou_type' ''' try: - tree = minidom.parseString(pou_xml.encode("utf-8")) - root = tree.childNodes[0] + new_pou = LoadPou(pou_xml) except: return _("Couldn't paste non-POU object.") - - if root.nodeName == "pou": - new_pou = plcopen.pous_pou() - new_pou.loadXMLTree(root) - - name = new_pou.getname() + + name = new_pou.getname() + + idx = 0 + new_name = name + while self.Project.getpou(new_name): + # a POU with that name already exists. + # make a new name and test if a POU with that name exists. + # append an incrementing numeric suffix to the POU name. + idx += 1 + new_name = "%s%d" % (name, idx) - idx = 0 - new_name = name - while self.Project.getpou(new_name): - # a POU with that name already exists. - # make a new name and test if a POU with that name exists. - # append an incrementing numeric suffix to the POU name. - idx += 1 - new_name = "%s%d" % (name, idx) - - # we've found a name that does not already exist, use it - new_pou.setname(new_name) + # we've found a name that does not already exist, use it + new_pou.setname(new_name) + + if pou_type is not None: + orig_type = new_pou.getpouType() + + # prevent violations of POU content restrictions: + # function blocks cannot be pasted as functions, + # programs cannot be pasted as functions or function blocks + if orig_type == 'functionBlock' and pou_type == 'function' or \ + orig_type == 'program' and pou_type in ['function', 'functionBlock']: + return _('''%s "%s" can't be pasted as a %s.''') % (orig_type, name, pou_type) - if pou_type is not None: - orig_type = new_pou.getpouType() - - # prevent violations of POU content restrictions: - # function blocks cannot be pasted as functions, - # programs cannot be pasted as functions or function blocks - if orig_type == 'functionBlock' and pou_type == 'function' or \ - orig_type == 'program' and pou_type in ['function', 'functionBlock']: - return _('''%s "%s" can't be pasted as a %s.''') % (orig_type, name, pou_type) - - new_pou.setpouType(pou_type) - - self.Project.insertpou(-1, new_pou) - self.BufferProject() - - return self.ComputePouName(new_name), - else: - return _("Couldn't paste non-POU object.") + new_pou.setpouType(pou_type) + + self.Project.insertpou(-1, new_pou) + self.BufferProject() + + return self.ComputePouName(new_name), # Remove a Pou from project def ProjectRemovePou(self, pou_name): @@ -2170,22 +2163,28 @@ def GetEditedElementCopy(self, tagname, debug = False): element = self.GetEditedElement(tagname, debug) if element is not None: - name = element.__class__.__name__ - return element.generateXMLText(name.split("_")[-1], 0) + return element.tostring() return "" def GetEditedElementInstancesCopy(self, tagname, blocks_id = None, wires = None, debug = False): element = self.GetEditedElement(tagname, debug) text = "" if element is not None: - wires = dict([(wire, True) for wire in wires if wire[0] in blocks_id and wire[1] in blocks_id]) + wires = dict([(wire, True) + for wire in wires + if wire[0] in blocks_id and wire[1] in blocks_id]) + copy_body = PLCOpenParser.CreateElement("body", "pou") + element.append(copy_body) + copy_body.setcontent( + PLCOpenParser.CreateElement(element.getbodyType(), "body")) for id in blocks_id: instance = element.getinstance(id) if instance is not None: - instance_copy = self.Copy(instance) + copy_body.appendcontentInstance(self.Copy(instance)) + instance_copy = copy_body.getcontentInstance(id) instance_copy.filterConnections(wires) - name = instance_copy.__class__.__name__ - text += instance_copy.generateXMLText(name.split("_")[-1], 0) + text += instance_copy.tostring() + element.remove(copy_body) return text def GenerateNewName(self, tagname, name, format, start_idx=0, exclude={}, debug=False): @@ -2227,10 +2226,6 @@ i += 1 return name - CheckPasteCompatibility = {"SFC": lambda name: True, - "LD": lambda name: not name.startswith("sfcObjects"), - "FBD": lambda name: name.startswith("fbdObjects") or name.startswith("commonObjects")} - def PasteEditedElementInstances(self, tagname, text, new_pos, middle=False, debug=False): element = self.GetEditedElement(tagname, debug) element_name, element_type = self.GetEditedElementType(tagname, debug) @@ -2248,62 +2243,46 @@ used_id = dict([(instance.getlocalId(), True) for instance in element.getinstances()]) new_id = {} - text = "%s"%text - try: - tree = minidom.parseString(text.encode("utf-8")) + instances = LoadPouInstances(text.encode("utf-8"), bodytype) + if len(instances) == 0: + raise ValueError except: return _("Invalid plcopen element(s)!!!") - instances = [] + exclude = {} - for root in tree.childNodes: - if root.nodeType == tree.ELEMENT_NODE and root.nodeName == "paste": - for child in root.childNodes: - if child.nodeType == tree.ELEMENT_NODE: - if not child.nodeName in plcopen.ElementNameToClass: - return _("\"%s\" element can't be pasted here!!!")%child.nodeName - - classname = plcopen.ElementNameToClass[child.nodeName] - if not self.CheckPasteCompatibility[bodytype](classname): - return _("\"%s\" element can't be pasted here!!!")%child.nodeName - - classobj = getattr(plcopen, classname, None) - if classobj is not None: - instance = classobj() - instance.loadXMLTree(child) - if child.nodeName == "block": - blockname = instance.getinstanceName() - if blockname is not None: - blocktype = instance.gettypeName() - if element_type == "function": - return _("FunctionBlock \"%s\" can't be pasted in a Function!!!")%blocktype - blockname = self.GenerateNewName(tagname, - blockname, - "%s%%d"%blocktype, - debug=debug) - exclude[blockname] = True - instance.setinstanceName(blockname) - self.AddEditedElementPouVar(tagname, blocktype, blockname) - elif child.nodeName == "step": - stepname = self.GenerateNewName(tagname, - instance.getname(), - "Step%d", - exclude=exclude, - debug=debug) - exclude[stepname] = True - instance.setname(stepname) - localid = instance.getlocalId() - if not used_id.has_key(localid): - new_id[localid] = True - instances.append((child.nodeName, instance)) - - if len(instances) == 0: - return _("Invalid plcopen element(s)!!!") + for instance in instances: + element.addinstance(instance) + instance_type = instance.getLocalTag() + if instance_type == "block": + blockname = instance.getinstanceName() + if blockname is not None: + blocktype = instance.gettypeName() + if element_type == "function": + return _("FunctionBlock \"%s\" can't be pasted in a Function!!!")%blocktype + blockname = self.GenerateNewName(tagname, + blockname, + "%s%%d"%blocktype, + debug=debug) + exclude[blockname] = True + instance.setinstanceName(blockname) + self.AddEditedElementPouVar(tagname, blocktype, blockname) + elif instance_type == "step": + stepname = self.GenerateNewName(tagname, + instance.getname(), + "Step%d", + exclude=exclude, + debug=debug) + exclude[stepname] = True + instance.setname(stepname) + localid = instance.getlocalId() + if not used_id.has_key(localid): + new_id[localid] = True idx = 1 translate_id = {} - bbox = plcopen.rect() - for name, instance in instances: + bbox = rect() + for instance in instances: localId = instance.getlocalId() bbox.union(instance.getBoundingBox()) if used_id.has_key(localId): @@ -2336,12 +2315,11 @@ diff = (new_pos[0] - x, new_pos[1] - y) connections = {} - for name, instance in instances: + for instance in instances: connections.update(instance.updateConnectionsId(translate_id)) if getattr(instance, "setexecutionOrderId", None) is not None: instance.setexecutionOrderId(0) instance.translate(*diff) - element.addinstance(name, instance) return new_id, connections @@ -3131,7 +3109,7 @@ self.CreateProjectBuffer(True) self.ProgramChunks = [] self.ProgramOffset = 0 - self.NextCompiledProject = self.Project ## self.Copy(self.Project) + self.NextCompiledProject = self.Copy(self.Project) self.CurrentCompiledProject = None self.Buffering = False self.CurrentElementEditing = None @@ -3175,7 +3153,7 @@ Return a copy of the project """ def Copy(self, model): - return cPickle.loads(cPickle.dumps(model)) + return deepcopy(model) def CreateProjectBuffer(self, saved): if self.ProjectBufferEnabled: diff -r f034fb2b1aab -r 9ffc49bfdf9d plcopen/__init__.py --- a/plcopen/__init__.py Fri Aug 30 10:59:06 2013 +0200 +++ b/plcopen/__init__.py Fri Aug 30 18:10:30 2013 +0200 @@ -25,6 +25,7 @@ # Package initialisation # plcopen module dynamically creates its classes -from plcopen import PLCOpenParser, LoadProject, SaveProject, VarOrder, QualifierList, rect +from plcopen import PLCOpenParser, LoadProject, SaveProject, LoadPou, \ + LoadPouInstances, VarOrder, QualifierList, rect from structures import * diff -r f034fb2b1aab -r 9ffc49bfdf9d plcopen/plcopen.py --- a/plcopen/plcopen.py Fri Aug 30 10:59:06 2013 +0200 +++ b/plcopen/plcopen.py Fri Aug 30 18:10:30 2013 +0200 @@ -125,6 +125,38 @@ PLCOpenParser = GenerateParserFromXSD(os.path.join(os.path.split(__file__)[0], "tc6_xml_v201.xsd")) +LOAD_POU_PROJECT_TEMPLATE = """ + + + + + + + + + + + + %s + + + + + +""" + +def LOAD_POU_INSTANCES_PROJECT_TEMPLATE(body_type): + return LOAD_POU_PROJECT_TEMPLATE % """ + + + <%(body_type)s>%%s + +""" % locals() + def LoadProject(filepath): project_file = open(filepath) project_xml = project_file.read().replace( @@ -138,6 +170,22 @@ return etree.fromstring(project_xml, PLCOpenParser) +def LoadPou(xml_string): + root = etree.fromstring( + LOAD_POU_PROJECT_TEMPLATE % xml_string, + PLCOpenParser) + return root.xpath( + "/ppx:project/ppx:types/ppx:pous/ppx:pou", + namespaces=PLCOpenParser.NSMAP)[0] + +def LoadPouInstances(xml_string, body_type): + root = etree.fromstring( + LOAD_POU_INSTANCES_PROJECT_TEMPLATE(body_type) % xml_string, + PLCOpenParser) + return root.xpath( + "/ppx:project/ppx:types/ppx:pous/ppx:pou[@name='paste_pou']/ppx:body/ppx:%s/*" % body_type, + namespaces=PLCOpenParser.NSMAP) + def SaveProject(project, filepath): project_file = open(filepath, 'w') project_file.write(etree.tostring( @@ -2409,7 +2457,7 @@ def getconditionConnection(self): if self.condition is not None: content = self.condition.getcontent() - if content.getLocalTag() == "connection": + if content.getLocalTag() == "connectionPointIn": return content return None setattr(cls, "getconditionConnection", getconditionConnection) @@ -2425,7 +2473,7 @@ def translate(self, dx, dy): _translateSingle(self, dx, dy) condition_connection = self.getconditionConnection() - if condition_connection: + if condition_connection is not None: _translateConnections(condition_connection, dx, dy) setattr(cls, "translate", translate)