plcopen/plcopen.py
changeset 225 7726c8ffda42
parent 200 8a1ed1959c69
child 238 389f2046e495
equal deleted inserted replaced
224:e5bf78b847e1 225:7726c8ffda42
    21 #You should have received a copy of the GNU General Public
    21 #You should have received a copy of the GNU General Public
    22 #License along with this library; if not, write to the Free Software
    22 #License along with this library; if not, write to the Free Software
    23 #Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
    23 #Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
    24 
    24 
    25 from xmlclass import *
    25 from xmlclass import *
       
    26 from structures import *
    26 from types import *
    27 from types import *
    27 import os, re
    28 import os, re
    28 
    29 
    29 """
    30 """
    30 Dictionary that makes the relation between var names in plcopen and displayed values
    31 Dictionary that makes the relation between var names in plcopen and displayed values
    61     setattr(cls, "updateElementName", updateElementName)
    62     setattr(cls, "updateElementName", updateElementName)
    62     
    63     
    63 cls = PLCOpenClasses.get("project", None)
    64 cls = PLCOpenClasses.get("project", None)
    64 if cls:
    65 if cls:
    65     cls.singleLineAttributes = False
    66     cls.singleLineAttributes = False
       
    67     cls.EnumeratedDataTypeValues = {}
       
    68     cls.CustomDataTypeRange = {}
       
    69     cls.CustomTypeHierarchy = {}
       
    70     cls.ElementUsingTree = {}
       
    71     cls.CustomBlockTypes = []
    66     
    72     
    67     def setname(self, name):
    73     def setname(self, name):
    68         self.contentHeader.setname(name)
    74         self.contentHeader.setname(name)
    69     setattr(cls, "setname", setname)
    75     setattr(cls, "setname", setname)
    70         
    76         
   141     def getdataType(self, name):
   147     def getdataType(self, name):
   142         return self.types.getdataTypeElement(name)
   148         return self.types.getdataTypeElement(name)
   143     setattr(cls, "getdataType", getdataType)
   149     setattr(cls, "getdataType", getdataType)
   144     
   150     
   145     def appenddataType(self, name):
   151     def appenddataType(self, name):
       
   152         if name in self.CustomTypeHierarchy:
       
   153             raise ValueError, "\"%s\" Data Type already exists !!!"%name
   146         self.types.appenddataTypeElement(name)
   154         self.types.appenddataTypeElement(name)
       
   155         self.AddCustomDataType(self.getdataType(name))
   147     setattr(cls, "appenddataType", appenddataType)
   156     setattr(cls, "appenddataType", appenddataType)
   148         
   157         
   149     def insertdataType(self, index, datatype):
   158     def insertdataType(self, index, datatype):
   150         self.types.insertdataTypeElement(index, datatype)
   159         self.types.insertdataTypeElement(index, datatype)
       
   160         self.AddCustomDataType(datatype)
   151     setattr(cls, "insertdataType", insertdataType)
   161     setattr(cls, "insertdataType", insertdataType)
   152     
   162     
   153     def removedataType(self, name):
   163     def removedataType(self, name):
   154         self.types.removedataTypeElement(name)
   164         self.types.removedataTypeElement(name)
       
   165         self.RefreshDataTypeHierarchy()
       
   166         self.RefreshElementUsingTree()
   155     setattr(cls, "removedataType", removedataType)
   167     setattr(cls, "removedataType", removedataType)
   156     
   168     
   157     def getpous(self):
   169     def getpous(self):
   158         return self.types.getpouElements()
   170         return self.types.getpouElements()
   159     setattr(cls, "getpous", getpous)
   171     setattr(cls, "getpous", getpous)
   162         return self.types.getpouElement(name)
   174         return self.types.getpouElement(name)
   163     setattr(cls, "getpou", getpou)
   175     setattr(cls, "getpou", getpou)
   164     
   176     
   165     def appendpou(self, name, pou_type, body_type):
   177     def appendpou(self, name, pou_type, body_type):
   166         self.types.appendpouElement(name, pou_type, body_type)
   178         self.types.appendpouElement(name, pou_type, body_type)
       
   179         self.AddCustomBlockType(self.getpou(name))
   167     setattr(cls, "appendpou", appendpou)
   180     setattr(cls, "appendpou", appendpou)
   168         
   181         
   169     def insertpou(self, index, pou):
   182     def insertpou(self, index, pou):
   170         self.types.insertpouElement(index, pou)
   183         self.types.insertpouElement(index, pou)
       
   184         self.AddCustomBlockType(pou)
   171     setattr(cls, "insertpou", insertpou)
   185     setattr(cls, "insertpou", insertpou)
   172     
   186     
   173     def removepou(self, name):
   187     def removepou(self, name):
   174         self.types.removepouElement(name)
   188         self.types.removepouElement(name)
       
   189         self.RefreshCustomBlockTypes()
       
   190         self.RefreshElementUsingTree()
   175     setattr(cls, "removepou", removepou)
   191     setattr(cls, "removepou", removepou)
   176 
   192 
   177     def getconfigurations(self):
   193     def getconfigurations(self):
   178         configurations = self.instances.configurations.getconfiguration()
   194         configurations = self.instances.configurations.getconfiguration()
   179         if configurations:
   195         if configurations:
   246             pou.updateElementName(old_name, new_name)
   262             pou.updateElementName(old_name, new_name)
   247         for configuration in self.instances.configurations.getconfiguration():
   263         for configuration in self.instances.configurations.getconfiguration():
   248             configuration.updateElementName(old_name, new_name)
   264             configuration.updateElementName(old_name, new_name)
   249     setattr(cls, "updateElementName", updateElementName)
   265     setattr(cls, "updateElementName", updateElementName)
   250 
   266 
       
   267     def RefreshDataTypeHierarchy(self):
       
   268         self.EnumeratedDataTypeValues = {}
       
   269         self.CustomDataTypeRange = {}
       
   270         self.CustomTypeHierarchy = {}
       
   271         for datatype in self.getdataTypes():
       
   272             self.AddCustomDataType(datatype)
       
   273     setattr(cls, "RefreshDataTypeHierarchy", RefreshDataTypeHierarchy)
       
   274 
       
   275     def AddCustomDataType(self, datatype):
       
   276         name = datatype.getname()
       
   277         basetype_content = datatype.getbaseType().getcontent()
       
   278         if basetype_content["value"] is None:
       
   279             self.CustomTypeHierarchy[name] = basetype_content["name"]
       
   280         elif basetype_content["name"] in ["string", "wstring"]:
       
   281             self.CustomTypeHierarchy[name] = basetype_content["name"].upper()
       
   282         elif basetype_content["name"] == "derived":
       
   283             self.CustomTypeHierarchy[name] = basetype_content["value"].getname()
       
   284         elif basetype_content["name"] in ["subrangeSigned", "subrangeUnsigned"]:
       
   285             range = (basetype_content["value"].range.getlower(), 
       
   286                      basetype_content["value"].range.getupper())
       
   287             self.CustomDataTypeRange[name] = range
       
   288             base_type = basetype_content["value"].baseType.getcontent()
       
   289             if base_type["value"] is None:
       
   290                 self.CustomTypeHierarchy[name] = base_type["name"]
       
   291             else:
       
   292                 self.CustomTypeHierarchy[name] = base_type["value"].getname()
       
   293         else:
       
   294             if basetype_content["name"] == "enum":
       
   295                 values = []
       
   296                 for value in basetype_content["value"].values.getvalue():
       
   297                     values.append(value.getname())
       
   298                 self.EnumeratedDataTypeValues[name] = values
       
   299             self.CustomTypeHierarchy[name] = "ANY_DERIVED"
       
   300     setattr(cls, "AddCustomDataType", AddCustomDataType)
       
   301 
       
   302     # Update Block types with user-defined pou added
       
   303     def RefreshCustomBlockTypes(self):
       
   304         # Reset the tree of user-defined pou cross-use
       
   305         self.CustomBlockTypes = []
       
   306         for pou in self.getpous():
       
   307             self.AddCustomBlockType(pou)
       
   308     setattr(cls, "RefreshCustomBlockTypes", RefreshCustomBlockTypes)
       
   309 
       
   310     def AddCustomBlockType(self, pou): 
       
   311         pou_name = pou.getname()
       
   312         pou_type = pou.getpouType()
       
   313         if pou_type != "program":
       
   314             block_infos = {"name" : pou_name, "type" : pou_type, "extensible" : False,
       
   315                            "inputs" : [], "outputs" : [], "comment" : "",
       
   316                            "generate" : generate_block, "initialise" : initialise_block }
       
   317             if pou.getinterface():
       
   318                 for type, varlist in pou.getvars():
       
   319                     if type == "InOut":
       
   320                         for var in varlist.getvariable():
       
   321                             var_type = var.type.getcontent()
       
   322                             if var_type["name"] == "derived":
       
   323                                 block_infos["inputs"].append((var.getname(), var_type["value"].getname(), "none"))
       
   324                                 block_infos["outputs"].append((var.getname(), var_type["value"].getname(), "none"))
       
   325                             elif var_type["name"] in ["string", "wstring"]:
       
   326                                 block_infos["inputs"].append((var.getname(), var_type["name"].upper(), "none"))
       
   327                                 block_infos["outputs"].append((var.getname(), var_type["name"].upper(), "none"))
       
   328                             else:
       
   329                                 block_infos["inputs"].append((var.getname(), var_type["name"], "none"))
       
   330                                 block_infos["outputs"].append((var.getname(), var_type["name"], "none"))
       
   331                     elif type == "Input":
       
   332                         for var in varlist.getvariable():
       
   333                             var_type = var.type.getcontent()
       
   334                             if var_type["name"] == "derived":
       
   335                                 block_infos["inputs"].append((var.getname(), var_type["value"].getname(), "none"))
       
   336                             elif var_type["name"] in ["string", "wstring"]:
       
   337                                 block_infos["inputs"].append((var.getname(), var_type["name"].upper(), "none"))
       
   338                             else:
       
   339                                 block_infos["inputs"].append((var.getname(), var_type["name"], "none"))
       
   340                     elif type == "Output":
       
   341                         for var in varlist.getvariable():
       
   342                             var_type = var.type.getcontent()
       
   343                             if var_type["name"] == "derived":
       
   344                                 block_infos["outputs"].append((var.getname(), var_type["value"].getname(), "none"))
       
   345                             elif var_type["name"] in ["string", "wstring"]:
       
   346                                 block_infos["outputs"].append((var.getname(), var_type["name"].upper(), "none"))
       
   347                             else:
       
   348                                 block_infos["outputs"].append((var.getname(), var_type["name"], "none"))    
       
   349                 return_type = pou.interface.getreturnType()
       
   350                 if return_type:
       
   351                     var_type = return_type.getcontent()
       
   352                     if var_type["name"] == "derived":
       
   353                         block_infos["outputs"].append(("", var_type["value"].getname(), "none"))
       
   354                     elif var_type["name"] in ["string", "wstring"]:
       
   355                         block_infos["outputs"].append(("", var_type["name"].upper(), "none"))
       
   356                     else:
       
   357                         block_infos["outputs"].append(("", var_type["name"], "none"))
       
   358             if pou.getbodyType() in ["FBD","LD","SFC"]:
       
   359                 for instance in pou.getinstances():
       
   360                     if isinstance(instance, PLCOpenClasses.get("commonObjects_comment", None)):
       
   361                         block_infos["comment"] = instance.getcontentText()
       
   362             self.CustomBlockTypes.append(block_infos)
       
   363     setattr(cls, "AddCustomBlockType", AddCustomBlockType)
       
   364 
       
   365     def RefreshElementUsingTree(self):
       
   366         # Reset the tree of user-defined element cross-use
       
   367         self.ElementUsingTree = {}
       
   368         pous = self.getpous()
       
   369         # Reference all the user-defined elementu names and initialize the tree of 
       
   370         # user-defined elemnt cross-use
       
   371         pounames = [pou.getname() for pou in pous]
       
   372         for name in pounames:
       
   373             self.ElementUsingTree[name] = []
       
   374         # Analyze each pou
       
   375         for pou in pous:
       
   376             name = pou.getname()
       
   377             if pou.interface:
       
   378                 # Extract variables from every varLists
       
   379                 for type, varlist in pou.getvars():
       
   380                     for var in varlist.getvariable():
       
   381                         vartype_content = var.gettype().getcontent()
       
   382                         if vartype_content["name"] == "derived":
       
   383                             typename = vartype_content["value"].getname()
       
   384                             if typename in pounames and name not in self.ElementUsingTree[typename]:
       
   385                                 self.ElementUsingTree[typename].append(name)
       
   386     setattr(cls, "RefreshElementUsingTree", RefreshElementUsingTree)
       
   387 
       
   388     def GetParentType(self, type):
       
   389         if type in self.CustomTypeHierarchy:
       
   390             return self.CustomTypeHierarchy[type]
       
   391         elif type in TypeHierarchy:
       
   392             return TypeHierarchy[type]
       
   393         return None
       
   394     setattr(cls, "GetParentType", GetParentType)
       
   395 
       
   396     def GetBaseType(self, type):
       
   397         parent_type = self.GetParentType(type)
       
   398         if parent_type is not None:
       
   399             if parent_type.startswith("ANY"):
       
   400                 return type
       
   401             else:
       
   402                 return self.GetBaseType(parent_type)
       
   403         return None
       
   404     setattr(cls, "GetBaseType", GetBaseType)
       
   405 
       
   406     def GetSubrangeBaseTypes(self, exclude):
       
   407         derived = []
       
   408         for type in self.CustomTypeHierarchy.keys():
       
   409             for base_type in DataTypeRange.keys():
       
   410                 if self.IsOfType(type, base_type) and not self.IsOfType(type, exclude):
       
   411                     derived.append(type)
       
   412                     break
       
   413         return DataTypeRange.keys() + derived
       
   414     setattr(cls, "GetSubrangeBaseTypes", GetSubrangeBaseTypes)
       
   415 
       
   416     """
       
   417     returns true if the given data type is the same that "reference" meta-type or one of its types.
       
   418     """
       
   419     def IsOfType(self, type, reference):
       
   420         if reference is None:
       
   421             return True
       
   422         elif type == reference:
       
   423             return True
       
   424         else:
       
   425             parent_type = self.GetParentType(type)
       
   426             if parent_type is not None:
       
   427                 return self.IsOfType(parent_type, reference)
       
   428         return False
       
   429     setattr(cls, "IsOfType", IsOfType)
       
   430 
       
   431     # Return if pou given by name is used by another pou
       
   432     def ElementIsUsed(self, name):
       
   433         if name in self.ElementUsingTree:
       
   434             return len(self.ElementUsingTree[name]) > 0
       
   435         return False
       
   436     setattr(cls, "ElementIsUsed", ElementIsUsed)
       
   437 
       
   438     def DataTypeIsDerived(self, name):
       
   439         return name in self.CustomTypeHierarchy.values()
       
   440     setattr(cls, "DataTypeIsDerived", DataTypeIsDerived)
       
   441 
       
   442     # Return if pou given by name is directly or undirectly used by the reference pou
       
   443     def ElementIsUsedBy(self, name, reference):
       
   444         if name in self.ElementUsingTree:
       
   445             list = self.ElementUsingTree[name]
       
   446             # Test if pou is directly used by reference
       
   447             if reference in list:
       
   448                 return True
       
   449             else:
       
   450                 # Test if pou is undirectly used by reference, by testing if pous 
       
   451                 # that directly use pou is directly or undirectly used by reference
       
   452                 used = False
       
   453                 for element in list:
       
   454                     used |= self.ElementIsUsedBy(element, reference)
       
   455                 return used
       
   456         return False
       
   457     setattr(cls, "ElementIsUsedBy", ElementIsUsedBy)
       
   458 
       
   459     def GetDataTypeRange(self, type):
       
   460         if type in self.CustomDataTypeRange:
       
   461             return self.CustomDataTypeRange[type]
       
   462         elif type in DataTypeRange:
       
   463             return DataTypeRange[type]
       
   464         else:
       
   465             parent_type = self.GetParentType(type)
       
   466             if parent_type is not None:
       
   467                 return self.GetDataTypeRange(parent_type)
       
   468         return None
       
   469     setattr(cls, "GetDataTypeRange", GetDataTypeRange)
       
   470 
       
   471     def GetEnumeratedDataTypeValues(self, type = None):
       
   472         if type is None:
       
   473             all_values = []
       
   474             for values in self.EnumeratedDataTypeValues.values():
       
   475                 all_values.extend(values)
       
   476             return all_values
       
   477         elif type in self.EnumeratedDataTypeValues:
       
   478             return self.EnumeratedDataTypeValues(type)
       
   479         return []
       
   480     setattr(cls, "GetEnumeratedDataTypeValues", GetEnumeratedDataTypeValues)
       
   481 
       
   482     # Function that returns the block definition associated to the block type given
       
   483     def GetCustomBlockType(self, type, inputs = None):
       
   484         for customblocktype in self.CustomBlockTypes:
       
   485             if inputs:
       
   486                 customblock_inputs = tuple([var_type for name, var_type, modifier in customblocktype["inputs"]])
       
   487                 same_inputs = inputs == customblock_inputs
       
   488             else:
       
   489                 same_inputs = True
       
   490             if customblocktype["name"] == type and same_inputs:
       
   491                 return customblocktype
       
   492         return None
       
   493     setattr(cls, "GetCustomBlockType", GetCustomBlockType)
       
   494 
       
   495     # Return Block types checking for recursion
       
   496     def GetCustomBlockTypes(self, exclude = ""):
       
   497         pou = self.getpou(exclude)
       
   498         type = pou.getpouType()
       
   499         customblocktypes = []
       
   500         for customblocktype in self.CustomBlockTypes:
       
   501             if customblocktype["name"] != exclude and not self.ElementIsUsedBy(exclude, customblocktype["name"]) and not (type == "function" and customblocktype["type"] != "function"):
       
   502                 customblocktypes.append(customblocktype)
       
   503         return customblocktypes
       
   504     setattr(cls, "GetCustomBlockTypes", GetCustomBlockTypes)
       
   505 
       
   506     # Return Function Block types checking for recursion
       
   507     def GetCustomFunctionBlockTypes(self, exclude = ""):
       
   508         pou = self.getpou(exclude)
       
   509         type = pou.getpouType()
       
   510         customblocktypes = []
       
   511         for customblocktype in self.CustomBlockTypes:
       
   512             if customblocktype["name"] != exclude and not self.ElementIsUsedBy(exclude, customblocktype["name"]) and not (type == "function" and customblocktype["type"] != "function"):
       
   513                 customblocktypes.appendcustom(customblocktype["name"])
       
   514         return customblocktypes
       
   515     setattr(cls, "GetCustomFunctionBlockTypes", GetCustomFunctionBlockTypes)
       
   516 
       
   517     # Return Block types checking for recursion
       
   518     def GetCustomBlockResource(self):
       
   519         customblocktypes = []
       
   520         for customblocktype in self.CustomBlockTypes:
       
   521             if customblocktype["type"] == "program":
       
   522                 customblocktypes.append(customblocktype["name"])
       
   523         return customblocktypes
       
   524     setattr(cls, "GetCustomBlockResource", GetCustomBlockResource)
       
   525 
       
   526     # Return Data Types checking for recursion
       
   527     def GetCustomDataTypes(self, exclude = ""):
       
   528         customdatatypes = []
       
   529         for customdatatype in self.getdataTypes():
       
   530             customdatatype_name = customdatatype.getname()
       
   531             if customdatatype_name != exclude and not self.ElementIsUsedBy(exclude, customdatatype_name):
       
   532                 customdatatypes.append(customdatatype_name)
       
   533         return customdatatypes
       
   534     setattr(cls, "GetCustomDataTypes", GetCustomDataTypes)
       
   535 
   251 cls = PLCOpenClasses.get("project_fileHeader", None)
   536 cls = PLCOpenClasses.get("project_fileHeader", None)
   252 if cls:
   537 if cls:
   253     cls.singleLineAttributes = False
   538     cls.singleLineAttributes = False
   254 
   539 
   255 cls = PLCOpenClasses.get("project_contentHeader", None)
   540 cls = PLCOpenClasses.get("project_contentHeader", None)
   363                 return element
   648                 return element
   364         return None
   649         return None
   365     setattr(cls, "getdataTypeElement", getdataTypeElement)
   650     setattr(cls, "getdataTypeElement", getdataTypeElement)
   366 
   651 
   367     def appenddataTypeElement(self, name):
   652     def appenddataTypeElement(self, name):
   368         for element in self.dataTypes.getdataType():
       
   369             if element.getname() == name:
       
   370                 raise ValueError, "\"%s\" Data Type already exists !!!"%name
       
   371         new_datatype = PLCOpenClasses["dataTypes_dataType"]()
   653         new_datatype = PLCOpenClasses["dataTypes_dataType"]()
   372         new_datatype.setname(name)
   654         new_datatype.setname(name)
   373         new_datatype.baseType.setcontent({"name" : "BOOL", "value" : None})
   655         new_datatype.baseType.setcontent({"name" : "BOOL", "value" : None})
   374         self.dataTypes.appenddataType(new_datatype)
   656         self.dataTypes.appenddataType(new_datatype)
   375     setattr(cls, "appenddataTypeElement", appenddataTypeElement)
   657     setattr(cls, "appenddataTypeElement", appenddataTypeElement)
   376         
   658     
   377     def insertdataTypeElement(self, index, dataType):
   659     def insertdataTypeElement(self, index, dataType):
   378         self.dataTypes.insertdataType(index, dataType)
   660         self.dataTypes.insertdataType(index, dataType)
   379     setattr(cls, "insertdataTypeElement", insertdataTypeElement)
   661     setattr(cls, "insertdataTypeElement", insertdataTypeElement)
   380     
   662     
   381     def removedataTypeElement(self, name):
   663     def removedataTypeElement(self, name):