Fixed datatype and configuration editing in xmlclass refactoring
authorLaurent Bessard
Thu, 29 Aug 2013 19:18:41 +0200
changeset 1294 f02ba5b83811
parent 1293 40117d02601b
child 1295 2ad168756c5e
Fixed datatype and configuration editing in xmlclass refactoring
PLCControler.py
plcopen/plcopen.py
xmlclass/xmlclass.py
--- a/PLCControler.py	Thu Aug 29 00:28:39 2013 +0200
+++ b/PLCControler.py	Thu Aug 29 19:18:41 2013 +0200
@@ -1232,7 +1232,7 @@
             else:
                 tempvar.setaddress(None)
             if var['Documentation'] != "":
-                ft = plcopen.formattedText()
+                ft = PLCOpenParser.CreateElement("documentation", "variable")
                 ft.setanyText(var['Documentation'])
                 tempvar.setdocumentation(ft)
 
@@ -1366,7 +1366,7 @@
         if project is not None:
             # Found the resource corresponding to name
             resource = project.getconfigurationResource(config_name, name)
-            if resource:
+            if resource is not None:
                 # Extract variables from every varLists
                 for varlist in resource.getglobalVars():
                     for var in varlist.getvariable():
@@ -1483,7 +1483,7 @@
                     pou.interface = PLCOpenParser.CreateElement("interface", "pou")
                 # If there isn't any return type yet, add it
                 return_type_obj = pou.interface.getreturnType()
-                if not return_type_obj:
+                if return_type_obj is None:
                     return_type_obj = PLCOpenParser.CreateElement("returnType", "interface")
                     pou.interface.setreturnType(return_type_obj)
                 # Change return type
@@ -1500,12 +1500,12 @@
                 self.Project.RefreshCustomBlockTypes()
     
     def UpdateProjectUsedPous(self, old_name, new_name):
-        if self.Project:
+        if self.Project is not None:
             self.Project.updateElementName(old_name, new_name)
     
     def UpdateEditedElementUsedVariable(self, tagname, old_name, new_name):
         pou = self.GetEditedElement(tagname)
-        if pou:
+        if pou is not None:
             pou.updateElementName(old_name, new_name)
     
     # Return the return type of the pou given by its name
@@ -1938,21 +1938,23 @@
                     derived_datatype.setname(infos["base_type"])
                     datatype.baseType.setcontent(derived_datatype)
             elif infos["type"] == "Subrange":
-                datatype.baseType.setcontent(PLCOpenParser.CreateElement(
+                subrange = PLCOpenParser.CreateElement(
                     "subrangeUnsigned" 
                     if infos["base_type"] in GetSubTypes("ANY_UINT")
-                    else "subrangeSigned", "dataType"))
+                    else "subrangeSigned", "dataType")
+                datatype.baseType.setcontent(subrange)
                 subrange.range.setlower(infos["min"])
                 subrange.range.setupper(infos["max"])
                 if infos["base_type"] in self.GetBaseTypes():
                     subrange.baseType.setcontent(
-                        PLCOpenParser.CreateElement(infos["base_type"]))
+                        PLCOpenParser.CreateElement(infos["base_type"], "dataType"))
                 else:
                     derived_datatype = PLCOpenParser.CreateElement("derived", "dataType")
                     derived_datatype.setname(infos["base_type"])
                     subrange.baseType.setcontent(derived_datatype)
             elif infos["type"] == "Enumerated":
                 enumerated = PLCOpenParser.CreateElement("enum", "dataType")
+                datatype.baseType.setcontent(enumerated)
                 values = PLCOpenParser.CreateElement("values", "enum")
                 enumerated.setvalues(values)
                 for i, enum_value in enumerate(infos["values"]):
@@ -1962,9 +1964,9 @@
                         values.setvalue([value])
                     else:
                         values.appendvalue(value)
-                datatype.baseType.setcontent(enumerated)
             elif infos["type"] == "Array":
                 array = PLCOpenParser.CreateElement("array", "dataType")
