plcopen/plcopen.py
changeset 225 7726c8ffda42
parent 200 8a1ed1959c69
child 238 389f2046e495
--- a/plcopen/plcopen.py	Fri Jul 18 15:44:37 2008 +0200
+++ b/plcopen/plcopen.py	Tue Aug 12 16:28:55 2008 +0200
@@ -23,6 +23,7 @@
 #Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 
 from xmlclass import *
+from structures import *
 from types import *
 import os, re
 
@@ -63,6 +64,11 @@
 cls = PLCOpenClasses.get("project", None)
 if cls:
     cls.singleLineAttributes = False
+    cls.EnumeratedDataTypeValues = {}
+    cls.CustomDataTypeRange = {}
+    cls.CustomTypeHierarchy = {}
+    cls.ElementUsingTree = {}
+    cls.CustomBlockTypes = []
     
     def setname(self, name):
         self.contentHeader.setname(name)
@@ -143,15 +149,21 @@
     setattr(cls, "getdataType", getdataType)
     
     def appenddataType(self, name):
+        if name in self.CustomTypeHierarchy:
+            raise ValueError, "\"%s\" Data Type already exists !!!"%name
         self.types.appenddataTypeElement(name)
+        self.AddCustomDataType(self.getdataType(name))
     setattr(cls, "appenddataType", appenddataType)
         
     def insertdataType(self, index, datatype):
         self.types.insertdataTypeElement(index, datatype)
+        self.AddCustomDataType(datatype)
     setattr(cls, "insertdataType", insertdataType)
     
     def removedataType(self, name):
         self.types.removedataTypeElement(name)
+        self.RefreshDataTypeHierarchy()
+        self.RefreshElementUsingTree()
     setattr(cls, "removedataType", removedataType)
     
     def getpous(self):
@@ -164,14 +176,18 @@
     
     def appendpou(self, name, pou_type, body_type):
         self.types.appendpouElement(name, pou_type, body_type)
+        self.AddCustomBlockType(self.getpou(name))
     setattr(cls, "appendpou", appendpou)
         
     def insertpou(self, index, pou):
         self.types.insertpouElement(index, pou)
+        self.AddCustomBlockType(pou)
     setattr(cls, "insertpou", insertpou)
     
     def removepou(self, name):
         self.types.removepouElement(name)
+        self.RefreshCustomBlockTypes()
+        self.RefreshElementUsingTree()
     setattr(cls, "removepou", removepou)
 
     def getconfigurations(self):
@@ -248,6 +264,275 @@
             configuration.updateElementName(old_name, new_name)
     setattr(cls, "updateElementName", updateElementName)
 
