--- a/PLCControler.py Wed May 28 11:50:38 2014 +0200
+++ b/PLCControler.py Wed May 28 18:33:41 2014 +0200
@@ -2,7 +2,7 @@
#This file is part of PLCOpenEditor, a library implementing an IEC 61131-3 editor
-#based on the plcopen standard.
+#based on the plcopen standard. #Copyright (C) 2007: Edouard TISSERANT and Laurent BESSARD
@@ -57,7 +57,7 @@
ITEMS_VARIABLE = [ITEM_VAR_LOCAL,
@@ -92,12 +92,12 @@
def GetUneditableNames():
- return [_("User-defined POUs"), _("Functions"), _("Function Blocks"),
- _("Programs"), _("Data Types"), _("Transitions"), _("Actions"),
+ return [_("User-defined POUs"), _("Functions"), _("Function Blocks"), + _("Programs"), _("Data Types"), _("Transitions"), _("Actions"), _("Configurations"), _("Resources"), _("Properties")]
UNEDITABLE_NAMES = GetUneditableNames()
-[USER_DEFINED_POUS, FUNCTIONS, FUNCTION_BLOCKS, PROGRAMS,
- DATA_TYPES, TRANSITIONS, ACTIONS, CONFIGURATIONS,
+[USER_DEFINED_POUS, FUNCTIONS, FUNCTION_BLOCKS, PROGRAMS, + DATA_TYPES, TRANSITIONS, ACTIONS, CONFIGURATIONS, RESOURCES, PROPERTIES] = UNEDITABLE_NAMES
#-------------------------------------------------------------------------------
@@ -133,7 +133,7 @@
_BoolValue = lambda x: x in ["true", "0"]
def _translate_args(translations, args):
- return [translate(arg[0]) if len(arg) > 0 else None
+ return [translate(arg[0]) if len(arg) > 0 else None @@ -142,7 +142,7 @@
#-------------------------------------------------------------------------------
class _VariableInfos(object):
- __slots__ = ["Name", "Class", "Option", "Location", "InitialValue",
+ __slots__ = ["Name", "Class", "Option", "Location", "InitialValue", "Edit", "Documentation", "Type", "Tree", "Number"]
def __init__(self, *args):
for attr, value in zip(self.__slots__, args):
@@ -151,39 +151,39 @@
return _VariableInfos(*[getattr(self, attr) for attr in self.__slots__])
class VariablesInfosFactory:
def __init__(self, variables):
self.Variables = variables
def SetType(self, context, *args):
if len(self.Dimensions) > 0:
return ("array", self.Type, self.Dimensions)
return (self.TreeStack.pop(-1), self.Dimensions)
def AddDimension(self, context, *args):
self.Dimensions.append(tuple(
_translate_args([_StringValue] * 2, args)))
def AddTree(self, context, *args):
self.TreeStack.append([])
def AddVarToTree(self, context, *args):
var = (args[0][0], self.Type, self.GetTree())
self.TreeStack[-1].append(var)
def AddVariable(self, context, *args):
self.Variables.append(_VariableInfos(*(_translate_args(
- [_StringValue] * 5 + [_BoolValue] + [_StringValue], args) +
+ [_StringValue] * 5 + [_BoolValue] + [_StringValue], args) + [self.GetType(), self.GetTree()])))
#-------------------------------------------------------------------------------
@@ -199,15 +199,15 @@
"program": ITEM_PROGRAM}.get(value)
if class_type is not None:
pou_type = POU_TYPES.get(value)
var_type = VAR_CLASS_INFOS.get(value)
class _VariablesTreeItemInfos(object):
@@ -219,24 +219,24 @@
return _VariableTreeItem(*[getattr(self, attr) for attr in self.__slots__])
class VariablesTreeInfosFactory:
def SetRoot(self, context, *args):
self.Root = _VariablesTreeItemInfos(
*([''] + _translate_args(
- [class_extraction, _StringValue] + [_BoolValue] * 2,
+ [class_extraction, _StringValue] + [_BoolValue] * 2, def AddVariable(self, context, *args):
if self.Root is not None:
self.Root.variables.append(_VariablesTreeItemInfos(
- [_StringValue, class_extraction, _StringValue] +
+ [_StringValue, class_extraction, _StringValue] + [_BoolValue] * 2, args) + [[]])))
#-------------------------------------------------------------------------------
@@ -244,10 +244,10 @@
#-------------------------------------------------------------------------------
class InstancesPathFactory:
def __init__(self, instances):
self.Instances = instances
def AddInstance(self, context, *args):
self.Instances.append(args[0][0])
@@ -260,13 +260,13 @@
def __init__(self, controller):
self.Controller = controller
def ConfigTagName(self, context, *args):
self.TagName = self.Controller.ComputeConfigurationName(args[0][0])
def ResourceTagName(self, context, *args):
self.TagName = self.Controller.ComputeConfigurationResourceName(args[0][0], args[1][0])
@@ -285,15 +285,15 @@
_Point = namedtuple("Point", ["x", "y"])
-_BlockInstanceInfos = namedtuple("BlockInstanceInfos",
+_BlockInstanceInfos = namedtuple("BlockInstanceInfos", ["type", "id", "x", "y", "width", "height", "specific_values", "inputs", "outputs"])
- namedtuple("BlockSpecificValues",
+ namedtuple("BlockSpecificValues", ["name", "execution_order"]),
_VariableSpecificValues = (
- namedtuple("VariableSpecificValues",
+ namedtuple("VariableSpecificValues", ["name", "value_type", "execution_order"]),
[_StringValue, _StringValue, int])
_ConnectionSpecificValues = (
@@ -305,7 +305,7 @@
_LDElementSpecificValues = (
- namedtuple("LDElementSpecificValues",
+ namedtuple("LDElementSpecificValues", ["name", "negated", "edge", "storage", "execution_order"]),
[_StringValue, _BoolValue, _StringValue, _StringValue, int])
@@ -330,7 +330,7 @@
namedtuple("StepSpecificValues", ["name", "initial", "action"]),
[_StringValue, _BoolValue, lambda x: x]),
- namedtuple("TransitionSpecificValues",
+ namedtuple("TransitionSpecificValues", ["priority", "condition_type", "condition", "connection"]),
[int, _StringValue, _StringValue, lambda x: x]),
"selectionDivergence": _DivergenceSpecificValues,
@@ -360,24 +360,24 @@
return _ActionInfos(*[getattr(self, attr) for attr in self.__slots__])
class BlockInstanceFactory:
def __init__(self, block_instances):
self.BlockInstances = block_instances
self.CurrentInstance = None
self.SpecificValues = None
self.CurrentConnection = None
def SetSpecificValues(self, context, *args):
self.SpecificValues = list(args)
self.CurrentInstance = None
self.CurrentConnection = None
def AddBlockInstance(self, context, *args):
specific_values_tuple, specific_values_translation = \
_SpecificValuesTuples.get(args[0][0], _BlockSpecificValues)
if (args[0][0] == "step" and len(self.SpecificValues) < 3 or
args[0][0] == "transition" and len(self.SpecificValues) < 4):
self.SpecificValues.append([None])
@@ -386,21 +386,21 @@
specific_values = specific_values_tuple(*_translate_args(
specific_values_translation, self.SpecificValues))
self.SpecificValues = None
self.CurrentInstance = _BlockInstanceInfos(
- *(_translate_args([_StringValue, int] + [float] * 4, args) +
+ *(_translate_args([_StringValue, int] + [float] * 4, args) + [specific_values, [], []]))
self.BlockInstances[self.CurrentInstance.id] = self.CurrentInstance
def AddInstanceConnection(self, context, *args):
connection_args = _translate_args(
[_StringValue] * 2 + [_BoolValue, _StringValue] + [float] * 2, args)
self.CurrentConnection = _InstanceConnectionInfos(
*(connection_args[1:4] + [
_Point(*connection_args[4:6]), []]))
if self.CurrentInstance is not None:
if connection_args[0] == "input":
self.CurrentInstance.inputs.append(self.CurrentConnection)
@@ -408,22 +408,22 @@
self.CurrentInstance.outputs.append(self.CurrentConnection)
self.SpecificValues.append([self.CurrentConnection])
def AddConnectionLink(self, context, *args):
self.CurrentLink = _ConnectionLinkInfos(
*(_translate_args([int, _StringValue], args) + [[]]))
self.CurrentConnection.links.append(self.CurrentLink)
def AddLinkPoint(self, context, *args):
self.CurrentLink.points.append(_Point(
*_translate_args([float] * 2, args)))
def AddAction(self, context, *args):
if len(self.SpecificValues) == 0:
self.SpecificValues.append([[]])
translated_args = _translate_args([_StringValue] * 5, args)
self.SpecificValues[0][0].append(_ActionInfos(*translated_args))
pou_block_instances_xslt = etree.parse(
os.path.join(ScriptDirectory, "plcopen", "pou_block_instances.xslt"))
@@ -461,7 +461,7 @@
# Add a new state in buffer
def Buffering(self, currentstate):
self.CurrentIndex = (self.CurrentIndex + 1) % UNDO_BUFFER_LENGTH
@@ -474,29 +474,29 @@
self.MinIndex = (self.MinIndex + 1) % UNDO_BUFFER_LENGTH
self.MinIndex = max(self.MinIndex, 0)
# Return current state of buffer
return self.Buffer[self.CurrentIndex]
# Change current state to previous in buffer and return new current state
if self.CurrentIndex != self.MinIndex:
self.CurrentIndex = (self.CurrentIndex - 1) % UNDO_BUFFER_LENGTH
return self.Buffer[self.CurrentIndex]
# Change current state to next in buffer and return new current state
if self.CurrentIndex != self.MaxIndex:
self.CurrentIndex = (self.CurrentIndex + 1) % UNDO_BUFFER_LENGTH
return self.Buffer[self.CurrentIndex]
# Return True if current state is the first in buffer
return self.CurrentIndex == self.MinIndex
# Return True if current state is the last in buffer
return self.CurrentIndex == self.MaxIndex
@@ -504,7 +504,7 @@
# Note that current state is saved
self.LastSave = self.CurrentIndex
# Return True if current state is saved
def IsCurrentSaved(self):
return self.LastSave == self.CurrentIndex
@@ -518,12 +518,12 @@
Class which controls the operations made on the plcopen model and answers to view requests
# Create a new PLCControler
# Reset PLCControler internal variables
@@ -541,7 +541,7 @@
self.TotalTypesDict = StdBlckDct.copy()
self.TotalTypes = StdBlckLst[:]
self.ProgramFilePath = ""
def GetQualifierTypes(self):
@@ -567,7 +567,7 @@
self.Project.setfileHeader(properties)
self.Project.setcontentHeader(properties)
# Initialize the project buffer
self.CreateProjectBuffer(False)
@@ -575,28 +575,28 @@
self.NextCompiledProject = self.Copy(self.Project)
self.CurrentCompiledProject = None
# Return project data type names
def GetProjectDataTypeNames(self, debug = False):
project = self.GetProject(debug)
return [datatype.getname() for datatype in project.getdataTypes()]
# Return project pou names
def GetProjectPouNames(self, debug = False):
project = self.GetProject(debug)
return [pou.getname() for pou in project.getpous()]
# Return project pou names
def GetProjectConfigNames(self, debug = False):
project = self.GetProject(debug)
return [config.getname() for config in project.getconfigurations()]
# Return project pou variable names
def GetProjectPouVariableNames(self, pou_name = None, debug = False):
@@ -610,15 +610,15 @@
for action in pou.getactionList():
variables.append(action.getname())
# Return file path if project is an open file
# Return file path if project is an open file
def GetProgramFilePath(self):
return self.ProgramFilePath
# Return file name and point out if file is up to date
if self.Project is not None:
@@ -627,7 +627,7 @@
return "~%s~"%self.FileName
# Change file path and save file name or create a default one if file path not defined
def SetFilePath(self, filepath):
@@ -636,7 +636,7 @@
self.FileName = _("Unnamed%d")%self.LastNewIndex
self.FileName = os.path.splitext(os.path.basename(filepath))[0]
# Change project properties
def SetProjectProperties(self, name = None, properties = None, buffer = True):
if self.Project is not None:
@@ -647,14 +647,14 @@
self.Project.setcontentHeader(properties)
if buffer and (name is not None or properties is not None):
def GetProjectName(self, debug=False):
project = self.GetProject(debug)
# Return project properties
def GetProjectProperties(self, debug = False):
project = self.GetProject(debug)
@@ -663,7 +663,7 @@
properties.update(project.getcontentHeader())
# Return project informations
def GetProjectInfos(self, debug = False):
project = self.GetProject(debug)
@@ -671,7 +671,7 @@
infos = {"name": project.getname(), "type": ITEM_PROJECT}
datatypes = {"name": DATA_TYPES, "type": ITEM_DATATYPES, "values":[]}
for datatype in project.getdataTypes():
- datatypes["values"].append({"name": datatype.getname(), "type": ITEM_DATATYPE,
+ datatypes["values"].append({"name": datatype.getname(), "type": ITEM_DATATYPE, "tagname": self.ComputeDataTypeName(datatype.getname()), "values": []})
pou_types = {"function": {"name": FUNCTIONS, "type": ITEM_FUNCTION, "values":[]},
"functionBlock": {"name": FUNCTION_BLOCKS, "type": ITEM_FUNCTIONBLOCK, "values":[]},
@@ -684,14 +684,14 @@
if pou.getbodyType() == "SFC":
for transition in pou.gettransitionList():
- transitions.append({"name": transition.getname(), "type": ITEM_TRANSITION,
- "tagname": self.ComputePouTransitionName(pou.getname(), transition.getname()),
+ transitions.append({"name": transition.getname(), "type": ITEM_TRANSITION, + "tagname": self.ComputePouTransitionName(pou.getname(), transition.getname()), pou_values.append({"name": TRANSITIONS, "type": ITEM_TRANSITIONS, "values": transitions})
for action in pou.getactionList():
- actions.append({"name": action.getname(), "type": ITEM_ACTION,
- "tagname": self.ComputePouActionName(pou.getname(), action.getname()),
+ actions.append({"name": action.getname(), "type": ITEM_ACTION, + "tagname": self.ComputePouActionName(pou.getname(), action.getname()), pou_values.append({"name": ACTIONS, "type": ITEM_ACTIONS, "values": actions})
if pou_type in pou_types:
@@ -700,19 +700,19 @@
configurations = {"name": CONFIGURATIONS, "type": ITEM_CONFIGURATIONS, "values": []}
for config in project.getconfigurations():
config_name = config.getname()
- config_infos = {"name": config_name, "type": ITEM_CONFIGURATION,
- "tagname": self.ComputeConfigurationName(config.getname()),
+ config_infos = {"name": config_name, "type": ITEM_CONFIGURATION, + "tagname": self.ComputeConfigurationName(config.getname()), resources = {"name": RESOURCES, "type": ITEM_RESOURCES, "values": []}
for resource in config.getresource():
resource_name = resource.getname()
- resource_infos = {"name": resource_name, "type": ITEM_RESOURCE,
- "tagname": self.ComputeConfigurationResourceName(config.getname(), resource.getname()),
+ resource_infos = {"name": resource_name, "type": ITEM_RESOURCE, + "tagname": self.ComputeConfigurationResourceName(config.getname(), resource.getname()), resources["values"].append(resource_infos)
config_infos["values"] = [resources]
configurations["values"].append(config_infos)
- infos["values"] = [datatypes, pou_types["function"], pou_types["functionBlock"],
+ infos["values"] = [datatypes, pou_types["function"], pou_types["functionBlock"], pou_types["program"], configurations]
@@ -722,17 +722,17 @@
project = self.GetProject(debug)
factory = VariablesTreeInfosFactory()
parser = etree.XMLParser()
parser.resolvers.add(LibraryResolver(self, debug))
pou_variable_xslt_tree = etree.XSLT(
os.path.join(ScriptDirectory, "plcopen", "pou_variables.xslt"),
extensions = {("pou_vars_ns", name): getattr(factory, name)
for name in ["SetRoot", "AddVariable"]})
words = tagname.split("::")
@@ -742,7 +742,7 @@
pou_variable_xslt_tree(obj)
def GetInstanceList(self, root, name, debug = False):
@@ -750,22 +750,22 @@
project = self.GetProject(debug)
factory = InstancesPathFactory(instances)
parser = etree.XMLParser()
parser.resolvers.add(LibraryResolver(self, debug))
instances_path_xslt_tree = etree.XSLT(
os.path.join(ScriptDirectory, "plcopen", "instances_path.xslt"),
("instances_ns", "AddInstance"): factory.AddInstance})
- instances_path_xslt_tree(root,
+ instances_path_xslt_tree(root, instance_type=etree.XSLT.strparam(name))
def SearchPouInstances(self, tagname, debug = False):
project = self.GetProject(debug)
@@ -781,28 +781,28 @@
for instance in self.SearchPouInstances(
self.ComputePouName(words[1]), debug)]
def GetPouInstanceTagName(self, instance_path, debug = False):
project = self.GetProject(debug)
factory = InstanceTagName(self)
parser = etree.XMLParser()
parser.resolvers.add(LibraryResolver(self, debug))
instance_tagname_xslt_tree = etree.XSLT(
os.path.join(ScriptDirectory, "plcopen", "instance_tagname.xslt"),
extensions = {("instance_tagname_ns", name): getattr(factory, name)
for name in ["ConfigTagName", "ResourceTagName",
- "PouTagName", "ActionTagName",
+ "PouTagName", "ActionTagName",
- instance_tagname_xslt_tree(project,
+ instance_tagname_xslt_tree(project, instance_path=etree.XSLT.strparam(instance_path))
return factory.GetTagName()
def GetInstanceInfos(self, instance_path, debug = False):
tagname = self.GetPouInstanceTagName(instance_path)
@@ -818,7 +818,7 @@
if var_infos.name == var_name:
# Return if data type given by name is used by another data type or pou
def DataTypeIsUsed(self, name, debug = False):
project = self.GetProject(debug)
@@ -887,11 +887,11 @@
row, col = next_row, next_col
#-------------------------------------------------------------------------------
# Project Pous management functions
#-------------------------------------------------------------------------------
# Add a Data Type to Project
def ProjectAddDataType(self, datatype_name=None):
if self.Project is not None:
@@ -902,13 +902,13 @@
return self.ComputeDataTypeName(datatype_name)
# Remove a Data Type from project
def ProjectRemoveDataType(self, datatype_name):
if self.Project is not None:
self.Project.removedataType(datatype_name)
def ProjectAddPou(self, pou_name, pou_type, body_type):
if self.Project is not None:
@@ -919,21 +919,21 @@
return self.ComputePouName(pou_name)
def ProjectChangePouType(self, name, pou_type):
if self.Project is not None:
pou = self.Project.getpou(name)
def GetPouXml(self, pou_name):
if self.Project is not None:
pou = self.Project.getpou(pou_name)
def PastePou(self, pou_type, pou_xml):
Adds the POU defined by 'pou_xml' to the current project with type 'pou_type'
@@ -944,21 +944,21 @@
return _("Couldn't paste non-POU object.")
- while self.Project.getpou(new_name):
+ while self.Project.getpou(new_name) is not None: # a POU with that name already exists.
# make a new name and test if a POU with that name exists.
# append an incrementing numeric suffix to the POU name.
new_name = "%s%d" % (name, idx)
# we've found a name that does not already exist, use it
new_pou.setname(new_name)
orig_type = new_pou.getpouType()
@@ -968,12 +968,12 @@
if orig_type == 'functionBlock' and pou_type == 'function' or \
orig_type == 'program' and pou_type in ['function', 'functionBlock']:
return _('''%s "%s" can't be pasted as a %s.''') % (orig_type, name, pou_type)
new_pou.setpouType(pou_type)
- self.Project.insertpou(-1, new_pou)
+ self.Project.insertpou(0, new_pou)
return self.ComputePouName(new_name),
# Remove a Pou from project
@@ -981,7 +981,7 @@
if self.Project is not None:
self.Project.removepou(pou_name)
# Return the name of the configuration if only one exist
def GetProjectMainConfigurationName(self):
if self.Project is not None:
@@ -990,7 +990,7 @@
if len(configurations) == 1:
return configurations[0].getname()
# Add a configuration to Project
def ProjectAddConfiguration(self, config_name=None):
if self.Project is not None:
@@ -1000,13 +1000,13 @@
return self.ComputeConfigurationName(config_name)
# Remove a configuration from project
def ProjectRemoveConfiguration(self, config_name):
if self.Project is not None:
self.Project.removeconfiguration(config_name)
# Add a resource to a configuration of the Project
def ProjectAddConfigurationResource(self, config_name, resource_name=None):
if self.Project is not None:
@@ -1016,13 +1016,13 @@
return self.ComputeConfigurationResourceName(config_name, resource_name)
# Remove a resource from a configuration of the project
def ProjectRemoveConfigurationResource(self, config_name, resource_name):
if self.Project is not None:
self.Project.removeconfigurationResource(config_name, resource_name)
# Add a Transition to a Project Pou
def ProjectAddPouTransition(self, pou_name, transition_name, transition_type):
if self.Project is not None:
@@ -1032,7 +1032,7 @@
return self.ComputePouTransitionName(pou_name, transition_name)
# Remove a Transition from a Project Pou
def ProjectRemovePouTransition(self, pou_name, transition_name):
# Search if the pou removed is currently opened
@@ -1041,7 +1041,7 @@
pou.removetransition(transition_name)
# Add an Action to a Project Pou
def ProjectAddPouAction(self, pou_name, action_name, action_type):
if self.Project is not None:
@@ -1051,7 +1051,7 @@
return self.ComputePouActionName(pou_name, action_name)
# Remove an Action from a Project Pou
def ProjectRemovePouAction(self, pou_name, action_name):
# Search if the pou removed is currently opened
@@ -1060,7 +1060,7 @@
pou.removeaction(action_name)
# Change the name of a pou
def ChangeDataTypeName(self, old_name, new_name):
if self.Project is not None:
@@ -1070,7 +1070,7 @@
datatype.setname(new_name)
self.Project.updateElementName(old_name, new_name)
# Change the name of a pou
def ChangePouName(self, old_name, new_name):
if self.Project is not None:
@@ -1080,7 +1080,7 @@
self.Project.updateElementName(old_name, new_name)
# Change the name of a pou transition
def ChangePouTransitionName(self, pou_name, old_name, new_name):
if self.Project is not None:
@@ -1092,7 +1092,7 @@
transition.setname(new_name)
pou.updateElementName(old_name, new_name)
# Change the name of a pou action
def ChangePouActionName(self, pou_name, old_name, new_name):
if self.Project is not None:
@@ -1104,7 +1104,7 @@
pou.updateElementName(old_name, new_name)
# Change the name of a pou variable
def ChangePouVariableName(self, pou_name, old_name, new_name):
if self.Project is not None:
@@ -1116,7 +1116,7 @@
if var.getname() == old_name:
# Change the name of a configuration
def ChangeConfigurationName(self, old_name, new_name):
if self.Project is not None:
@@ -1125,7 +1125,7 @@
if configuration is not None:
configuration.setname(new_name)
# Change the name of a configuration resource
def ChangeConfigurationResourceName(self, config_name, old_name, new_name):
if self.Project is not None:
@@ -1134,7 +1134,7 @@
resource.setname(new_name)
# Return the description of the pou given by its name
def GetPouDescription(self, name, debug = False):
project = self.GetProject(debug)
@@ -1144,7 +1144,7 @@
return pou.getdescription()
# Return the description of the pou given by its name
def SetPouDescription(self, name, description, debug = False):
project = self.GetProject(debug)
@@ -1154,7 +1154,7 @@
pou.setdescription(description)
# Return the type of the pou given by its name
def GetPouType(self, name, debug = False):
project = self.GetProject(debug)
@@ -1164,7 +1164,7 @@
# Return pous with SFC language
def GetSFCPous(self, debug = False):
@@ -1174,7 +1174,7 @@
if pou.getBodyType() == "SFC":
list.append(pou.getname())
# Return the body language of the pou given by its name
def GetPouBodyType(self, name, debug = False):
project = self.GetProject(debug)
@@ -1184,7 +1184,7 @@
# Return the actions of a pou
def GetPouTransitions(self, pou_name, debug = False):
@@ -1196,7 +1196,7 @@
for transition in pou.gettransitionList():
transitions.append(transition.getname())
# Return the body language of the transition given by its name
def GetTransitionBodyType(self, pou_name, pou_transition, debug = False):
project = self.GetProject(debug)
@@ -1209,7 +1209,7 @@
if transition is not None:
return transition.getbodyType()
# Return the actions of a pou
def GetPouActions(self, pou_name, debug = False):
@@ -1221,7 +1221,7 @@
for action in pou.getactionList():
actions.append(action.getname())
# Return the body language of the pou given by its name
def GetActionBodyType(self, pou_name, pou_action, debug = False):
project = self.GetProject(debug)
@@ -1233,18 +1233,18 @@
return action.getbodyType()
# Extract varlists from a list of vars
def ExtractVarLists(self, vars):
- next_type = (var.Class,
- var.Location in ["", None] or
- # When declaring globals, located
- # and not located variables are
+ next_type = (var.Class, + var.Location in ["", None] or + # When declaring globals, located + # and not located variables are # in the same declaration block
if current_type != next_type:
@@ -1264,7 +1264,7 @@
# Create variable and change its properties
tempvar = PLCOpenParser.CreateElement("variable", "varListPlain")
tempvar.setname(var.Name)
var_type = PLCOpenParser.CreateElement("type", "variable")
if isinstance(var.Type, TupleType):
if var.Type[0] == "array":
@@ -1317,28 +1317,28 @@
# Add variable to varList
current_varlist.appendvariable(tempvar)
def GetVariableDictionary(self, object_with_vars, tree=False, debug=False):
factory = VariablesInfosFactory(variables)
parser = etree.XMLParser()
parser.resolvers.add(LibraryResolver(self, debug))
variables_infos_xslt_tree = etree.XSLT(
os.path.join(ScriptDirectory, "plcopen", "variables_infos.xslt"),
extensions = {("var_infos_ns", name): getattr(factory, name)
for name in ["SetType", "AddDimension", "AddTree",
"AddVarToTree", "AddVariable"]})
variables_infos_xslt_tree(object_with_vars,
tree=etree.XSLT.strparam(str(tree)))
# Add a global var to configuration to configuration
- def AddConfigurationGlobalVar(self, config_name, var_type, var_name,
+ def AddConfigurationGlobalVar(self, config_name, var_type, var_name, location="", description=""):
if self.Project is not None:
# Found the configuration corresponding to name
@@ -1346,7 +1346,7 @@
if configuration is not None:
# Set configuration global vars
configuration.addglobalVar(
- self.GetVarTypeObject(var_type),
+ self.GetVarTypeObject(var_type), var_name, location, description)
# Replace the configuration globalvars by those given
@@ -1359,7 +1359,7 @@
configuration.setglobalVars([
varlist for vartype, varlist
in self.ExtractVarLists(vars)])
# Return the configuration globalvars
def GetConfigurationGlobalVars(self, name, debug = False):
project = self.GetProject(debug)
@@ -1369,7 +1369,7 @@
if configuration is not None:
# Extract variables defined in configuration
return self.GetVariableDictionary(configuration, debug)
# Return configuration variable names
@@ -1381,7 +1381,7 @@
if config_name is None or config_name == configuration.getname():
[var.getname() for var in reduce(
- lambda x, y: x + y, [varlist.getvariable()
+ lambda x, y: x + y, [varlist.getvariable() for varlist in configuration.globalVars],
@@ -1396,7 +1396,7 @@
varlist for vartype, varlist
in self.ExtractVarLists(vars)])
# Return the resource globalvars
def GetConfigurationResourceGlobalVars(self, config_name, name, debug = False):
project = self.GetProject(debug)
@@ -1406,11 +1406,11 @@
# Extract variables defined in configuration
return self.GetVariableDictionary(resource, debug)
# Return resource variable names
- def GetConfigurationResourceVariableNames(self,
+ def GetConfigurationResourceVariableNames(self, config_name = None, resource_name = None, debug = False):
project = self.GetProject(debug)
@@ -1421,7 +1421,7 @@
if resource_name is None or resource.getname() == resource_name:
[var.getname() for var in reduce(
- lambda x, y: x + y, [varlist.getvariable()
+ lambda x, y: x + y, [varlist.getvariable() for varlist in resource.globalVars],
@@ -1445,7 +1445,7 @@
pou.interface = PLCOpenParser.CreateElement("interface", "pou")
pou.setvars([varlist for varlist_type, varlist in self.ExtractVarLists(vars)])
# Replace the return type of the pou given by its name (only for functions)
def SetPouInterfaceReturnType(self, name, return_type):
if self.Project is not None:
@@ -1468,16 +1468,16 @@
derived_type = PLCOpenParser.CreateElement("derived", "dataType")
derived_type.setname(return_type)
return_type.setcontent(derived_type)
def UpdateProjectUsedPous(self, old_name, new_name):
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)
pou.updateElementName(old_name, new_name)
# Return the return type of the given pou
def GetPouInterfaceReturnType(self, pou, tree=False, debug=False):
# Verify that the pou has an interface
@@ -1486,23 +1486,23 @@
return_type = pou.interface.getreturnType()
if return_type is not None:
factory = VariablesInfosFactory([])
parser = etree.XMLParser()
parser.resolvers.add(LibraryResolver(self))
return_type_infos_xslt_tree = etree.XSLT(
os.path.join(ScriptDirectory, "plcopen", "variables_infos.xslt"),
extensions = {("var_infos_ns", name): getattr(factory, name)
- for name in ["SetType", "AddDimension",
+ for name in ["SetType", "AddDimension", "AddTree", "AddVarToTree"]})
return_type_infos_xslt_tree(return_type,
tree=etree.XSLT.strparam(str(tree)))
return [factory.GetType(), factory.GetTree()]
@@ -1519,7 +1519,7 @@
BlkLst = self.TotalTypesDict.setdefault(desc["name"],[])
BlkLst.append((section["name"], desc))
# Function that clear the confnode list
def ClearConfNodeTypes(self):
@@ -1529,11 +1529,11 @@
def GetConfNodeDataTypes(self, exclude = None, only_locatables = False):
return [{"name": _("%s Data Types") % confnodetypes["name"],
for datatype in confnodetypes["types"].getdataTypes()
if not only_locatables or self.IsLocatableDataType(datatype, debug)]}
for confnodetypes in self.ConfNodeTypes]
def GetVariableLocationTree(self):
@@ -1545,7 +1545,7 @@
for var_name, var_type, var_initial in self.GetConfNodeGlobalInstances():
tempvar = PLCOpenParser.CreateElement("variable", "globalVars")
tempvar.setname(var_name)
tempvartype = PLCOpenParser.CreateElement("type", "variable")
if var_type in self.GetBaseTypes():
tempvartype.setcontent(PLCOpenParser.CreateElement(
@@ -1557,12 +1557,12 @@
tempderivedtype.setname(var_type)
tempvartype.setcontent(tempderivedtype)
tempvar.settype(tempvartype)
value = PLCOpenParser.CreateElement("initialValue", "variable")
value.setvalue(var_initial)
tempvar.setinitialValue(value)
global_vars.append(tempvar)
@@ -1592,11 +1592,11 @@
blocktype_infos = blocktype.getblockInfos()
if inputs in [None, "undefined"]:
- if inputs == tuple([var_type
+ if inputs == tuple([var_type for name, var_type, modifier in blocktype_infos["inputs"]]):
# Return Block types checking for recursion
@@ -1610,18 +1610,18 @@
if words[0] in ["P","T","A"]:
pou_type = self.GetPouType(name, debug)
- if pou_type == "function" or words[0] == "T"
+ if pou_type == "function" or words[0] == "T" else ["functionBlock", "function"])
{"name": category["name"],
"list": [block for block in category["list"]
if block["type"] in filter]}
for category in self.TotalTypes]
- blocktypes.append({"name" : USER_DEFINED_POUS,
+ blocktypes.append({"name" : USER_DEFINED_POUS, "list": [pou.getblockInfos()
for pou in project.getpous(name, filter)
len(self.GetInstanceList(pou, name, debug)) == 0)]})
@@ -1641,7 +1641,7 @@
blocktypes.extend([pou.getname()
for pou in project.getpous(name, ["functionBlock"])
len(self.GetInstanceList(pou, name, debug)) == 0)])
@@ -1672,10 +1672,10 @@
for datatype in project.getdataTypes(name)
if (not only_locatables or self.IsLocatableDataType(datatype, debug))
len(self.GetInstanceList(datatype, name, debug)) == 0)])
for category in self.GetConfNodeDataTypes(name, only_locatables):
@@ -1730,14 +1730,14 @@
def GetBaseType(self, typename, debug = False):
if TypeHierarchy.has_key(typename):
datatype = self.GetDataType(typename, debug)
basetype = self.GetDataTypeBaseType(datatype)
return self.GetBaseType(basetype, debug)
@@ -1751,19 +1751,19 @@
def IsOfType(self, typename, reference, debug = False):
if reference is None or typename == reference:
basetype = TypeHierarchy.get(typename)
return self.IsOfType(basetype, reference)
datatype = self.GetDataType(typename, debug)
basetype = self.GetDataTypeBaseType(datatype)
return self.IsOfType(basetype, reference, debug)
def IsEndType(self, typename):
return not typename.startswith("ANY")
@@ -1781,16 +1781,16 @@
if array_base_type.getLocalTag() == "derived":
return self.IsLocatableType(array_base_type.getname(), debug)
def IsLocatableType(self, typename, debug = False):
if isinstance(typename, TupleType) or self.GetBlockType(typename) is not None:
datatype = self.GetDataType(typename, debug)
return self.IsLocatableDataType(datatype)
def IsEnumeratedType(self, typename, debug = False):
if isinstance(typename, TupleType):
@@ -1822,7 +1822,7 @@
def IsNumType(self, typename, debug = False):
return self.IsOfType(typename, "ANY_NUM", debug) or\
self.IsOfType(typename, "ANY_BIT", debug)
def GetDataTypeRange(self, typename, debug = False):
range = DataTypeRange.get(typename)
@@ -1837,7 +1837,7 @@
elif basetype_content_type == "derived":
return self.GetDataTypeRange(basetype_content.getname(), debug)
def GetSubrangeBaseTypes(self, exclude, debug = False):
subrange_basetypes = DataTypeRange.keys()
@@ -1851,7 +1851,7 @@
[datatype.getname() for datatype in confnodetype["types"].getdataTypes()
if self.IsSubrangeType(datatype.getname(), exclude, debug)])
return subrange_basetypes
# Return Enumerated Values
def GetEnumeratedDataValues(self, typename = None, debug = False):
@@ -1861,7 +1861,7 @@
basetype_content = datatype_obj.baseType.getcontent()
basetype_content_type = basetype_content.getLocalTag()
if basetype_content_type == "enum":
- return [value.getname()
+ return [value.getname() for value in basetype_content.xpath(
namespaces=PLCOpenParser.NSMAP)]
@@ -1878,19 +1878,19 @@
#-------------------------------------------------------------------------------
# Project Element tag name computation functions
#-------------------------------------------------------------------------------
# Compute a data type name
def ComputeDataTypeName(self, datatype):
return "D::%s" % datatype
def ComputePouName(self, pou):
# Compute a pou transition name
def ComputePouTransitionName(self, pou, transition):
return "T::%s::%s" % (pou, transition)
# Compute a pou action name
def ComputePouActionName(self, pou, action):
return "A::%s::%s" % (pou, action)
@@ -1905,7 +1905,7 @@
def GetElementType(self, tagname):
words = tagname.split("::")
- return {"D" : ITEM_DATATYPE, "P" : ITEM_POU,
+ return {"D" : ITEM_DATATYPE, "P" : ITEM_POU, "T" : ITEM_TRANSITION, "A" : ITEM_ACTION,
"C" : ITEM_CONFIGURATION, "R" : ITEM_RESOURCE}[words[0]]
@@ -1963,7 +1963,7 @@
dimensions.append((dimension.getlower(), dimension.getupper()))
base_type = element_type.baseType.getcontent()
base_type_type = element_type.getLocalTag()
- element_infos["Type"] = ("array",
+ element_infos["Type"] = ("array", if base_type_type == "derived"
else base_type_type.upper(), dimensions)
@@ -1981,14 +1981,14 @@
infos["base_type"] = (basetype_content.getname()
if basetype_content_type == "derived"
else basetype_content_type.upper())
if datatype.initialValue is not None:
infos["initial"] = str(datatype.initialValue.getvalue())
# Change the data type informations
def SetDataTypeInfos(self, tagname, infos):
words = tagname.split("::")
@@ -2006,7 +2006,7 @@
datatype.baseType.setcontent(derived_datatype)
elif infos["type"] == "Subrange":
subrange = PLCOpenParser.CreateElement(
if infos["base_type"] in GetSubTypes("ANY_UINT")
else "subrangeSigned", "dataType")
datatype.baseType.setcontent(subrange)
@@ -2106,7 +2106,7 @@
datatype.initialValue = None
#-------------------------------------------------------------------------------
# Project opened Pous management functions
#-------------------------------------------------------------------------------
@@ -2132,7 +2132,7 @@
return project.getconfigurationResource(words[1], words[2])
# Return edited element name
def GetEditedElementName(self, tagname):
words = tagname.split("::")
@@ -2141,7 +2141,7 @@
# Return edited element name and type
def GetEditedElementType(self, tagname, debug = False):
words = tagname.split("::")
@@ -2183,14 +2183,14 @@
# Change the edited element text
def SetEditedElementText(self, tagname, text):
if self.Project is not None:
element = self.GetEditedElement(tagname)
# Return the edited element text
def GetEditedElementText(self, tagname, debug = False):
element = self.GetEditedElement(tagname, debug)
@@ -2236,13 +2236,13 @@
return element.tostring()
def GetEditedElementInstancesCopy(self, tagname, blocks_id = None, wires = None, debug = False):
element = self.GetEditedElement(tagname, debug)
- wires = dict([(wire, True)
+ wires = dict([(wire, True) if wire[0] in blocks_id and wire[1] in blocks_id])
copy_body = PLCOpenParser.CreateElement("body", "pou")
element.append(copy_body)
@@ -2257,20 +2257,20 @@
text += instance_copy.tostring()
element.remove(copy_body)
def GenerateNewName(self, tagname, name, format, start_idx=0, exclude={}, debug=False):
- names.update(dict([(varname.upper(), True)
+ names.update(dict([(varname.upper(), True) for varname in self.GetEditedElementVariables(tagname, debug)]))
words = tagname.split("::")
if words[0] in ["P","T","A"]:
element = self.GetEditedElement(tagname, debug)
if element is not None and element.getbodyType() not in ["ST", "IL"]:
for instance in element.getinstances():
- if isinstance(instance,
- (PLCOpenParser.GetElementClass("step", "sfcObjects"),
- PLCOpenParser.GetElementClass("connector", "commonObjects"),
+ if isinstance(instance, + (PLCOpenParser.GetElementClass("step", "sfcObjects"), + PLCOpenParser.GetElementClass("connector", "commonObjects"), PLCOpenParser.GetElementClass("continuation", "commonObjects"))):
names[instance.getname().upper()] = True
@@ -2290,37 +2290,37 @@
names[config.getname().upper()] = True
for resource in config.getresource():
names[resource.getname().upper()] = True
while name is None or names.get(name.upper(), False):
def PasteEditedElementInstances(self, tagname, text, new_pos, middle=False, debug=False):
element = self.GetEditedElement(tagname, debug)
element_name, element_type = self.GetEditedElementType(tagname, debug)
bodytype = element.getbodyType()
# Get edited element type scaling
project = self.GetProject(debug)
properties = project.getcontentHeader()
scaling = properties["scaling"][bodytype]
# Get ids already by all the instances in edited element
used_id = dict([(instance.getlocalId(), True) for instance in element.getinstances()])
instances, error = LoadPouInstances(text.encode("utf-8"), bodytype)
instances, error = [], ""
if error is not None or len(instances) == 0:
return _("Invalid plcopen element(s)!!!")
for instance in instances:
element.addinstance(instance)
@@ -2332,25 +2332,25 @@
if blocktype_infos["type"] != "function" and blockname is not None:
if element_type == "function":
return _("FunctionBlock \"%s\" can't be pasted in a Function!!!")%blocktype
- blockname = self.GenerateNewName(tagname,
+ blockname = self.GenerateNewName(tagname, exclude[blockname] = True
instance.setinstanceName(blockname)
self.AddEditedElementPouVar(tagname, blocktype, blockname)
elif instance_type == "step":
- stepname = self.GenerateNewName(tagname,
+ stepname = self.GenerateNewName(tagname, instance.setname(stepname)
localid = instance.getlocalId()
if not used_id.has_key(localid):
@@ -2363,7 +2363,7 @@
translate_id[localId] = idx
x, y, width, height = bbox.bounding_box()
@@ -2385,43 +2385,43 @@
new_pos = (max(30, new_pos[0]), max(30, new_pos[1]))
diff = (int(new_pos[0] - x), int(new_pos[1] - y))
for instance in instances:
connections.update(instance.updateConnectionsId(translate_id))
if getattr(instance, "setexecutionOrderId", None) is not None:
instance.setexecutionOrderId(0)
instance.translate(*diff)
return new_id, connections
def GetEditedElementInstancesInfos(self, tagname, debug = False):
element_instances = OrderedDict()
element = self.GetEditedElement(tagname, debug)
factory = BlockInstanceFactory(element_instances)
pou_block_instances_xslt_tree = etree.XSLT(
- pou_block_instances_xslt,
+ pou_block_instances_xslt, ("pou_block_instances_ns", name): getattr(factory, name)
for name in ["AddBlockInstance", "SetSpecificValues",
"AddInstanceConnection", "AddConnectionLink",
"AddLinkPoint", "AddAction"]})
pou_block_instances_xslt_tree(element)
def ClearEditedElementExecutionOrder(self, tagname):
element = self.GetEditedElement(tagname)
element.resetexecutionOrder()
def ResetEditedElementExecutionOrder(self, tagname):
element = self.GetEditedElement(tagname)
element.compileexecutionOrder()
def SetConnectionWires(self, connection, connector):
wires = connector.GetWires()
@@ -2443,7 +2443,7 @@
connection.setconnectionParameter(idx, None)
def GetVarTypeObject(self, var_type):
var_type_obj = PLCOpenParser.CreateElement("type", "variable")
if not var_type.startswith("ANY") and TypeHierarchy.get(var_type):
@@ -2455,7 +2455,7 @@
derived_type.setname(var_type)
var_type_obj.setcontent(derived_type)
def AddEditedElementPouVar(self, tagname, var_type, name,**args):
if self.Project is not None:
words = tagname.split("::")
@@ -2463,9 +2463,9 @@
pou = self.Project.getpou(words[1])
- self.GetVarTypeObject(var_type),
+ self.GetVarTypeObject(var_type),
def AddEditedElementPouExternalVar(self, tagname, var_type, name):
if self.Project is not None:
words = tagname.split("::")
@@ -2474,7 +2474,7 @@
self.GetVarTypeObject(var_type), name)
def ChangeEditedElementPouVar(self, tagname, old_type, old_name, new_type, new_name):
if self.Project is not None:
words = tagname.split("::")
@@ -2482,7 +2482,7 @@
pou = self.Project.getpou(words[1])
pou.changepouVar(old_type, old_name, new_type, new_name)
def RemoveEditedElementPouVar(self, tagname, type, name):
if self.Project is not None:
words = tagname.split("::")
@@ -2490,7 +2490,7 @@
pou = self.Project.getpou(words[1])
pou.removepouVar(type, name)
def AddEditedElementBlock(self, tagname, id, blocktype, blockname = None):
element = self.GetEditedElement(tagname)
@@ -2502,13 +2502,13 @@
block.setinstanceName(blockname)
self.AddEditedElementPouVar(tagname, blocktype, blockname)
element.addinstance(block)
def SetEditedElementBlockInfos(self, tagname, id, infos):
element = self.GetEditedElement(tagname)
block = element.getinstance(id)
old_name = block.getinstanceName()
old_type = block.gettypeName()
new_name = infos.get("name", old_name)
@@ -2571,7 +2571,7 @@
variable.addconnectionPointOut()
variable.connectionPointOut.setrelPositionXY(position.x, position.y)
def AddEditedElementVariable(self, tagname, id, var_type):
element = self.GetEditedElement(tagname)
@@ -2581,13 +2581,13 @@
INOUT: "inOutVariable"}[var_type], "fbdObjects")
element.addinstance(variable)
def SetEditedElementVariableInfos(self, tagname, id, infos):
element = self.GetEditedElement(tagname)
variable = element.getinstance(id)
for param, value in infos.items():
variable.setexpression(value)
@@ -2634,7 +2634,7 @@
CONTINUATION: "continuation"}[connection_type], "commonObjects")
connection.setlocalId(id)
element.addinstance(connection)
def SetEditedElementConnectionInfos(self, tagname, id, infos):
element = self.GetEditedElement(tagname)
@@ -2643,7 +2643,7 @@
for param, value in infos.items():
- connection.setname(value)
+ connection.setname(value) connection.setheight(value)
@@ -2668,7 +2668,7 @@
comment = PLCOpenParser.CreateElement("comment", "commonObjects")
element.addinstance(comment)
def SetEditedElementCommentInfos(self, tagname, id, infos):
element = self.GetEditedElement(tagname)
@@ -2693,7 +2693,7 @@
RIGHTRAIL: "rightPowerRail"}[powerrail_type], "ldObjects")
element.addinstance(powerrail)
def SetEditedElementPowerRailInfos(self, tagname, id, infos):
element = self.GetEditedElement(tagname)
@@ -2725,7 +2725,7 @@
powerrail.appendconnectionPointIn(connection)
connection.setrelPositionXY(position.x, position.y)
self.SetConnectionWires(connection, connector)
def AddEditedElementContact(self, tagname, id):
element = self.GetEditedElement(tagname)
@@ -2821,7 +2821,7 @@
step = PLCOpenParser.CreateElement("step", "sfcObjects")
element.addinstance(step)
def SetEditedElementStepInfos(self, tagname, id, infos):
element = self.GetEditedElement(tagname)
@@ -2864,14 +2864,14 @@
step.connectionPointOutAction.setrelPositionXY(position.x, position.y)
step.deleteconnectionPointOutAction()
def AddEditedElementTransition(self, tagname, id):
element = self.GetEditedElement(tagname)
transition = PLCOpenParser.CreateElement("transition", "sfcObjects")
transition.setlocalId(id)
element.addinstance(transition)
def SetEditedElementTransitionInfos(self, tagname, id, infos):
element = self.GetEditedElement(tagname)
@@ -2907,7 +2907,7 @@
elif infos.get("type", None) == "connection" and param == "connection" and value:
transition.setconditionContent("connection", None)
self.SetConnectionWires(transition.condition.content, value)
def AddEditedElementDivergence(self, tagname, id, divergence_type):
element = self.GetEditedElement(tagname)
@@ -2919,19 +2919,19 @@
divergence_type), "sfcObjects")
divergence.setlocalId(id)
element.addinstance(divergence)
PLCOpenParser.GetElementClass(divergence_type, "sfcObjects"))
for divergence_type in ["selectionDivergence", "simultaneousDivergence",
"selectionConvergence", "simultaneousConvergence"]]
def GetDivergenceType(self, divergence):
for divergence_type, divergence_class in self.DivergenceTypes:
if isinstance(divergence, divergence_class):
def SetEditedElementDivergenceInfos(self, tagname, id, infos):
element = self.GetEditedElement(tagname)
@@ -2975,14 +2975,14 @@
connection = PLCOpenParser.CreateElement("connectionPointOut", divergence_type)
divergence.appendconnectionPointOut(connection)
connection.setrelPositionXY(position.x, position.y)
def AddEditedElementJump(self, tagname, id):
element = self.GetEditedElement(tagname)
jump = PLCOpenParser.CreateElement("jumpStep", "sfcObjects")
element.addinstance(jump)
def SetEditedElementJumpInfos(self, tagname, id, infos):
element = self.GetEditedElement(tagname)
@@ -3005,14 +3005,14 @@
jump.addconnectionPointIn()
jump.connectionPointIn.setrelPositionXY(position.x, position.y)
self.SetConnectionWires(jump.connectionPointIn, value)
def AddEditedElementActionBlock(self, tagname, id):
element = self.GetEditedElement(tagname)
actionBlock = PLCOpenParser.CreateElement("actionBlock", "commonObjects")
actionBlock.setlocalId(id)
element.addinstance(actionBlock)
def SetEditedElementActionBlockInfos(self, tagname, id, infos):
element = self.GetEditedElement(tagname)
@@ -3035,7 +3035,7 @@
actionBlock.addconnectionPointIn()
actionBlock.connectionPointIn.setrelPositionXY(position.x, position.y)
self.SetConnectionWires(actionBlock.connectionPointIn, value)
def RemoveEditedElementInstance(self, tagname, id):
element = self.GetEditedElement(tagname)
@@ -3162,19 +3162,19 @@
self.CurrentElementEditing = None
def SaveXMLFile(self, filepath = None):
if not filepath and self.FilePath == "":
contentheader = {"modificationDateTime": datetime.datetime(*localtime()[:6])}
self.Project.setcontentHeader(contentheader)
SaveProject(self.Project, filepath)
SaveProject(self.Project, self.FilePath)
self.MarkProjectAsSaved()
self.SetFilePath(filepath)
@@ -3233,7 +3233,7 @@
self.ProjectSaved = False
if self.ProjectBuffer is not None and self.Buffering:
self.ProjectBuffer.Buffering(PLCOpenParser.Dumps(self.Project))
@@ -3245,7 +3245,7 @@
self.ProjectBuffer.CurrentSaved()
# Return if project is saved
def ProjectIsSaved(self):
if self.ProjectBuffer is not None:
@@ -3257,11 +3257,11 @@
if self.ProjectBuffer is not None:
self.Project = PLCOpenParser.Loads(self.ProjectBuffer.Previous())
if self.ProjectBuffer is not None:
self.Project = PLCOpenParser.Loads(self.ProjectBuffer.Next())
def GetBufferState(self):
if self.ProjectBuffer is not None:
first = self.ProjectBuffer.IsFirst() and not self.Buffering