+                datatype.baseType.setcontent(array)
                 for i, dimension in enumerate(infos["dimensions"]):
                     dimension_range = PLCOpenParser.CreateElement("dimension", "array")
                     dimension_range.setlower(dimension[0])
@@ -1982,9 +1984,9 @@
                     derived_datatype = PLCOpenParser.CreateElement("derived", "dataType")
                     derived_datatype.setname(infos["base_type"])
                     array.baseType.setcontent(derived_datatype)
-                datatype.baseType.setcontent(array)
             elif infos["type"] == "Structure":
                 struct = PLCOpenParser.CreateElement("struct", "dataType")
+                datatype.baseType.setcontent(struct)
                 for i, element_infos in enumerate(infos["elements"]):
                     element = PLCOpenParser.CreateElement("variable", "struct")
                     element.setname(element_infos["Name"])
@@ -1993,6 +1995,7 @@
                         if element_infos["Type"][0] == "array":
                             array_type, base_type_name, dimensions = element_infos["Type"]
                             array = PLCOpenParser.CreateElement("array", "dataType")
+                            element_type.setcontent(array)
                             for j, dimension in enumerate(dimensions):
                                 dimension_range = PLCOpenParser.CreateElement("dimension", "array")
                                 dimension_range.setlower(dimension[0])
@@ -2010,7 +2013,6 @@
                                 derived_datatype = PLCOpenParser.CreateElement("derived", "dataType")
                                 derived_datatype.setname(base_type_name)
                                 array.baseType.setcontent(derived_datatype)
