Fixed copy/paste with xmlclass refactoring
authorLaurent Bessard
Fri, 30 Aug 2013 18:10:30 +0200
changeset 1299 9ffc49bfdf9d
parent 1298 f034fb2b1aab
child 1300 8e1ee07bdff8
Fixed copy/paste with xmlclass refactoring
PLCControler.py
plcopen/__init__.py
plcopen/plcopen.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 = "<paste>%s</paste>"%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:
--- 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 *
--- 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 = """
+<project xmlns:ns1="http://www.plcopen.org/xml/tc6_0201" 
+         xmlns:xhtml="http://www.w3.org/1999/xhtml" 
+         xmlns:xsd="http://www.w3.org/2001/XMLSchema" 
+         xmlns="http://www.plcopen.org/xml/tc6_0201">
+  <fileHeader companyName="" productName="" productVersion="" 
+              creationDateTime="1970-01-01T00:00:00"/>
+  <contentHeader name="paste_project">
+    <coordinateInfo>
+      <fbd><scaling x="0" y="0"/></fbd>
+      <ld><scaling x="0" y="0"/></ld>
+      <sfc><scaling x="0" y="0"/></sfc>
+    </coordinateInfo>
+  </contentHeader>
+  <types>
+    <dataTypes/>
+    <pous>%s</pous>
+  </types>
+  <instances>
+    <configurations/>
+  </instances>
+</project>
+"""
+
+def LOAD_POU_INSTANCES_PROJECT_TEMPLATE(body_type):
+    return LOAD_POU_PROJECT_TEMPLATE % """
+<pou name="paste_pou" pouType="program">
+  <body>
+    <%(body_type)s>%%s</%(body_type)s>
+  </body>
+</pou>""" % 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)