--- 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__