-                            element_type.setcontent(array)
                     elif element_infos["Type"] in self.GetBaseTypes():
                         element_type.setcontent(
                             PLCOpenParser.CreateElement(
@@ -2030,7 +2032,6 @@
                         struct.setvariable([element])
                     else:
                         struct.appendvariable(element)
-                datatype.baseType.setcontent(struct)
             if infos["initial"] != "":
                 if datatype.initialValue is None:
                     datatype.initialValue = PLCOpenParser.CreateElement("initialValue", "dataType")
@@ -2705,18 +2706,13 @@
                     variable.text = value
                     contact.setvariable(variable)
                 elif param == "type":
-                    if value == CONTACT_NORMAL:
-                        contact.setnegated(False)
-                        contact.setedge("none")
-                    elif value == CONTACT_REVERSE:
-                        contact.setnegated(True)
-                        contact.setedge("none")
-                    elif value == CONTACT_RISING:
-                        contact.setnegated(False)
-                        contact.setedge("rising")
-                    elif value == CONTACT_FALLING:
-                        contact.setnegated(False)
-                        contact.setedge("falling")
+                    negated, edge = {
+                        CONTACT_NORMAL: (False, "none"),
+                        CONTACT_REVERSE: (True, "none"),
+                        CONTACT_RISING: (False, "rising"),
+                        CONTACT_FALLING: (False, "falling")}[value]
+                    contact.setnegated(negated)
+                    contact.setedge(edge)
                 elif param == "height":
                     contact.setheight(value)
                 elif param == "width":
@@ -2755,30 +2751,16 @@
                     variable.text = value
                     coil.setvariable(variable)
                 elif param == "type":
-                    if value == COIL_NORMAL:
-                        coil.setnegated(False)
-                        coil.setstorage("none")
-                        coil.setedge("none")
-                    elif value == COIL_REVERSE:
-                        coil.setnegated(True)
-                        coil.setstorage("none")
-                        coil.setedge("none")
-                    elif value == COIL_SET:
-                        coil.setnegated(False)
-                        coil.setstorage("set")
-                        coil.setedge("none")
-                    elif value == COIL_RESET:
-                        coil.setnegated(False)
-                        coil.setstorage("reset")
-                        coil.setedge("none")
-                    elif value == COIL_RISING:
-                        coil.setnegated(False)
-                        coil.setstorage("none")
-                        coil.setedge("rising")
-                    elif value == COIL_FALLING:
-                        coil.setnegated(False)
-                        coil.setstorage("none")
-                        coil.setedge("falling")
+                    negated, storage, edge = {
+                        COIL_NORMAL: (False, "none", "none"),
+                        COIL_REVERSE: (True, "none", "none"),
+                        COIL_SET: (False, "set", "none"),
+                        COIL_RESET: (False, "reset", "none"),
+                        COIL_RISING: (False, "none", "rising"),
+                        COIL_FALLING: (False, "none", "falling")}[value]
+                    coil.setnegated(negated)
+                    coil.setstorage(storage)
+                    coil.setedge(edge)
                 elif param == "height":
                     coil.setheight(value)
                 elif param == "width":
@@ -3046,7 +3028,8 @@
             resource.setpouInstance([])
             task_list = {}
             for task in tasks:
-                new_task = plcopen.resource_task()
+                new_task = PLCOpenParser.CreateElement("task", "resource")
+                resource.appendtask(new_task)
                 new_task.setname(task["Name"])
                 if task["Triggering"] == "Interrupt":
                     new_task.setsingle(task["Single"])
@@ -3066,12 +3049,16 @@
                 new_task.setpriority(int(task["Priority"]))
                 if task["Name"] != "":
                     task_list[task["Name"]] = new_task
-                resource.appendtask(new_task)
             for instance in instances:
-                new_instance = plcopen.pouInstance()
+                task = task_list.get(instance["Task"])
+                if task is not None:
+                    new_instance = PLCOpenParser.CreateElement("pouInstance", "task")
+                    task.appendpouInstance(new_instance)
+                else:
+                    new_instance = PLCOpenParser.CreateElement("pouInstance", "resource")
+                    resource.appendpouInstance(new_instance)
                 new_instance.setname(instance["Name"])
                 new_instance.settypeName(instance["Type"])
-                task_list.get(instance["Task"], resource).appendpouInstance(new_instance)
 
     def GetEditedResourceInfos(self, tagname, debug = False):
         resource = self.GetEditedElement(tagname, debug)
--- a/plcopen/plcopen.py	Thu Aug 29 00:28:39 2013 +0200
+++ b/plcopen/plcopen.py	Thu Aug 29 19:18:41 2013 +0200
@@ -127,11 +127,13 @@
 
 def LoadProject(filepath):
     project_file = open(filepath)
-    project_xml = project_file.read()\
-        .replace("http://www.plcopen.org/xml/tc6.xsd", 
-                 "http://www.plcopen.org/xml/tc6_0201")\
-        .replace("<![CDATA[", "<xhtml:p><![CDATA[")\
-        .replace("]]>", "]]></xhtml:p>")
+    project_xml = project_file.read().replace(
+        "http://www.plcopen.org/xml/tc6.xsd", 
+        "http://www.plcopen.org/xml/tc6_0201")
+    for cre, repl in [
+        (re.compile("(?<!<xhtml:p>)(?:<!\[CDATA\[)"), "<xhtml:p><![CDATA["),
+        (re.compile("(?:]]>)(?!</xhtml:p>)"), "]]></xhtml:p>")]:
+        project_xml = cre.sub(repl, project_xml)
     project_file.close()
     
     return etree.fromstring(project_xml, PLCOpenParser)
@@ -148,7 +150,7 @@
 cls = PLCOpenParser.GetElementClass("formattedText")
 if cls:
     def updateElementName(self, old_name, new_name):
-        text = self.text
+        text = self.getanyText()
         index = text.find(old_name)
         while index != -1:
             if index > 0 and (text[index - 1].isalnum() or text[index - 1] == "_"):
@@ -158,11 +160,11 @@
             else:
                 text = text[:index] + new_name + text[index + len(old_name):]
                 index = text.find(old_name, index + len(new_name))
-        self.text = text
+        self.setanyText(text)
     setattr(cls, "updateElementName", updateElementName)
     
     def updateElementAddress(self, address_model, new_leading):
-        text = self.text
+        text = self.getanyText()
         startpos = 0
         result = address_model.search(text, startpos)
         while result is not None:
@@ -171,11 +173,11 @@
             text = text[:result.start()] + new_address + text[result.end():]
             startpos = result.start() + len(new_address)
             result = address_model.search(self.text, startpos)
-        self.text = text
+        self.setanyText(text)
     setattr(cls, "updateElementAddress", updateElementAddress)
     
     def hasblock(self, block_type):
-        text = self.text.upper()
+        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 
@@ -326,7 +328,7 @@
 
     def getconfigurations(self):
         configurations = self.instances.configurations.getconfiguration()
-        if configurations:
+        if configurations is not None:
             return configurations
         return []
     setattr(cls, "getconfigurations", getconfigurations)
@@ -360,7 +362,7 @@
 
     def getconfigurationResource(self, config_name, name):
         configuration = self.getconfiguration(config_name)
-        if configuration:
+        if configuration is not None:
             for resource in configuration.getresource():
                 if resource.getname() == name:
                     return resource
@@ -369,7 +371,7 @@
 
     def addconfigurationResource(self, config_name, name):
         configuration = self.getconfiguration(config_name)
-        if configuration:
+        if configuration is not None:
             for resource in configuration.getresource():
                 if resource.getname() == name:
                     raise ValueError, _("\"%s\" resource already exists in \"%s\" configuration !!!")%(name, config_name)
@@ -380,7 +382,7 @@
 
     def removeconfigurationResource(self, config_name, name):
         configuration = self.getconfiguration(config_name)
-        if configuration:
+        if configuration is not None:
             found = False
             for idx, resource in enumerate(configuration.getresource()):
                 if resource.getname() == name:
@@ -447,7 +449,7 @@
             self.CustomDataTypeRange[name] = range
             base_type = basetype_content.baseType.getcontent()
             if base_type.__class__ == DefaultElementClass:
-                self.CustomTypeHierarchy[name] = basetype.getLocalTag()
+                self.CustomTypeHierarchy[name] = base_type.getLocalTag()
             else:
                 self.CustomTypeHierarchy[name] = base_type.getname()
         else:
@@ -880,7 +882,7 @@
         if location != "":
             var.setaddress(location)
         if description != "":
-            ft = PLCOpenParser.CreateElement("formattedText")
+            ft = PLCOpenParser.CreateElement("documentation", "variable")
             ft.setanyText(description)
             var.setdocumentation(ft)
         globalvars[-1].appendvariable(var)
@@ -1081,7 +1083,7 @@
         new_datatype = PLCOpenParser.CreateElement("dataType", "dataTypes")
         self.dataTypes.appenddataType(new_datatype)
         new_datatype.setname(name)
-        new_datatype.baseType.setcontent(PLCOpenParser.CreateElement("BOOL"))
+        new_datatype.baseType.setcontent(PLCOpenParser.CreateElement("BOOL", "dataType"))
     setattr(cls, "appenddataTypeElement", appenddataTypeElement)
     
     def insertdataTypeElement(self, index, dataType):
@@ -1183,9 +1185,9 @@
         search_result = []
         content_name = self.content.getLocalTag()
         if content_name in ["derived", "array", "enum", "subrangeSigned", "subrangeUnsigned"]:
-            search_result.extend(self.content["value"].Search(criteria, parent_infos))
+            search_result.extend(self.content.Search(criteria, parent_infos + ["base"]))
         elif content_name == "struct":
-            for i, element in enumerate(self.content["value"].getvariable()):
+            for i, element in enumerate(self.content.getvariable()):
                 search_result.extend(element.Search(criteria, parent_infos + ["struct", i]))
         else:
             if content_name in ["string", "wstring"]:
@@ -1194,6 +1196,17 @@
         return search_result
     setattr(cls, "Search", Search)
 
+cls = PLCOpenParser.GetElementClass("derived", "dataType")
+if cls:
+    def updateElementName(self, old_name, new_name):
+        if self.name == old_name:
+            self.name = new_name
+    setattr(cls, "updateElementName", updateElementName)
+    
+    def Search(self, criteria, parent_infos=[]):
+        return [(tuple(parent_infos),) + result for result in TestTextElement(self.name, criteria)]
+    setattr(cls, "Search", Search)
+
 cls = PLCOpenParser.GetElementClass("array", "dataType")
 if cls:
     setattr(cls, "updateElementName", _updateBaseTypeElementName)
@@ -1233,7 +1246,7 @@
     
     def Search(self, criteria, parent_infos=[]):
         search_result = []
-        for i, value in enumerate(self.values.getvalue()):
+        for i, value in enumerate(self.xpath("ppx:values/ppx:value", namespaces=PLCOpenParser.NSMAP)):
             for result in TestTextElement(value.getname(), criteria):
                 search_result.append((tuple(parent_infos + ["value", i]),) + result)
         return search_result
@@ -1245,7 +1258,7 @@
     def setdescription(self, description):
         doc = self.getdocumentation()
         if doc is None:
-            doc = PLCOpenParser.CreateElement("formattedText")
+            doc = PLCOpenParser.CreateElement("documentation", "pou")
             self.setdocumentation(doc)
         doc.setanyText(description)
     setattr(cls, "setdescription", setdescription)
@@ -1359,16 +1372,21 @@
         if self.interface is None:
             self.interface = PLCOpenParser.CreateElement("interface", "pou")
         content = self.interface.getcontent()
-        if len(content) == 0 or content[-1]["name"] != var_class:
-            self.appendcontent(PLCOpenParser.CreateElement(var_class, "interface"))
+        if len(content) == 0:
+            varlist = PLCOpenParser.CreateElement(var_class, "interface")
+            self.interface.setcontent([varlist])
+        elif content[-1] != var_class:
+            varlist = PLCOpenParser.CreateElement(var_class, "interface")
+            content[-1].addnext(varlist)
         else:
-            varlist = content[-1]["value"]
+            varlist = content[-1]
             variables = varlist.getvariable()
             if varlist.getconstant() or varlist.getretain() or len(variables) > 0 and variables[0].getaddress():
-                self.appendcontent(PLCOpenParser.CreateElement(var_class, "interface"))
+                varlist = PLCOpenParser.CreateElement(var_class, "interface")
+                content[-1].addnext(varlist)
         var = PLCOpenParser.CreateElement("variable", "varListPlain")
         var.setname(name)
-        var_type_obj = PLCOpenParser.CreateElement("dataType")
+        var_type_obj = PLCOpenParser.CreateElement("type", "variable")
         if var_type in [x for x,y in TypeHierarchy_list if not x.startswith("ANY")]:
             var_type_obj.setcontent(PLCOpenParser.CreateElement(
                 var_type.lower() if var_type in ["STRING", "WSTRING"]
@@ -1381,18 +1399,18 @@
         if location != "":
             var.setaddress(location)
         if description != "":
-            ft = PLCOpenParser.GetElementClass("formattedText")()
+            ft = PLCOpenParser.CreateElement("documentation", "variable")
             ft.setanyText(description)
             var.setdocumentation(ft)
         
-        content[-1]["value"].appendvariable(var)
+        varlist.appendvariable(var)
     setattr(cls, "addpouVar", addpouVar)
     
     def changepouVar(self, old_type, old_name, new_type, new_name):
         if self.interface is not None:
             content = self.interface.getcontent()
             for varlist in content:
-                variables = varlist["value"].getvariable()
+                variables = varlist.getvariable()
                 for var in variables:
                     if var.getname() == old_name:
                         vartype_content = var.gettype().getcontent()
@@ -1785,7 +1803,7 @@
     setattr(cls, "compileelementExecutionOrder", compileelementExecutionOrder)
     
     def setelementExecutionOrder(self, instance, new_executionOrder):
-        if self.contentLgetLocalTag() == "FBD":
+        if self.content.getLocalTag() == "FBD":
             old_executionOrder = instance.getexecutionOrderId()
             if old_executionOrder is not None and old_executionOrder != 0 and new_executionOrder != 0:
                 for element in self.content.getcontent():
@@ -2148,7 +2166,7 @@
         _getexecutionOrder(self, specific_values)
         specific_values["negated"] = self.getnegated()
         specific_values["edge"] = self.getedge()
-        if type == "coil":
+        if ld_element_type == "coil":
             specific_values["storage"] = self.getstorage()
         infos["inputs"].append(_getconnectioninfos(self, self.connectionPointIn, True))
         infos["outputs"].append(_getconnectioninfos(self, self.connectionPointOut))
@@ -2350,9 +2368,9 @@
         condition = self.getconditionContent()
         specific_values["condition_type"] = condition["type"]
         if specific_values["condition_type"] == "connection":
-            specific_values["connection"] = _getconnectioninfos(self, condition["value"], True)
+            specific_values["connection"] = _getconnectioninfos(self, condition, True)
         else:
-            specific_values["condition"] = condition["value"]
+            specific_values["condition"] = condition
         infos["inputs"].append(_getconnectioninfos(self, self.connectionPointIn, True))
         infos["outputs"].append(_getconnectioninfos(self, self.connectionPointOut))
         return infos
--- a/xmlclass/xmlclass.py	Thu Aug 29 00:28:39 2013 +0200
+++ b/xmlclass/xmlclass.py	Thu Aug 29 19:18:41 2013 +0200
@@ -536,17 +536,16 @@
 
 def GenerateAnyInfos(infos):
     
+    def GetTextElement(tree):
+        if infos["namespace"][0] == "##any":
+            return tree.xpath("p")[0]
+        return tree.xpath("ns:p", namespaces={"ns": infos["namespace"][0]})[0]
+    
     def ExtractAny(tree):
-        if infos["namespace"][0] == "##any":
-            return tree.xpath("p/text()")[0]
-        return tree.xpath("ns:p/text()", namespaces={"ns": infos["namespace"][0]})[0]
+        return GetTextElement(tree).text
     
     def GenerateAny(tree, value):
-        if infos["namespace"][0] == "##any":
-            p = tree.xpath("p")[0]
-        else:
-            p = tree.xpath("ns:p", namespaces={"ns": infos["namespace"][0]})[0]
-        p.text = etree.CDATA(value)
+        GetTextElement(tree).text = etree.CDATA(value)
         
     def InitialAny():
         if infos["namespace"][0] == "##any":
@@ -1395,7 +1394,9 @@
                 return attribute_infos["attr_type"]["extract"](value, extract=False)
             elif attribute_infos.has_key("fixed"):
                 return attribute_infos["attr_type"]["extract"](attribute_infos["fixed"], extract=False)
-            return attribute_infos["attr_type"]["initial"]()
+            elif attribute_infos.has_key("default"):
+                return attribute_infos["attr_type"]["extract"](attribute_infos["default"], extract=False)
+            return None
         
         elif elements.has_key(name):
             element_infos = elements[name]
@@ -1460,13 +1461,17 @@
                     self.remove(element)
                 
                 if value is not None:
-                    previous_elements_xpath = "|".join(map(
-                        lambda x: "%s:%s" % (factory.TargetNamespace, x)
-                                  if x != "content"
-                                  else elements["content"]["elmt_type"]["choices_xpath"](),
-                        elements.keys()[elements.keys().index(name)]))
-                    
-                    insertion_point = len(self.xpath(previous_elements_xpath, namespaces=factory.NSMAP))
+                    element_idx = elements.keys().index(name)
+                    if element_idx > 0:
+                        previous_elements_xpath = "|".join(map(
+                            lambda x: "%s:%s" % (factory.TargetNamespace, x)
+                                      if x != "content"
+                                      else elements["content"]["elmt_type"]["choices_xpath"](),
+                            elements.keys()[:element_idx]))
+                        
+                        insertion_point = len(self.xpath(previous_elements_xpath, namespaces=factory.NSMAP))
+                    else:
+                        insertion_point = 0
                     
                     if not isinstance(value, ListType):
                         value = [value]