+    def RefreshDataTypeHierarchy(self):
+        self.EnumeratedDataTypeValues = {}
+        self.CustomDataTypeRange = {}
+        self.CustomTypeHierarchy = {}
+        for datatype in self.getdataTypes():
+            self.AddCustomDataType(datatype)
+    setattr(cls, "RefreshDataTypeHierarchy", RefreshDataTypeHierarchy)
+
+    def AddCustomDataType(self, datatype):
+        name = datatype.getname()
+        basetype_content = datatype.getbaseType().getcontent()
+        if basetype_content["value"] is None:
+            self.CustomTypeHierarchy[name] = basetype_content["name"]
+        elif basetype_content["name"] in ["string", "wstring"]:
+            self.CustomTypeHierarchy[name] = basetype_content["name"].upper()
+        elif basetype_content["name"] == "derived":
+            self.CustomTypeHierarchy[name] = basetype_content["value"].getname()
+        elif basetype_content["name"] in ["subrangeSigned", "subrangeUnsigned"]:
+            range = (basetype_content["value"].range.getlower(), 
+                     basetype_content["value"].range.getupper())
+            self.CustomDataTypeRange[name] = range
+            base_type = basetype_content["value"].baseType.getcontent()
+            if base_type["value"] is None:
+                self.CustomTypeHierarchy[name] = base_type["name"]
+            else:
+                self.CustomTypeHierarchy[name] = base_type["value"].getname()
+        else:
+            if basetype_content["name"] == "enum":
+                values = []
+                for value in basetype_content["value"].values.getvalue():
+                    values.append(value.getname())
+                self.EnumeratedDataTypeValues[name] = values
+            self.CustomTypeHierarchy[name] = "ANY_DERIVED"
+    setattr(cls, "AddCustomDataType", AddCustomDataType)
+
+    # Update Block types with user-defined pou added
+    def RefreshCustomBlockTypes(self):
+        # Reset the tree of user-defined pou cross-use
+        self.CustomBlockTypes = []
+        for pou in self.getpous():
+            self.AddCustomBlockType(pou)
+    setattr(cls, "RefreshCustomBlockTypes", RefreshCustomBlockTypes)
+
+    def AddCustomBlockType(self, pou): 
+        pou_name = pou.getname()
+        pou_type = pou.getpouType()
+        if pou_type != "program":
+            block_infos = {"name" : pou_name, "type" : pou_type, "extensible" : False,
+                           "inputs" : [], "outputs" : [], "comment" : "",
+                           "generate" : generate_block, "initialise" : initialise_block }
+            if pou.getinterface():
+                for type, varlist in pou.getvars():
+                    if type == "InOut":
+                        for var in varlist.getvariable():
+                            var_type = var.type.getcontent()
+                            if var_type["name"] == "derived":
+                                block_infos["inputs"].append((var.getname(), var_type["value"].getname(), "none"))
+                                block_infos["outputs"].append((var.getname(), var_type["value"].getname(), "none"))
+                            elif var_type["name"] in ["string", "wstring"]:
+                                block_infos["inputs"].append((var.getname(), var_type["name"].upper(), "none"))
+                                block_infos["outputs"].append((var.getname(), var_type["name"].upper(), "none"))
+                            else:
+                                block_infos["inputs"].append((var.getname(), var_type["name"], "none"))
+                                block_infos["outputs"].append((var.getname(), var_type["name"], "none"))
+                    elif type == "Input":
+                        for var in varlist.getvariable():
+                            var_type = var.type.getcontent()
+                            if var_type["name"] == "derived":
+                                block_infos["inputs"].append((var.getname(), var_type["value"].getname(), "none"))
+                            elif var_type["name"] in ["string", "wstring"]:
+                                block_infos["inputs"].append((var.getname(), var_type["name"].upper(), "none"))
+                            else:
+                                block_infos["inputs"].append((var.getname(), var_type["name"], "none"))
+                    elif type == "Output":
+                        for var in varlist.getvariable():
+                            var_type = var.type.getcontent()
+                            if var_type["name"] == "derived":
+                                block_infos["outputs"].append((var.getname(), var_type["value"].getname(), "none"))
+                            elif var_type["name"] in ["string", "wstring"]:
+                                block_infos["outputs"].append((var.getname(), var_type["name"].upper(), "none"))
+                            else:
+                                block_infos["outputs"].append((var.getname(), var_type["name"], "none"))    
+                return_type = pou.interface.getreturnType()
+                if return_type:
+                    var_type = return_type.getcontent()
+                    if var_type["name"] == "derived":
+                        block_infos["outputs"].append(("", var_type["value"].getname(), "none"))
+                    elif var_type["name"] in ["string", "wstring"]:
+                        block_infos["outputs"].append(("", var_type["name"].upper(), "none"))
+                    else:
+                        block_infos["outputs"].append(("", var_type["name"], "none"))
+            if pou.getbodyType() in ["FBD","LD","SFC"]:
+                for instance in pou.getinstances():
+                    if isinstance(instance, PLCOpenClasses.get("commonObjects_comment", None)):
+                        block_infos["comment"] = instance.getcontentText()
+            self.CustomBlockTypes.append(block_infos)
+    setattr(cls, "AddCustomBlockType", AddCustomBlockType)
+
+    def RefreshElementUsingTree(self):
+        # Reset the tree of user-defined element cross-use
+        self.ElementUsingTree = {}
+        pous = self.getpous()
+        # Reference all the user-defined elementu names and initialize the tree of 
+        # user-defined elemnt cross-use
+        pounames = [pou.getname() for pou in pous]
+        for name in pounames:
+            self.ElementUsingTree[name] = []
+        # Analyze each pou
+        for pou in pous:
+            name = pou.getname()
+            if pou.interface:
+                # Extract variables from every varLists
+                for type, varlist in pou.getvars():
+                    for var in varlist.getvariable():
+                        vartype_content = var.gettype().getcontent()
+                        if vartype_content["name"] == "derived":
+                            typename = vartype_content["value"].getname()
+                            if typename in pounames and name not in self.ElementUsingTree[typename]:
+                                self.ElementUsingTree[typename].append(name)
+    setattr(cls, "RefreshElementUsingTree", RefreshElementUsingTree)
+
+    def GetParentType(self, type):
+        if type in self.CustomTypeHierarchy:
+            return self.CustomTypeHierarchy[type]
+        elif type in TypeHierarchy:
+            return TypeHierarchy[type]
+        return None
+    setattr(cls, "GetParentType", GetParentType)
+
+    def GetBaseType(self, type):
+        parent_type = self.GetParentType(type)
+        if parent_type is not None:
+            if parent_type.startswith("ANY"):
+                return type
+            else:
+                return self.GetBaseType(parent_type)
+        return None
+    setattr(cls, "GetBaseType", GetBaseType)
+
+    def GetSubrangeBaseTypes(self, exclude):
+        derived = []
+        for type in self.CustomTypeHierarchy.keys():
+            for base_type in DataTypeRange.keys():
+                if self.IsOfType(type, base_type) and not self.IsOfType(type, exclude):
+                    derived.append(type)
+                    break
+        return DataTypeRange.keys() + derived
+    setattr(cls, "GetSubrangeBaseTypes", GetSubrangeBaseTypes)
+
+    """
+    returns true if the given data type is the same that "reference" meta-type or one of its types.
+    """
+    def IsOfType(self, type, reference):
+        if reference is None:
+            return True
+        elif type == reference:
+            return True
+        else:
+            parent_type = self.GetParentType(type)
+            if parent_type is not None:
+                return self.IsOfType(parent_type, reference)
+        return False
+    setattr(cls, "IsOfType", IsOfType)
+
+    # Return if pou given by name is used by another pou
+    def ElementIsUsed(self, name):
+        if name in self.ElementUsingTree:
+            return len(self.ElementUsingTree[name]) > 0
+        return False
+    setattr(cls, "ElementIsUsed", ElementIsUsed)
+
+    def DataTypeIsDerived(self, name):
+        return name in self.CustomTypeHierarchy.values()
+    setattr(cls, "DataTypeIsDerived", DataTypeIsDerived)
+
+    # Return if pou given by name is directly or undirectly used by the reference pou
+    def ElementIsUsedBy(self, name, reference):
+        if name in self.ElementUsingTree:
+            list = self.ElementUsingTree[name]
+            # Test if pou is directly used by reference
+            if reference in list:
+                return True
+            else:
+                # Test if pou is undirectly used by reference, by testing if pous 
+                # that directly use pou is directly or undirectly used by reference
+                used = False
+                for element in list:
+                    used |= self.ElementIsUsedBy(element, reference)
+                return used
+        return False
+    setattr(cls, "ElementIsUsedBy", ElementIsUsedBy)
+
+    def GetDataTypeRange(self, type):
+        if type in self.CustomDataTypeRange:
+            return self.CustomDataTypeRange[type]
+        elif type in DataTypeRange:
+            return DataTypeRange[type]
+        else:
+            parent_type = self.GetParentType(type)
+            if parent_type is not None:
+                return self.GetDataTypeRange(parent_type)
+        return None
+    setattr(cls, "GetDataTypeRange", GetDataTypeRange)
+
+    def GetEnumeratedDataTypeValues(self, type = None):
+        if type is None:
+            all_values = []
+            for values in self.EnumeratedDataTypeValues.values():
+                all_values.extend(values)
+            return all_values
+        elif type in self.EnumeratedDataTypeValues:
+            return self.EnumeratedDataTypeValues(type)
+        return []
+    setattr(cls, "GetEnumeratedDataTypeValues", GetEnumeratedDataTypeValues)
+
+    # Function that returns the block definition associated to the block type given
+    def GetCustomBlockType(self, type, inputs = None):
+        for customblocktype in self.CustomBlockTypes:
+            if inputs:
+                customblock_inputs = tuple([var_type for name, var_type, modifier in customblocktype["inputs"]])
+                same_inputs = inputs == customblock_inputs
+            else:
+                same_inputs = True
+            if customblocktype["name"] == type and same_inputs:
+                return customblocktype
+        return None
+    setattr(cls, "GetCustomBlockType", GetCustomBlockType)
+
+    # Return Block types checking for recursion
+    def GetCustomBlockTypes(self, exclude = ""):
+        pou = self.getpou(exclude)
+        type = pou.getpouType()
+        customblocktypes = []
+        for customblocktype in self.CustomBlockTypes:
+            if customblocktype["name"] != exclude and not self.ElementIsUsedBy(exclude, customblocktype["name"]) and not (type == "function" and customblocktype["type"] != "function"):
+                customblocktypes.append(customblocktype)
+        return customblocktypes
+    setattr(cls, "GetCustomBlockTypes", GetCustomBlockTypes)
+
+    # Return Function Block types checking for recursion
+    def GetCustomFunctionBlockTypes(self, exclude = ""):
+        pou = self.getpou(exclude)
+        type = pou.getpouType()
+        customblocktypes = []
+        for customblocktype in self.CustomBlockTypes:
+            if customblocktype["name"] != exclude and not self.ElementIsUsedBy(exclude, customblocktype["name"]) and not (type == "function" and customblocktype["type"] != "function"):
+                customblocktypes.appendcustom(customblocktype["name"])
+        return customblocktypes
+    setattr(cls, "GetCustomFunctionBlockTypes", GetCustomFunctionBlockTypes)
+
+    # Return Block types checking for recursion
+    def GetCustomBlockResource(self):
+        customblocktypes = []
+        for customblocktype in self.CustomBlockTypes:
+            if customblocktype["type"] == "program":
+                customblocktypes.append(customblocktype["name"])
+        return customblocktypes
+    setattr(cls, "GetCustomBlockResource", GetCustomBlockResource)
+
+    # Return Data Types checking for recursion
+    def GetCustomDataTypes(self, exclude = ""):
+        customdatatypes = []
+        for customdatatype in self.getdataTypes():
+            customdatatype_name = customdatatype.getname()
+            if customdatatype_name != exclude and not self.ElementIsUsedBy(exclude, customdatatype_name):
+                customdatatypes.append(customdatatype_name)
+        return customdatatypes
+    setattr(cls, "GetCustomDataTypes", GetCustomDataTypes)
+
 cls = PLCOpenClasses.get("project_fileHeader", None)
 if cls:
     cls.singleLineAttributes = False
@@ -365,15 +650,12 @@
     setattr(cls, "getdataTypeElement", getdataTypeElement)
 
     def appenddataTypeElement(self, name):
-        for element in self.dataTypes.getdataType():
-            if element.getname() == name:
-                raise ValueError, "\"%s\" Data Type already exists !!!"%name
         new_datatype = PLCOpenClasses["dataTypes_dataType"]()
         new_datatype.setname(name)
         new_datatype.baseType.setcontent({"name" : "BOOL", "value" : None})
         self.dataTypes.appenddataType(new_datatype)
     setattr(cls, "appenddataTypeElement", appenddataTypeElement)
-        
+    
     def insertdataTypeElement(self, index, dataType):
         self.dataTypes.insertdataType(index, dataType)
     setattr(cls, "insertdataTypeElement", insertdataTypeElement)