1.总体结构
2.PLCControler详解
3.XML到ST的过程详解
4.生成POU程序的过程
.
5.细节分析
- 总体代码
controller = PLCControler() # 生成PLCControler对象
controller.OpenXMLFile(args.infilename, args.xmlstring) # 打开XML文件
programs, errors, warnings=controller.GenerateProgram() # 生成ST程序
- 关键函数
class PLCControler(object):
"""
Controler for PLCOpenEditor
Class which controls the operations made on the plcopen model and answers to view requests
"""
# Create a new PLCControler
def __init__(self):
self.LastNewIndex = 0 # 初始化一个计数器,用于生成默认的项目元素名称
self.SortAlphaNumeric = False # 初始化一个标志位,表示是否将项目元素按字母顺序排序
self.Reset() # 调用Reset()方法重置内部变量
self.InstancesPathCollector = InstancesPathCollector(self) # 初始化InstancesPathCollector收集器对象,用于收集实例的路径
self.POUVariablesCollector = POUVariablesCollector(self) # 初始化POUVariablesCollector收集器对象,用于收集POU的变量
self.InstanceTagnameCollector = InstanceTagnameCollector(self) # 初始化InstanceTagnameCollector收集器对象,用于收集实例的标签名
self.BlockInstanceCollector = BlockInstanceCollector(self) # 初始化BlockInstanceCollector收集器对象,用于收集块的实例信息
self.VariableInfoCollector = VariableInfoCollector(self) # 初始化VariableInfoCollector收集器对象,用于收集变量信息
def GenerateProgram(self, filepath=None): # 生成PLC代码的核心方法,调用生成器类
errors = []
warnings = []
# project是xml树的根节点, 可以采用 type(project) 和 dir(project)查看有那些方法
if self.Project is not None:
try:
# 调用GenerateCurrentProgram,传入控制器self、项目模型和错误列表,生成PLC代码。GenerateCurrentProgram会调用ProgramGenerator进行逐步代码生成
self.ProgramChunks = GenerateCurrentProgram(self, self.Project, errors, warnings)
# 将当前项目模型复制到self.NextCompiledProject,用于下载代码后更新模型
self.NextCompiledProject = self.Copy(self.Project)
program_text = "".join([item[0] for item in self.ProgramChunks]) # 将代码片段拼接成字符串program_text
if filepath is not None:
programfile = open(filepath, "w")
programfile.write(program_text.encode("utf-8"))
programfile.close()
self.ProgramFilePath = filepath
return program_text, errors, warnings
except PLCGenException as ex:
#import traceback
#traceback.print_exc()
#把错误日志打印出来, 很关键
print(ex)
errors.append(ex)
sys.exit(1)
else:
errors.append("No project opened")
return "", errors, warnings
def GenerateCurrentProgram(controler, project, errors, warnings):
# 创建ProgramGenerator实例generator,并传入控制器controler、项目project、错误列表errors和警告列表warnings
generator = ProgramGenerator(controler, project, errors, warnings)
if hasattr(controler, "logger"):
def log(txt):
sys.stdout.write(" "+txt+"\n")
#controler.logger.write(" "+txt+"\n")
else:
def log(txt):
sys.stdout.write(" " + txt + "\n")
pass
# 调用generator的GenerateProgram方法生成代码,并传入log方法,以便生成过程中输出日志
generator.GenerateProgram(log)
return generator.GetGeneratedProgram() # 生成完成后,调用generator的GetGeneratedProgram方法获取生成的代码
class ProgramGenerator(object):
# Create a new PCL program generator
def __init__(self, controler, project, errors, warnings):
# Keep reference of the controler and project
self.Controler = controler
#project是xml节点的根节点
#project有这些方法
# GetEnumeratedDataTypeValues、getElementAttributes、getElementInfos、getLocalTag、getconfiguration、getconfigurationResource、getcontentHeader
# getdataTypes、getfileHeader、getpous
self.Project = project
# Reset the internal variables used to generate PLC programs
self.Program = []
self.DatatypeComputed = {}
self.PouComputed = {}
self.Errors = errors
self.Warnings = warnings
# Generate the entire program for current project
def GenerateProgram(self, log):
#print("===Collecting data types")
# Find all data types defined
# 生成 project节点下的数据类型
for datatype in self.Project.getdataTypes():
self.DatatypeComputed[datatype.getname()] = False
#print("===Collecting POUs")
# Find all data types defined
for pou in self.Project.getpous():
self.PouComputed[pou.getname()] = False
# Generate data type declaration structure if there is at least one data
# type defined
if len(self.DatatypeComputed) > 0:
self.Program += [("TYPE\n", ())]
# Generate every data types defined
for datatype_name in self.DatatypeComputed.keys():
#log("->Generate Data Type %s"%datatype_name)
self.GenerateDataType(datatype_name)
self.Program += [("END_TYPE\n\n", ())]
# Generate every POUs defined
# 生成 project下的 pou
for pou_name in self.PouComputed.keys():
#print(" ->Generate POU %s"%pou_name)
self.GeneratePouProgram(pou_name)
# Generate every configurations defined
#print("===Generate Config(s)")
for config in self.Project.getconfigurations():
self.Program += self.GenerateConfiguration(config)
# Generate a data type from its name
def GenerateDataType(self, datatype_name):
# Verify that data type hasn't been generated yet
if not self.DatatypeComputed.get(datatype_name, True):
# If not mark data type as computed
self.DatatypeComputed[datatype_name] = True
# Getting datatype model from project
datatype = self.Project.getdataType(datatype_name)
tagname = ComputeDataTypeName(datatype.getname())
datatype_def = [(" ", ()),
(datatype.getname(), (tagname, "name")),
(" : ", ())]
basetype_content = datatype.baseType.getcontent()
basetype_content_type = basetype_content.getLocalTag()
# Data type derived directly from a user defined type
if basetype_content_type == "derived":
basetype_name = basetype_content.getname()
self.GenerateDataType(basetype_name)
datatype_def += [(basetype_name, (tagname, "base"))]
# Data type is a subrange
elif basetype_content_type in ["subrangeSigned", "subrangeUnsigned"]:
base_type = basetype_content.baseType.getcontent()
base_type_type = base_type.getLocalTag()
# Subrange derived directly from a user defined type
if base_type_type == "derived":
basetype_name = base_type_type.getname()
self.GenerateDataType(basetype_name)
# Subrange derived directly from an elementary type
else:
basetype_name = base_type_type
min_value = basetype_content.range.getlower()
max_value = basetype_content.range.getupper()
datatype_def += [(basetype_name, (tagname, "base")),
(" (", ()),
("%s" % min_value, (tagname, "lower")),
("..", ()),
("%s" % max_value, (tagname, "upper")),
(")", ())]
# Generate a POU from its name
def GeneratePouProgram(self, pou_name):
# Verify that POU hasn't been generated yet
if not self.PouComputed.get(pou_name, True):
# If not mark POU as computed
self.PouComputed[pou_name] = True
# Getting POU model from project
pou = self.Project.getpou(pou_name)
pou_type = pou.getpouType()
# Verify that POU type exists
if pou_type in pouTypeNames:
# Create a POU program generator
pou_program = PouProgramGenerator(self, pou.getname(), pouTypeNames[pou_type], self.Errors, self.Warnings)
program = pou_program.GenerateProgram(pou) # 调用PouProgramGenerator的GenerateProgram方法生成该POU的代码program
self.Program += program
else:
raise PLCGenException(_("Undefined pou type \"%s\"") % pou_type)
class PouProgramGenerator(object):
# Create a new POU program generator
def __init__(self, parent, name, type, errors, warnings):
# Keep Reference to the parent generator
self.ParentGenerator = parent
self.Name = name
self.Type = type
self.TagName = ComputePouName(name)
self.CurrentIndent = " "
self.ReturnType = None
self.Interface = []
self.InitialSteps = []
self.ComputedBlocks = {}
self.ComputedConnectors = {}
self.ConnectionTypes = {}
self.RelatedConnections = []
self.SFCNetworks = {"Steps": {}, "Transitions": {}, "Actions": {}}
self.SFCComputedBlocks = []
self.ActionNumber = 0
self.Program = []
self.Errors = errors
self.Warnings = warnings
def GenerateProgram(self, pou):
# xml 中的pou节点 类型是 <class 'xmlclass.xmlclass.pou'>
#print("\n".join(dir(pou)))
# 获取POU的输入输出
pouname = pou.getname()
#print(" ------pou[%s] interface-------"%pouname)
self.ComputeInterface(pou) # 调用ComputeInterface解析POU的接口变量
self.ComputeConnectionTypes(pou) # 调用ComputeConnectionTypes分析POU中的连接
#print(" ------pou[%s] body-------" % pouname)
self.ComputeProgram(pou) # 调用ComputeProgram生成POU的主体代码
program = [("%s " % self.Type, ()),
(self.Name, (self.TagName, "name"))]
if self.ReturnType is not None:
program += [(" : ", ()),
(self.ReturnType, (self.TagName, "return"))]
program += [("\n", ())]
if len(self.Interface) == 0:
raise PLCGenException(_("No variable defined in \"%s\" POU") % self.Name)
if len(self.Program) == 0:
raise PLCGenException(_("No body defined in \"%s\" POU") % self.Name)
var_number = 0
# 遍历接口变量,生成变量声明代码
for list_type, option, _located, variables in self.Interface:
variable_type = errorVarTypes.get(list_type, "var_local")
program += [(" %s" % list_type, ())]
if option is not None:
program += [(" %s" % option, (self.TagName, variable_type, (var_number, var_number + len(variables)), option.lower()))]
program += [("\n", ())]
# 为每个变量生成声明语句,包含类型、名称、地址、初始值等
for var_type, var_name, var_address, var_initial in variables:
program += [(" ", ())]
if var_name:
program += [(var_name, (self.TagName, variable_type, var_number, "name")),
(" ", ())]
if var_address is not None:
program += [("AT ", ()),
(var_address, (self.TagName, variable_type, var_number, "location")),
(" ", ())]
program += [(": ", ()),
(var_type, (self.TagName, variable_type, var_number, "type"))]
if var_initial is not None:
program += [(" := ", ()),
(self.ParentGenerator.ComputeValue(var_initial, var_type), (self.TagName, variable_type, var_number, "initial value"))]
program += [(";\n", ())]
var_number += 1
program += [(" END_VAR\n", ())]
program += [("\n", ())]
program += self.Program # 与POU的主体代码合并
program += [("END_%s\n\n" % self.Type, ())]
return program
def ComputeProgram(self, pou):
pouname = pou.getname()
body = pou.getbody()
if isinstance(body, list):
body = body[0]
body_content = body.getcontent()
body_type = body_content.getLocalTag()
if body_type in ["IL", "ST"]:
text = body_content.getanyText()
#print(text)
self.ParentGenerator.GeneratePouProgramInText(text.upper())
self.Program = [(ReIndentText(text, len(self.CurrentIndent)),
(self.TagName, "body", len(self.CurrentIndent)))]
## SFC的 POU程序
elif body_type == "SFC":
self.IndentRight()
for instance in body.getcontentInstances():
if isinstance(instance, StepClass):
self.GenerateSFCStep(instance, pou)
elif isinstance(instance, ActionBlockClass):
self.GenerateSFCStepActions(instance, pou)
elif isinstance(instance, TransitionClass):
self.GenerateSFCTransition(instance, pou)
elif isinstance(instance, JumpStepClass):
self.GenerateSFCJump(instance, pou)
if len(self.InitialSteps) > 0 and len(self.SFCComputedBlocks) > 0:
action_name = "COMPUTE_FUNCTION_BLOCKS"
action_infos = {"qualifier": "S", "content": action_name}
self.SFCNetworks["Steps"][self.InitialSteps[0]]["actions"].append(action_infos)
self.SFCNetworks["Actions"][action_name] = (self.SFCComputedBlocks, ())
self.Program = []
self.IndentLeft()
for initialstep in self.InitialSteps:
self.ComputeSFCStep(initialstep)
else:
otherInstances = {"outVariables&coils": [], "blocks": [], "connectors": []}
orderedInstances = []
for instance in body.getcontentInstances():
if isinstance(instance, (OutVariableClass, InOutVariableClass, BlockClass)):
executionOrderId = instance.getexecutionOrderId()
if executionOrderId > 0:
orderedInstances.append((executionOrderId, instance))
elif isinstance(instance, (OutVariableClass, InOutVariableClass)):
otherInstances["outVariables&coils"].append(instance)
elif isinstance(instance, BlockClass):
otherInstances["blocks"].append(instance)
elif isinstance(instance, ConnectorClass):
otherInstances["connectors"].append(instance)
elif isinstance(instance, CoilClass):
otherInstances["outVariables&coils"].append(instance)
orderedInstances.sort()
otherInstances["outVariables&coils"].sort(SortInstances)
otherInstances["blocks"].sort(SortInstances)
instances = [instance for (executionOrderId, instance) in orderedInstances]
instances.extend(otherInstances["outVariables&coils"] + otherInstances["blocks"] + otherInstances["connectors"])
for instance in instances:
if isinstance(instance, (OutVariableClass, InOutVariableClass)):
connections = instance.connectionPointIn.getconnections()
if connections is not None:
expression = self.ComputeExpression(body, connections)
if expression is not None:
eno_var = self.GetUsedEno(body, connections)
if eno_var is not None:
self.Program += [(self.CurrentIndent + "IF %s" % eno_var, ())]
self.Program += [(" THEN\n ", ())]
self.IndentRight()
self.Program += [(self.CurrentIndent, ()),
(instance.getexpression(), (self.TagName, "io_variable", instance.getlocalId(), "expression")),
(" := ", ())]
self.Program += expression
self.Program += [(";\n", ())]
if eno_var is not None:
self.IndentLeft()
self.Program += [(self.CurrentIndent + "END_IF;\n", ())]
elif isinstance(instance, BlockClass):
block_type = instance.gettypeName()
self.ParentGenerator.GeneratePouProgram(block_type)
block_infos = self.GetBlockType(block_type, tuple([self.ConnectionTypes.get(variable.connectionPointIn, "ANY") for variable in instance.inputVariables.getvariable() if variable.getformalParameter() != "EN"]))
if block_infos is None:
block_infos = self.GetBlockType(block_type)
if block_infos is None:
raise PLCGenException(
_("Undefined block type \"{a1}\" in \"{a2}\" POU").
format(a1=block_type, a2=self.Name))
try:
self.GenerateBlock(instance, block_infos, body, None)
except ValueError as e:
raise PLCGenException(str(e))
elif isinstance(instance, ConnectorClass):
connector = instance.getname()
if self.ComputedConnectors.get(connector, None):
continue
expression = self.ComputeExpression(body, instance.connectionPointIn.getconnections())
if expression is not None:
self.ComputedConnectors[connector] = expression
elif isinstance(instance, CoilClass):
connections = instance.connectionPointIn.getconnections()
if connections is not None:
coil_info = (self.TagName, "coil", instance.getlocalId())
expression = self.ComputeExpression(body, connections)
if expression is not None:
expression = self.ExtractModifier(instance, expression, coil_info)
self.Program += [(self.CurrentIndent, ())]
self.Program += [(instance.getvariable(), coil_info + ("reference",))]
self.Program += [(" := ", ())] + expression + [(";\n", ())]
# 生成POU中SFC部分的代码
def ComputeSFCStep(self, step_name):
if step_name in self.SFCNetworks["Steps"].keys():
step_infos = self.SFCNetworks["Steps"].pop(step_name)
self.Program += [(self.CurrentIndent, ())]
if step_infos["initial"]:
self.Program += [("INITIAL_", ())]
self.Program += [("STEP ", ()),
(step_name, (self.TagName, "step", step_infos["id"], "name")),
(":\n", ())]
actions = []
self.IndentRight()
for action_infos in step_infos["actions"]:
if action_infos.get("id", None) is not None:
action_info = (self.TagName, "action_block", action_infos["id"], "action", action_infos["num"])
else:
action_info = ()
actions.append(action_infos["content"])
self.Program += [(self.CurrentIndent, ()),
(action_infos["content"], action_info + ("reference",)),
("(", ()),
(action_infos["qualifier"], action_info + ("qualifier",))]
if "duration" in action_infos:
self.Program += [(", ", ()),
(action_infos["duration"], action_info + ("duration",))]
if "indicator" in action_infos:
self.Program += [(", ", ()),
(action_infos["indicator"], action_info + ("indicator",))]
self.Program += [(");\n", ())]
self.IndentLeft()
self.Program += [("%sEND_STEP\n\n" % self.CurrentIndent, ())]
for action in actions:
self.ComputeSFCAction(action)
for transition in step_infos["transitions"]:
self.ComputeSFCTransition(transition)
def ComputeSFCAction(self, action_name):
if action_name in self.SFCNetworks["Actions"].keys():
action_content, action_info = self.SFCNetworks["Actions"].pop(action_name)
self.Program += [("%sACTION " % self.CurrentIndent, ()),
(action_name, action_info),
(":\n", ())]
self.Program += action_content
self.Program += [("%sEND_ACTION\n\n" % self.CurrentIndent, ())]
def ComputeSFCTransition(self, transition):
if transition in self.SFCNetworks["Transitions"].keys():
transition_infos = self.SFCNetworks["Transitions"].pop(transition)
self.Program += [("%sTRANSITION" % self.CurrentIndent, ())]
if transition_infos["priority"] is not None:
self.Program += [(" (PRIORITY := ", ()),
("%d" % transition_infos["priority"], (self.TagName, "transition", transition_infos["id"], "priority")),
(")", ())]
self.Program += [(" FROM ", ())]
if len(transition_infos["from"]) > 1:
self.Program += [("(", ())]
self.Program += JoinList([(", ", ())], transition_infos["from"])
self.Program += [(")", ())]
elif len(transition_infos["from"]) == 1:
self.Program += transition_infos["from"][0]
else:
raise PLCGenException(
_("Transition with content \"{a1}\" not connected to a previous step in \"{a2}\" POU").
format(a1=transition_infos["content"], a2=self.Name))
self.Program += [(" TO ", ())]
if len(transition_infos["to"]) > 1:
self.Program += [("(", ())]
self.Program += JoinList([(", ", ())], transition_infos["to"])
self.Program += [(")", ())]
elif len(transition_infos["to"]) == 1:
self.Program += transition_infos["to"][0]
else:
raise PLCGenException(
_("Transition with content \"{a1}\" not connected to a next step in \"{a2}\" POU").
format(a1=transition_infos["content"], a2=self.Name))
self.Program += transition_infos["content"]
self.Program += [("%sEND_TRANSITION\n\n" % self.CurrentIndent, ())]
for [(step_name, _step_infos)] in transition_infos["to"]:
self.ComputeSFCStep(step_name)
def GetBlockType(self, type, inputs=None):
return self.ParentGenerator.Controler.GetBlockType(type, inputs)
* * * * * * * * * * * * * * * * * *
# self.ProgramChunks的值
[('PROGRAM ', ()), ('PLC_PRG', ('P::PLC_PRG', 'name')), ('\n', ()), (' VAR', ()), (' RETAIN', ('P::PLC_PRG', 'var_local', (0, 1), 'retain')), ('\n', ()), (' ', ()), ('local1', ('P::PLC_PRG', 'var_local', 0, 'name')), (' ', ()), (': ', ()), (u'BOOL', ('P::PLC_PRG', 'var_local', 0, 'type')), (' := ', ()), ('true', ('P::PLC_PRG', 'var_local', 0, 'initial value')), (';\n', ()), (' END_VAR\n', ()), (' VAR', ()), ('\n', ()), (' ', ()), ('local2', ('P::PLC_PRG', 'var_local', 1, 'name')), (' ', ()), (': ', ()), (u'BOOL', ('P::PLC_PRG', 'var_local', 1, 'type')), (' := ', ()), ('true', ('P::PLC_PRG', 'var_local', 1, 'initial value')), (';\n', ()), (' END_VAR\n', ()), ('\n', ()), (' local2 := TRUE;\n local1 := FALSE;\n', ('P::PLC_PRG', 'body', 2)), ('END_PROGRAM\n\n', ()), ('\nCONFIGURATION ', ()), ('Config0', ('C::Config0', 'name')), ('\n', ()), ('\n RESOURCE ', ()), ('Res0', ('R::Config0::Res0', 'name')), (' ON PLC\n', ()), (' TASK ', ()), ('task0', ('R::Config0::Res0', 'task', 0, 'name')), ('(', ()), ('INTERVAL := ', ()), ('T#20ms', ('R::Config0::Res0', 'task', 0, 'interval')), (',', ()), ('PRIORITY := ', ()), ('0', ('R::Config0::Res0', 'task', 0, 'priority')), (');\n', ()), (' PROGRAM ', ()), ('instance0', ('R::Config0::Res0', 'instance', 0, 'name')), (' WITH ', ()), ('task0', ('R::Config0::Res0', 'instance', 0, 'task')), (' : ', ()), ('PLC_PRG', ('R::Config0::Res0', 'instance', 0, 'type')), (';\n', ()), (' END_RESOURCE\n', ()), ('END_CONFIGURATION\n', ())]
# POU生成的SFC主体代码self.Program的值
[(' ', ()), ('INITIAL_', ()), ('STEP ', ()), ('Init', ('P::aaaa', 'step', 0, 'name')), (':\n', ()), (' END_STEP\n\n', ()), (' TRANSITION', ()), (' (PRIORITY := ', ()), ('0', ('P::aaaa', 'transition', 3, 'priority')), (')', ()), (' FROM ', ()), ('Init', ('P::aaaa', 'transition', 3, 'from', 0)), (' TO ', ()), ('Step0', ('P::aaaa', 'transition', 3, 'to', 4)), ('\n := ', ()), ('a', ('P::aaaa', 'io_variable', 2, 'expression')), (';\n', ()), (' END_TRANSITION\n\n', ()), (' ', ()), ('STEP ', ()), ('Step0', ('P::aaaa', 'step', 4, 'name')), (':\n', ()), (' END_STEP\n\n', ()), (' TRANSITION', ()), (' (PRIORITY := ', ()), ('0', ('P::aaaa', 'transition', 7, 'priority')), (')', ()), (' FROM ', ()), ('Step0', ('P::aaaa', 'transition', 7, 'from', 4)), (' TO ', ()), ('Init', ('P::aaaa', 'jump', 8, 'target')), ('\n := ', ()), ('a', ('P::aaaa', 'io_variable', 6, 'expression')), (';\n', ()), (' END_TRANSITION\n\n', ())]
# program_text的值
PROGRAM PLC_PRG
VAR RETAIN
local1 : BOOL := true;
END_VAR
VAR
local2 : BOOL := true;
END_VAR
local2 := TRUE;
local1 := FALSE;
END_PROGRAM
CONFIGURATION Config0
RESOURCE Res0 ON PLC
TASK task0(INTERVAL := T#20ms,PRIORITY := 0);
PROGRAM instance0 WITH task0 : PLC_PRG;
END_RESOURCE
END_CONFIGURATION
6.代码运行
python2 PLCControler.py -i /home/zwx/Program/xml2st/plc.xml -o zwxhhh.st
python2 -m pdb PLCControler.py -i /home/zwx/Program/xml2st/plc.xml
7.代码错误提示
- PLCGenerator.py中定义了PLCGenException类,用于抛出PLC代码生成过程中的异常。 当代码生成过程中遇到错误时,可以raise PLCGenException来抛出异常并中断代码生成过程
- 在PouProgramGenerator的GenerateBlock方法中,会根据函数块的输入和输出变量类型进行检查,如果不匹配会抛出ValueError异常
- 在GenerateSFCStep等方法中,会检查SFC图形元素的连接关系是否正确,若不正确会抛出PLCGenException异常
- 在GenerateConfiguration等方法中,也会根据配置要求进行检查,如资源或全局变量定义时类型不匹配等会抛出异常
- 在GenerateProgram方法中,会检查项目元素是否符合要求,如POU没有输入变量、代码体为空等情况会抛出PLCGenException异常
- PLCControler和ProgramGenerator都持有errors和warnings列表,代码生成过程中的非严重问题会添加到 warnings,严重错误添加到errors。
8.标准xml文件与对应的st文件
9.xml文件细节解释
<body>
<SFC>
<step localId="0" name="Init" initialStep="true">
<position x="50" y="50"/> <!-- 指定此步骤在SFC图上的坐标位置 -->
<connectionPointIn> <!-- 定义了步骤的输入连接点 -->
<connection formalParameter="sfc"/> <!-- 表示此连接点用于SFC连接 -->
</connectionPointIn>
<connectionPointOut formalParameter="sfc"/> <!-- 定义了步骤的输出连接点 -->
</step>
<inVariable localId="2">
<connectionPointOut/> <!-- 表示这个变量有一个输出连接点 -->
<expression>a</expression> <!-- 表示该变量实际绑定到程序变量a -->
</inVariable>
<transition localId="3" priority="0">
<connectionPointIn>
<connection refLocalId="0" formalParameter="sfc"/> <!-- 引用了localId
为0的输出作为转换的输入 -->
</connectionPointIn>
<connectionPointOut/> <!-- 表示这个变量有一个输出连接点 -->
<condition> <!-- 检查localId为2的变量a作为转换的条件 -->
<connectionPointIn>
<connection refLocalId="2"></connection>
</connectionPointIn>
</condition>
</transition>
</SFC>
</body>
10.SFC错误情况
检查SFC中是否缺少transition
在SFC中不缺少transition的情况下,判断transition是否多余或者缺少step
文字描述:
- 存在多个init初始步
step重名step前缺少transitionstep之间存在多个Transition- 并行分支
发散节点前缺少transition发散节点后存在transition收敛节点前存在trans收敛节点后缺少trans
- 选择分支
发散节点前存在trans发散节点后缺少trans收敛节点前缺少trans收敛节点后存在trans
- Jump
jump前节点是步
并行分支嵌套选择分支
11.任务配置检查
12.主要修改部分
def GenerateSFCTransition(self, transition, pou):
if transition not in self.SFCNetworks["Transitions"].keys():
steps = []
connections = transition.connectionPointIn.getconnections()
if connections is not None and len(connections) == 1:
instanceLocalId = connections[0].getrefLocalId()
body = pou.getbody()
if isinstance(body, list):
body = body[0]
instance = body.getcontentInstance(instanceLocalId)
if isinstance(instance, StepClass):
steps.append(instance)
elif isinstance(instance, SelectionDivergenceClass):
step = self.ExtractDivergenceInput(instance, pou)
if step is not None:
if isinstance(step, StepClass):
steps.append(step)
elif isinstance(step, SimultaneousConvergenceClass):
steps.extend(self.ExtractStepOfConvergenceInputs(step, pou))
elif isinstance(step, SimultaneousDivergenceClass):
steps.extend(self.ExtractDivergenceInput(step, pou))
elif isinstance(instance, SimultaneousConvergenceClass):
steps.extend(self.ExtractStepOfConvergenceInputs(instance, pou))
def ExtractStepOfConvergenceInputs(self, convergence, pou):
instances = []
for connectionPointIn in convergence.getconnectionPointIn():
connections = connectionPointIn.getconnections()
if connections is not None and len(connections) == 1:
instanceLocalId = connections[0].getrefLocalId()
body = pou.getbody()
if isinstance(body, list):
body = body[0]
instance = body.getcontentInstance(instanceLocalId)
if isinstance(instance, StepClass):
instances.append(body.getcontentInstance(instanceLocalId))
return instances
def ExtractStepOfDivergenceInputs(self, divergence, pou):
instances = []
for connectionPointIn in divergence.getconnectionPointIn():
connections = connectionPointIn.getconnections()
if connections is not None and len(connections) == 1:
instanceLocalId = connections[0].getrefLocalId()
body = pou.getbody()
if isinstance(body, list):
body = body[0]
instance = body.getcontentInstance(instanceLocalId)
if isinstance(instance, StepClass):
instances.append(instance)
elif isinstance(instance, SimultaneousDivergenceClass):
instances.append(ExtractStepOfDivergenceInputs(instance, pou))
return instances
12.每个节点的情况
- 步
- 转移
- 选择分支发散
- 选择分支收敛
- 并行分支发散
- 并行分支收敛
- 跳转
步之前可以的节点
- 转移
- 选择分支收敛
- 并行分支发散
转移之前可以的节点
- 步
- 并行分支收敛
- 选择分支发散
选择分支发散之前可以的节点
- 步
- 并行分支收敛
- 选择分支发散
选择分支收敛之前可以的节点
- 转移
并行分支发散之前可以的节点
- 转移
- 选择分支收敛
- 并行分支发散
并行分支收敛之前可以的节点
- 步
跳转之前可以的节点
- 转移
- 选择分支收敛
- 并行分支发散