diff -r 635d0817508c -r 394d9f168258 PLCControler.py --- a/PLCControler.py Tue Nov 27 12:58:34 2007 +0100 +++ b/PLCControler.py Thu Dec 06 18:05:29 2007 +0100 @@ -37,7 +37,10 @@ duration_model = re.compile("(?:([0-9]{1,2})h)?(?:([0-9]{1,2})m(?!s))?(?:([0-9]{1,2})s)?(?:([0-9]{1,3}(?:.[0-9]*)?)ms)?") [ITEM_UNEDITABLE, ITEM_PROJECT, ITEM_POU, ITEM_CLASS, ITEM_VARIABLE, - ITEM_TRANSITION, ITEM_ACTION, ITEM_CONFIGURATION, ITEM_RESOURCE] = range(9) + ITEM_TRANSITION, ITEM_ACTION, ITEM_CONFIGURATION, ITEM_RESOURCE, + ITEM_DATATYPE] = range(10) + +ScriptDirectory = os.path.split(os.path.realpath(__file__))[0] """ pyxsval is not complete and the parts that are not supported print some error @@ -167,6 +170,8 @@ self.FilePath = "" self.FileName = "" self.ProgramFilePath = "" + self.RefreshDataTypeUsingTree() + self.RefreshDataTypes() self.RefreshPouUsingTree() self.RefreshBlockTypes() @@ -192,6 +197,12 @@ self.ProjectBuffer = UndoBuffer(self.Copy(self.Project), False) self.Buffering = False + # Return project data type names + def GetProjectDataTypeNames(self): + if self.Project: + return [datatype.getName() for datatype in self.Project.getDataTypes()] + return [] + # Return project pou names def GetProjectPouNames(self): if self.Project: @@ -263,6 +274,9 @@ def GetProjectInfos(self): if self.Project: infos = {"name": self.Project.getName(), "type": ITEM_PROJECT} + datatypes = {"name": "Data Types", "type": ITEM_UNEDITABLE, "values":[]} + for datatype in self.Project.getDataTypes(): + datatypes["values"].append({"name": datatype.getName(), "type": ITEM_DATATYPE, "values": []}) pou_types = {"function": {"name": "Functions", "type": ITEM_UNEDITABLE, "values":[]}, "functionBlock": {"name": "Function Blocks", "type": ITEM_UNEDITABLE, "values":[]}, "program": {"name": "Programs", "type": ITEM_UNEDITABLE, "values":[]}} @@ -294,11 +308,38 @@ config_infos["values"] = [resources] configurations["values"].append(config_infos) infos["values"] = [{"name": "Properties", "type": ITEM_UNEDITABLE, "values": []}, - pou_types["function"], pou_types["functionBlock"], + datatypes, pou_types["function"], pou_types["functionBlock"], pou_types["program"], configurations] return infos return None + # Refresh the tree of user-defined data type cross-use + def RefreshDataTypeUsingTree(self): + # Reset the tree of user-defined pou cross-use + self.DataTypeUsingTree = {} + if self.Project: + datatypes = self.Project.getDataTypes() + # Reference all the user-defined data type names and initialize the tree of + # user-defined data type cross-use + datatypenames = [datatype.getName() for datatype in datatypes] + for name in datatypenames: + self.DataTypeUsingTree[name] = [] + # Analyze each data type + for datatype in datatypes: + name = datatype.getName() + basetype_content = datatype.getBaseType().getContent() + if basetype_content["value"] is not None: + if basetype_content["name"] == "derived": + basetype_name = basetype_content["value"].getName() + if basetype_name in datatypenames and name not in self.DataTypeUsingTree[basetype_name]: + self.DataTypeUsingTree[basetype_name].append(name) + elif basetype_content["name"] in ["subrangeSigned", "subrangeUnsigned", "array"]: + base_type = basetype_content["value"].baseType.getContent() + if base_type["value"] is not None: + basetype_name = base_type["value"].getName() + if basetype_name in datatypenames and name not in self.DataTypeUsingTree[basetype_name]: + self.DataTypeUsingTree[basetype_name].append(name) + # Refresh the tree of user-defined pou cross-use def RefreshPouUsingTree(self): # Reset the tree of user-defined pou cross-use @@ -317,9 +358,9 @@ # Extract variables from every varLists for type, varlist in pou.getVars(): for var in varlist.getVariable(): - var_type = var.getType().getValue() - if not isinstance(var_type, (StringType, UnicodeType)): - typename = var_type.getName() + vartype_content = var.getType().getContent() + if vartype_content["value"] is not None: + typename = vartype_content["value"].getName() if typename in pounames and name not in self.PouUsingTree[typename]: self.PouUsingTree[typename].append(name) bodytype = pou.getBodyType() @@ -342,12 +383,34 @@ if typename != name and typename_model.search(text): self.PouUsingTree[typename].append(name) + # Return if data type given by name is used by another data type or pou + def DataTypeIsUsed(self, name): + if name in self.DataTypeUsingTree: + return len(self.DataTypeUsingTree[name]) > 0 + return False + # Return if pou given by name is used by another pou def PouIsUsed(self, name): if name in self.PouUsingTree: return len(self.PouUsingTree[name]) > 0 return False + # Return if data type given by name is directly or undirectly used by the reference data type + def DataTypeIsUsedBy(self, name, reference): + if name in self.DataTypeUsingTree: + list = self.DataTypeUsingTree[name] + # Test if data type is directly used by reference + if reference in list: + return True + else: + # Test if data type is undirectly used by reference, by testing if data types + # that directly use data type is directly or undirectly used by reference + used = False + for element in list: + used |= self.DataTypeIsUsedBy(element, reference) + return used + return False + # Return if pou given by name is directly or undirectly used by the reference pou def PouIsUsedBy(self, name, reference): if name in self.PouUsingTree: @@ -380,7 +443,22 @@ #------------------------------------------------------------------------------- # Project Pous management functions #------------------------------------------------------------------------------- - + + # Add a Data Type to Project + def ProjectAddDataType(self, datatype_name): + # Add the pou to project + self.Project.appendDataType(datatype_name) + self.RefreshDataTypeUsingTree() + self.RefreshDataTypes() + self.BufferProject() + + # Remove a Data Type from project + def ProjectRemoveDataType(self, datatype_name): + self.Project.removeDataType(datatype_name) + self.RefreshDataTypeUsingTree() + self.RefreshDataTypes() + self.BufferProject() + # Add a Pou to Project def ProjectAddPou(self, pou_name, pou_type, body_type): # Add the pou to project @@ -391,7 +469,7 @@ self.RefreshBlockTypes() self.BufferProject() - # Remove a pou from project + # Remove a Pou from project def ProjectRemovePou(self, pou_name): self.Project.removePou(pou_name) self.RefreshPouUsingTree() @@ -401,8 +479,6 @@ # Add a configuration to Project def ProjectAddConfiguration(self, config_name): self.Project.addConfiguration(config_name) - self.RefreshPouUsingTree() - self.RefreshBlockTypes() self.BufferProject() # Remove a configuration from project @@ -413,8 +489,6 @@ # Add a resource to a configuration of the Project def ProjectAddConfigurationResource(self, config_name, resource_name): self.Project.addConfigurationResource(config_name, resource_name) - self.RefreshPouUsingTree() - self.RefreshBlockTypes() self.BufferProject() # Remove a resource from a configuration of the project @@ -450,7 +524,15 @@ pou = self.Project.getPou(pou_name) pou.removeAction(action_name) self.BufferProject() - + + # Change the name of a pou + def ChangeDataTypeName(self, old_name, new_name): + # Found the pou corresponding to old name and change its name to new name + datatype = self.Project.getDataType(old_name) + datatype.setName(new_name) + self.Project.updateElementName(old_name, new_name) + self.BufferProject() + # Change the name of a pou def ChangePouName(self, old_name, new_name): # Found the pou corresponding to old name and change its name to new name @@ -575,12 +657,12 @@ tempvar = plcopen.varListPlain_variable() tempvar.setName(var["Name"]) var_type = plcopen.dataType() - if GetBlockType(var["Type"]) != None: + if var["Type"] not in var_type.getChoices(): derived_type = plcopen.derived() derived_type.setName(var["Type"]) - var_type.setValue(derived_type) + var_type.setContent("derived", derived_type) else: - var_type.setValue(var["Type"]) + var_type.setContent(var["Type"], None) tempvar.setType(var_type) if var["Initial Value"] != "": value = plcopen.value() @@ -615,11 +697,11 @@ for varlist in configuration.getGlobalVars(): for var in varlist.getVariable(): tempvar = {"Name":var.getName(),"Class":"Global"} - var_type = var.getType().getValue() - if isinstance(var_type, (StringType, UnicodeType)): - tempvar["Type"] = var_type + vartype_content = var.getType().getContent() + if vartype_content["value"] is None: + tempvar["Type"] = vartype_content["name"] else: - tempvar["Type"] = var_type.getName() + tempvar["Type"] = vartype_content["value"].getName() tempvar["Edit"] = True initial = var.getInitialValue() if initial: @@ -663,11 +745,11 @@ for varlist in resource.getGlobalVars(): for var in varlist.getVariable(): tempvar = {"Name":var.getName(),"Class":"Global"} - var_type = var.getType().getValue() - if isinstance(var_type, (StringType, UnicodeType)): - tempvar["Type"] = var_type + vartype_content = var.getType().getContent() + if vartype_content["value"] is None: + tempvar["Type"] = vartype_content["name"] else: - tempvar["Type"] = var_type.getName() + tempvar["Type"] = vartype_content["value"].getName() tempvar["Edit"] = True initial = var.getInitialValue() if initial: @@ -704,12 +786,12 @@ for type, varlist in pou.getVars(): for var in varlist.getVariable(): tempvar = {"Name":var.getName(),"Class":type} - var_type = var.getType().getValue() - if isinstance(var_type, (StringType, UnicodeType)): - tempvar["Type"] = var_type + vartype_content = var.getType().getContent() + if vartype_content["value"] is None: + tempvar["Type"] = vartype_content["name"] tempvar["Edit"] = True else: - tempvar["Type"] = var_type.getName() + tempvar["Type"] = vartype_content["value"].getName() tempvar["Edit"] = not pou.hasBlock(tempvar["Name"]) initial = var.getInitialValue() if initial: @@ -754,7 +836,12 @@ return_type = plcopen.dataType() pou.interface.setReturnType(return_type) # Change return type - return_type.setValue(type) + if type in self.GetBaseTypes(): + return_type.setContent(type, None) + else: + derived_type = plcopen.derived() + derived_type.setName(type) + return_type.setContent("derived", derived_type) self.RefreshBlockTypes() def UpdateProjectUsedPous(self, old_name, new_name): @@ -778,9 +865,39 @@ # Return the return type if there is one return_type = pou.interface.getReturnType() if return_type: - return return_type.getValue() + returntype_content = return_type.getContent() + if returntype_content["value"] is None: + return returntype_content["name"] + else: + return returntype_content["value"].getName() return None - + + # Update data types with user-defined data types added + def RefreshDataTypes(self): + ResetTypeHierarchy() + ResetEnumeratedDataValues() + if self.Project: + for datatype in self.Project.getDataTypes(): + name = datatype.getName() + basetype_content = datatype.getBaseType().getContent() + if basetype_content["value"] is None: + AddDataTypeHierarchy(name, basetype_content["name"]) + elif basetype_content["name"] == "derived": + AddDataTypeHierarchy(name, basetype_content["value"].getName()) + elif basetype_content["name"] in ["subrangeSigned", "subrangeUnsigned"]: + base_type = basetype_content["value"].baseType.getContent() + if base_type["value"] is None: + AddDataTypeHierarchy(name, base_type["name"]) + else: + AddDataTypeHierarchy(name, base_type["value"].getName()) + else: + if basetype_content["name"] == "enum": + values = [] + for value in basetype_content["value"].values.getValue(): + values.append(value.getName()) + AddEnumeratedDataValues(values) + AddDataTypeHierarchy(name, "ANY_DERIVED") + # Update Block types with user-defined pou added def RefreshBlockTypes(self): if BlockTypes[-1]["name"] == "User-defined POUs": @@ -799,17 +916,34 @@ for type, varlist in pou.getVars(): if type == "InOut": for var in varlist.getVariable(): - block_infos["inputs"].append((var.getName(),var.getType().getValue(),"none")) - block_infos["outputs"].append((var.getName(),var.getType().getValue(),"none")) + var_type = var.type.getContent() + if var_type["value"] is None: + block_infos["inputs"].append((var.getName(),var_type["name"],"none")) + block_infos["outputs"].append((var.getName(),var_type["name"],"none")) + else: + block_infos["inputs"].append((var.getName(),var_type["value"].getName(),"none")) + block_infos["outputs"].append((var.getName(),var_type["value"].getName(),"none")) elif type == "Input": for var in varlist.getVariable(): - block_infos["inputs"].append((var.getName(),var.getType().getValue(),"none")) + var_type = var.type.getContent() + if var_type["value"] is None: + block_infos["inputs"].append((var.getName(),var_type["name"],"none")) + else: + block_infos["inputs"].append((var.getName(),var_type["value"].getName(),"none")) elif type == "Output": for var in varlist.getVariable(): - block_infos["outputs"].append((var.getName(),var.getType().getValue(),"none")) + var_type = var.type.getContent() + if var_type["value"] is None: + block_infos["outputs"].append((var.getName(),var_type["name"],"none")) + else: + block_infos["outputs"].append((var.getName(),var_type["value"].getName(),"none")) return_type = pou.interface.getReturnType() if return_type: - block_infos["outputs"].append(("",return_type.getValue(),"none")) + var_type = return_type.getContent() + if var_type["value"] is None: + block_infos["outputs"].append(("",var_type["name"],"none")) + else: + block_infos["outputs"].append(("",var_type["value"].getName(),"none")) if pou.getBodyType() in ["FBD","LD","SFC"]: for instance in pou.getInstances(): if isinstance(instance, plcopen.comment): @@ -818,16 +952,13 @@ # Return Block types checking for recursion def GetBlockTypes(self, tagname = ""): + name = "" + type = None if self.Project: words = tagname.split("::") - if len(words) == 1: - name = current_name - else: + if words[0] in ["P","T","A"]: name = words[1] - type = self.GetPouType(name) - else: - name = "" - type = None + type = self.GetPouType(name) if type == "function": blocktypes = [] for category in BlockTypes[:-1] + PluginTypes: @@ -853,10 +984,7 @@ if self.Project: words = tagname.split("::") if words[0] in ["P","T","A"]: - if len(words) == 1: - name = current_name - else: - name = words[1] + name = words[1] type = self.GetPouType(name) blocktypes = [] for category in BlockTypes[:-1]: @@ -882,11 +1010,45 @@ blocktypes.append(pou.getName()) return blocktypes + # Return Data Types checking for recursion + def GetDataTypes(self, tagname = "", basetypes = True): + if basetypes: + datatypes = self.GetBaseTypes() + else: + datatypes = [] + if self.Project: + words = tagname.split("::") + if words[0] in ["D"]: + name = words[1] + else: + name = "" + for datatype in self.Project.getDataTypes(): + datatype_name = datatype.getName() + if datatype_name != name and not self.DataTypeIsUsedBy(name, datatype_name): + datatypes.append(datatype_name) + return datatypes + + # Return Base Types + def GetBaseTypes(self): + return [value for value, parent in TypeHierarchy_list if not value.startswith("ANY")] + + # Return Subrange types + def GetSubrangeTypes(self): + return [value for value, range in DataTypeRange_list] + + # Return Enumerated Values + def GetEnumeratedDataValues(self): + return EnumeratedDataValues + #------------------------------------------------------------------------------- # Project Element tag name computation functions #------------------------------------------------------------------------------- - # Compute a pou transition name + # Compute a data type name + def ComputeDataTypeName(self, datatype): + return "D::%s" % datatype + + # Compute a pou name def ComputePouName(self, pou): return "P::%s" % pou @@ -905,6 +1067,112 @@ # Compute a pou name def ComputeConfigurationResourceName(self, config, resource): return "R::%s::%s" % (config, resource) + +#------------------------------------------------------------------------------- +# Project opened Data types management functions +#------------------------------------------------------------------------------- + + # Return the data type informations + def GetDataTypeInfos(self, tagname): + words = tagname.split("::") + if words[0] == "D": + infos = {} + datatype = self.Project.getDataType(words[1]) + basetype_content = datatype.baseType.getContent() + if basetype_content["value"] is None: + infos["type"] = "Directly" + infos["base_type"] = basetype_content["name"] + elif basetype_content["name"] == "derived": + infos["type"] = "Directly" + infos["base_type"] = basetype_content["value"].getName() + elif basetype_content["name"] in ["subrangeSigned", "subrangeUnsigned"]: + infos["type"] = "Subrange" + infos["min"] = basetype_content["value"].range.getLower() + infos["max"] = basetype_content["value"].range.getUpper() + base_type = basetype_content["value"].baseType.getContent() + if base_type["value"] is None: + infos["base_type"] = base_type["name"] + else: + infos["base_type"] = base_type["value"].getName() + elif basetype_content["name"] == "enum": + infos["type"] = "Enumerated" + infos["values"] = [] + for value in basetype_content["value"].values.getValue(): + infos["values"].append(value.getName()) + elif basetype_content["name"] == "array": + infos["type"] = "Array" + infos["dimensions"] = [] + for dimension in basetype_content["value"].getDimension(): + infos["dimensions"].append(str(dimension.getUpper())) + base_type = basetype_content["value"].baseType.getContent() + if base_type["value"] is None: + infos["base_type"] = base_type["name"] + else: + infos["base_type"] = base_type["value"].getName() + if datatype.initialValue is not None: + infos["initial"] = str(datatype.initialValue.getValue()) + else: + infos["initial"] = "" + return infos + return None + + # Change the data type informations + def SetDataTypeInfos(self, tagname, infos): + words = tagname.split("::") + if words[0] == "D": + datatype = self.Project.getDataType(words[1]) + if infos["type"] == "Directly": + if infos["base_type"] in self.GetBaseTypes(): + datatype.baseType.setContent(infos["base_type"], None) + else: + derived_datatype = plcopen.derived() + derived_datatype.setName(infos["base_type"]) + datatype.baseType.setContent("derived", derived_datatype) + elif infos["type"] == "Subrange": + if infos["base_type"] in GetSubTypes("ANY_UINT"): + subrange = plcopen.subrangeUnsigned() + datatype.baseType.setContent("subrangeUnsigned", subrange) + else: + subrange = plcopen.subrangeSigned() + datatype.baseType.setContent("subrangeSigned", subrange) + subrange.range.setLower(infos["min"]) + subrange.range.setUpper(infos["max"]) + if infos["base_type"] in self.GetBaseTypes(): + subrange.baseType.setContent(infos["base_type"], None) + else: + derived_datatype = plcopen.derived() + derived_datatype.setName(infos["base_type"]) + subrange.baseType.setContent("derived", derived_datatype) + elif infos["type"] == "Enumerated": + enumerated = plcopen.enum() + for enum_value in infos["values"]: + value = plcopen.values_value() + value.setName(enum_value) + enumerated.values.appendValue(value) + datatype.baseType.setContent("enum", enumerated) + elif infos["type"] == "Array": + array = plcopen.array() + for dimension in infos["dimensions"]: + dimension_range = plcopen.rangeSigned() + dimension_range.setLower(1) + dimension_range.setUpper(int(dimension)) + array.appendDimension(dimension_range) + if infos["base_type"] in self.GetBaseTypes(): + array.baseType.setContent(infos["base_type"], None) + else: + derived_datatype = plcopen.derived() + derived_datatype.setName(infos["base_type"]) + array.baseType.setContent("derived", derived_datatype) + datatype.baseType.setContent("array", array) + if infos["initial"] != "": + if datatype.initialValue is None: + datatype.initialValue = plcopen.value() + datatype.initialValue.setValue(infos["initial"]) + else: + datatype.initialValue = None + self.RefreshDataTypeUsingTree() + self.RefreshDataTypes() + self.BufferProject() #------------------------------------------------------------------------------- # Project opened Pous management functions @@ -972,7 +1240,7 @@ return "BOOL" return None - # Change the edited element taxt + # Change the edited element text def SetEditedElementText(self, tagname, text): element = self.GetEditedElement(tagname) if element != None: @@ -1331,7 +1599,11 @@ for type, varlist in pou.getVars(): for var in varlist.getVariable(): if var.getName() == varname: - return var.getType().getValue() + vartype_content = var.getType().getContent() + if vartype_content["value"] is None: + return vartype_content["name"] + else: + return vartype_content["value"].getName() return None def SetConnectionWires(self, connection, connector): @@ -1998,7 +2270,7 @@ if self.VerifyXML: if sys: sys.stdout = HolePseudoFile() - result = pyxsval.parseAndValidate(filepath, os.path.join(sys.path[0], "plcopen/TC6_XML_V10_B.xsd")) + result = pyxsval.parseAndValidate(filepath, os.path.join(ScriptDirectory, "plcopen", "TC6_XML_V10_B.xsd")) if sys: sys.stdout = sys.__stdout__ tree = result.getTree() @@ -2016,6 +2288,8 @@ self.Buffering = False self.ElementsOpened = [] self.CurrentElementEditing = None + self.RefreshDataTypeUsingTree() + self.RefreshDataTypes() self.RefreshPouUsingTree() self.RefreshBlockTypes() return None @@ -2031,11 +2305,11 @@ "xmlns:xsi" : "http://www.w3.org/2001/XMLSchema-instance", "xsi:schemaLocation" : "http://www.plcopen.org/xml/tc6.xsd http://www.plcopen.org/xml/tc6.xsd"} text += self.Project.generateXMLText("project", 0, extras) - + if self.VerifyXML: if sys: sys.stdout = HolePseudoFile() - pyxsval.parseAndValidateString(text, open(os.path.join(sys.path[0], "plcopen/TC6_XML_V10_B.xsd"),"r").read()) + pyxsval.parseAndValidateString(text, open(os.path.join(ScriptDirectory, "plcopen", "TC6_XML_V10_B.xsd"),"r").read()) if sys: sys.stdout = sys.__stdout__