diff -r 000000000000 -r b622defdfd98 PLCGenerator.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/PLCGenerator.py Wed Jan 31 16:31:39 2007 +0100 @@ -0,0 +1,422 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +#This file is part of PLCOpenEditor, a library implementing an IEC 61131-3 editor +#based on the plcopen standard. +# +#Copyright (C): Edouard TISSERANT and Laurent BESSARD +# +#See COPYING file for copyrights details. +# +#This library is free software; you can redistribute it and/or +#modify it under the terms of the GNU Lesser General Public +#License as published by the Free Software Foundation; either +#version 2.1 of the License, or (at your option) any later version. +# +#This library is distributed in the hope that it will be useful, +#but WITHOUT ANY WARRANTY; without even the implied warranty of +#MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +#Lesser General Public License for more details. +# +#You should have received a copy of the GNU Lesser General Public +#License along with this library; if not, write to the Free Software +#Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +from plcopen import plcopen +from plcopen.structures import * +from types import * + +varTypeNames = {"localVars" : "VAR", "tempVars" : "VAR_TEMP", "inputVars" : "VAR_INPUT", + "outputVars" : "VAR_OUTPUT", "inOutVars" : "VAR_IN_OUT", "externalVars" : "VAR_EXTERNAL", + "globalVars" : "VAR_GLOBAL", "accessVars" : "VAR_ACCESS"} +""" +Module implementing methods for generating PLC programs in ST or IL +""" + +class PouProgram: + + def __init__(self, name, type): + self.Name = name + self.Type = type + self.Interface = {} + self.Steps = {} + self.Transitions = {} + self.Order = [] + self.Program = "" + + def GenerateInterface(self, interface): + if self.Type == "FUNCTION": + self.Interface["returnType"] = interface.getReturnType().getValue() + for varlist in interface.getContent(): + variables = {} + for var in varlist["value"].getVariable(): + type = var.getType().getValue() + if type not in variables: + variables[type] = [] + variables[type].append(var.getName()) + self.Interface[(varTypeNames[varlist["name"]], varlist["value"].getRetain(), + varlist["value"].getConstant())] = variables + + def GenerateProgram(self, pou): + body = pou.getBody() + body_content = body.getContent() + body_type = body_content["name"] + if body_type in ["IL","ST"]: + self.Program = "%s\n"%body_content["value"].getText() + elif body_type == "FBD": + for instance in body.getContentInstances(): + if isinstance(instance, plcopen.outVariable): + var = instance.getExpression() + connections = instance.connectionPointIn.getConnections() + if connections and len(connections) == 1: + expression = self.ComputeFBDExpression(body, connections[0]) + self.Program += " %s := %s;\n"%(var, expression) + elif body_type == "LD": + for instance in body.getContentInstances(): + if isinstance(instance, plcopen.coil): + paths = self.GenerateLDPaths(instance, body) + variable = self.ExtractModifier(instance, instance.getVariable()) + expression = self.ComputeLDExpression(paths, True) + self.Program += " %s := %s;\n"%(variable, expression) + elif body_type == "SFC": + for instance in body.getContentInstances(): + if isinstance(instance, plcopen.step): + self.GenerateSFCSteps(instance, pou) + elif isinstance(instance, plcopen.actionBlock): + self.GenerateSFCActions(instance, pou) + elif isinstance(instance, plcopen.transition): + self.GenerateSFCTransitions(instance, pou) + elif isinstance(instance, plcopen.jumpStep): + self.GenerateSFCJump(instance, pou) + for name, values in self.Steps.items(): + if values['initial']: + self.GenerateSFCStepOrder(name, []) + steps_type = "ARRAY [1..%d] OF BOOL"%len(self.Order) + if ("VAR", False, False) not in self.Interface: + self.Interface[("VAR", False, False)] = {} + if steps_type not in self.Interface[("VAR", False, False)]: + self.Interface[("VAR", False, False)][steps_type] = ["Steps"] + else: + self.Interface[("VAR", False, False)][steps_type].append("Steps") + for index, name in enumerate(self.Order): + values = self.Steps[name] + self.Program += " IF Steps[%d] THEN\n"%index + for action in values["actions"]: + if action["qualifier"] == "N": + for line in action["content"].splitlines(): + self.Program += " %s\n"%line + elif action["qualifier"] == "S": + if "R_TRIG" not in self.Interface[("VAR", False, False)]: + self.Interface[("VAR", False, False)]["R_TRIG"] = [] + i = 1 + name = "R_TRIG%d"%i + while name in self.Interface[("VAR", False, False)]["R_TRIG"]: + i += 1 + name = "R_TRIG%d"%i + self.Interface[("VAR", False, False)]["R_TRIG"].append(name) + self.Program += " IF %s(CLK := Steps[%d]) THEN\n"%(name, index) + self.Program += " %s := TRUE;\n"%action["content"] + for transition in values["transitions"]: + if transition["compute"] != '': + self.Program += " %s %s"%(transition["condition"], transition["compute"]) + self.Program += " IF %s THEN\n"%transition["condition"] + for target in transition["targets"]: + self.Program += " Steps[%d] := TRUE;\n"%self.Order.index(target) + self.Program += " Steps[%d] := FALSE;\n"%index + + def ComputeFBDExpression(self, body, link): + localid = link.getRefLocalId() + instance = body.getContentInstance(localid) + if isinstance(instance, plcopen.inVariable): + return instance.getExpression() + elif isinstance(instance, plcopen.block): + name = instance.getInstanceName() + type = instance.getTypeName() + block_infos = GetBlockType(type) + if block_infos["type"] == "function": + vars = [] + for variable in instance.inputVariables.getVariable(): + connections = variable.connectionPointIn.getConnections() + if connections and len(connections) == 1: + value = self.ComputeFBDExpression(body, connections[0]) + vars.append(self.ExtractModifier(variable, value)) + variable = instance.outputVariables.getVariable()[0] + return self.ExtractModifier(variable, "%s(%s)"%(type, ", ".join(vars))) + elif block_infos["type"] == "functionBlock": + if ("VAR", False, False) not in self.Interface: + self.Interface[("VAR", False, False)] = {} + if type not in self.Interface[("VAR", False, False)]: + self.Interface[("VAR", False, False)][type] = [] + if name not in self.Interface[("VAR", False, False)][type]: + self.Interface[("VAR", False, False)][type].append(name) + vars = [] + for variable in instance.inputVariables.getVariable(): + connections = variable.connectionPointIn.getConnections() + if connections and len(connections) == 1: + parameter = variable.getFormalParameter() + value = self.ComputeFBDExpression(body, connections[0]) + vars.append(self.ExtractModifier(variable, "%s := %s"%(parameter, value))) + self.Program += " %s(%s);\n"%(name, ", ".join(vars)) + connectionPoint = link.getPosition()[-1] + for variable in instance.outputVariables.getVariable(): + blockPointx, blockPointy = variable.connectionPointOut.getRelPosition() + if instance.getX() + blockPointx == connectionPoint.getX() and instance.getY() + blockPointy == connectionPoint.getY(): + return self.ExtractModifier(variable, "%s.%s"%(name, variable.getFormalParameter())) + raise ValueError, "No output variable found" + + def GenerateLDPaths(self, instance, body): + paths = [] + variable = self.ExtractModifier(instance, instance.getVariable()) + connections = instance.connectionPointIn.getConnections() + for connection in connections: + localId = connection.getRefLocalId() + next = body.getContentInstance(localId) + if isinstance(next, plcopen.leftPowerRail): + paths.append(None) + else: + paths.append(self.GenerateLDPaths(next, body)) + if isinstance(instance, plcopen.coil): + if len(paths) > 1: + return tuple(paths) + else: + return paths + else: + if len(paths) > 1: + return [variable, tuple(paths)] + elif type(paths[0]) == ListType: + return [variable] + paths[0] + elif paths[0]: + return [variable, paths[0]] + else: + return variable + + def GenerateSFCSteps(self, step, pou): + step_name = step.getName() + if step_name not in self.Steps: + step_infos = {"initial" : step.getInitialStep(), "transitions" : [], "actions" : []} + self.Steps[step_name] = step_infos + if step.connectionPointIn: + instances = [] + connections = step.connectionPointIn.getConnections() + if len(connections) == 1: + instanceLocalId = connections[0].getRefLocalId() + instance = pou.body.getContentInstance(instanceLocalId) + if isinstance(instance, plcopen.transition): + self.GenerateSFCTransitions(instance, pou) + instances.append(instance) + elif isinstance(instance, plcopen.selectionConvergence): + for connectionPointIn in instance.getConnectionPointIn(): + divergence_connections = connectionPointIn.getConnections() + if len(divergence_connections) == 1: + transitionLocalId = connections[0].getRefLocalId() + transition = pou.body.getContentInstance(transitionLocalId) + self.GenerateSFCTransitions(transition, pou) + instances.append(transition) + for instance in instances: + self.Transitions[instance]["targets"].append(step_name) + + def GenerateSFCJump(self, jump, pou): + jump_target = jump.getTargetName() + if jump.connectionPointIn: + instances = [] + connections = jump.connectionPointIn.getConnections() + if len(connections) == 1: + instanceLocalId = connections[0].getRefLocalId() + instance = pou.body.getContentInstance(instanceLocalId) + if isinstance(instance, plcopen.transition): + self.GenerateSFCTransitions(instance, pou) + instances.append(instance) + elif isinstance(instance, plcopen.selectionConvergence): + for connectionPointIn in instance.getConnectionPointIn(): + divergence_connections = connectionPointIn.getConnections() + if len(divergence_connections) == 1: + transitionLocalId = divergence_connections[0].getRefLocalId() + transition = pou.body.getContentInstance(transitionLocalId) + self.GenerateSFCTransitions(transition, pou) + instances.append(transition) + for instance in instances: + self.Transitions[instance]["targets"].append(jump_target) + + def GenerateSFCActions(self, actionBlock, pou): + connections = actionBlock.connectionPointIn.getConnections() + if len(connections) == 1: + stepLocalId = connections[0].getRefLocalId() + step = pou.body.getContentInstance(stepLocalId) + step_name = step.getName() + if step_name not in self.Steps: + self.GenerateSFCSteps(step, pou) + if step_name in self.Steps: + actions = actionBlock.getActions() + for action in actions: + action_infos = {"qualifier" : action["qualifier"], "content" : ""} + if action["type"] == "inline": + action_infos["content"] = action["value"] + elif action["type"] == "reference": + actionContent = pou.getAction(action["value"]) + if actionContent: + actionType = actionContent.getBodyType() + actionBody = actionContent.getBody() + if actionType in ["ST", "IL"]: + action_infos["content"] = actionContent.getText() + elif actionType == "FBD": + for instance in actionBody.getContentInstances(): + if isinstance(instance, plcopen.outVariable): + var = instance.getExpression() + connections = instance.connectionPointIn.getConnections() + if connections and len(connections) == 1: + expression = self.ComputeFBDExpression(actionBody, connections[0]) + action_infos["content"] = self.Program + " %s := %s;"%(var, expression) + self.Program = "" + elif actionType == "LD": + for instance in actionbody.getContentInstances(): + if isinstance(instance, plcopen.coil): + paths = self.GenerateLDPaths(instance, actionBody) + variable = self.ExtractModifier(instance, instance.getVariable()) + expression = self.ComputeLDExpression(paths, True) + action_infos["content"] = self.Program + " %s := %s;"%(variable, expression) + self.Program = "" + else: + action_infos["content"] = action["value"] + self.Steps[step_name]["actions"].append(action_infos) + + def GenerateSFCTransitions(self, transition, pou): + if transition not in self.Transitions: + connections = transition.connectionPointIn.getConnections() + if len(connections) == 1: + instanceLocalId = connections[0].getRefLocalId() + instance = pou.body.getContentInstance(instanceLocalId) + if isinstance(instance, plcopen.step): + step_name = instance.getName() + if step_name not in self.Steps: + self.GenerateSFCSteps(instance, pou) + elif isinstance(instance, plcopen.selectionDivergence): + divergence_connections = instance.connectionPointIn.getConnections() + if len(divergence_connections) == 1: + stepLocalId = divergence_connections[0].getRefLocalId() + divergence_instance = pou.body.getContentInstance(stepLocalId) + if isinstance(divergence_instance, plcopen.step): + step_name = divergence_instance.getName() + if step_name not in self.Steps: + self.GenerateSFCSteps(divergence_instance, pou) + if step_name in self.Steps: + transition_infos = {"targets" : []} + transitionValues = transition.getConditionContent() + transition_infos["condition"] = transitionValues["value"] + if transitionValues["type"] == "inline": + transition_infos["compute"] = "" + else: + transitionContent = pou.getTransition(transitionValues["value"]) + transitionType = transitionContent.getBodyType() + transitionBody = transitionContent.getBody() + if transitionType in ["ST", "IL"]: + transition_infos["compute"] = "%s\n"%transitionContent.getText() + elif conditionType == "FBD": + for instance in conditionBody.getContentInstances(): + if isinstance(instance, plcopen.outVariable): + var = instance.getExpression() + connections = instance.connectionPointIn.getConnections() + if connections and len(connections) == 1: + expression = self.ComputeFBDExpression(actionBody, connections[0]) + transition_infos["compute"] = self.Program + ":= %s;\n"%(var, expression) + self.Program = "" + elif actionType == "LD": + for instance in conditionbody.getContentInstances(): + if isinstance(instance, plcopen.coil): + paths = self.GenerateLDPaths(instance, conditionBody) + variable = self.ExtractModifier(instance, instance.getVariable()) + expression = self.ComputeLDExpression(paths, True) + transition_infos["compute"] = self.Program + ":= %s;\n"%(variable, expression) + self.Program = "" + self.Steps[step_name]["transitions"].append(transition_infos) + self.Transitions[transition] = transition_infos + + def GenerateSFCStepOrder(self, name, path): + self.Order.append(name) + for transition in self.Steps[name]["transitions"]: + for target in transition["targets"]: + if target not in self.Order or target not in path: + if target in self.Order: + self.Order.remove(target) + self.GenerateSFCStepOrder(target, path + [name]) + + def ComputeLDExpression(self, paths, first = False): + if type(paths) == TupleType: + if None in paths: + return "TRUE" + else: + var = [self.ComputeLDExpression(path) for path in paths] + if first: + return " OR ".join(var) + else: + return "(%s)"%" OR ".join(var) + elif type(paths) == ListType: + var = [self.ComputeLDExpression(path) for path in paths] + return " AND ".join(var) + else: + return paths + + def ExtractModifier(self, variable, text): + if variable.getNegated(): + return "NOT(%s)"%text + else: + edge = variable.getEdge() + if edge: + if edge.getValue() == "rising": + return self.AddTrigger("R_TRIG", text) + elif edge.getValue() == "falling": + return self.AddTrigger("F_TRIG", text) + return text + + def AddTrigger(self, edge, text): + if ("VAR", False, False) not in self.Interface: + self.Interface[("VAR", False, False)] = {} + if type not in self.Interface[("VAR", False, False)]: + self.Interface[("VAR", False, False)][edge] = [] + i = 1 + name = "%s%d"%(edge, i) + while name in self.Interface[("VAR", False, False)][edge]: + i += 1 + name = "%s%d"%(edge, i) + self.Interface[("VAR", False, False)][edge].append(name) + self.Program += " %s(CLK := %s);\n"%(name, text) + return "%s.Q"%name + + def GenerateSTProgram(self): + program = "" + if "returnType" in self.Interface: + program += "%s %s : %s\n"%(self.Type, self.Name, self.Interface["returnType"]) + else: + program += "%s %s\n"%(self.Type, self.Name) + for values, variables in self.Interface.items(): + if values != "returnType": + program += " %s"%values[0] + if values[1]: + program += " RETAIN" + if values[2]: + program += " CONSTANT" + program += "\n" + for vartype, list in variables.items(): + program += " %s : %s;\n"%(", ".join(list), vartype) + program += " END_%s\n"%values[0] + program += "\n" + program += self.Program + program += "END_%s\n\n"%self.Type + return program + +def GenerateCurrentProgram(project): + program = "" + for pou in project.getPous(): + pou_type = pou.getPouType().getValue() + if pou_type == "function": + pou_program = PouProgram(pou.getName(), "FUNCTION") + elif pou_type == "functionBlock": + pou_program = PouProgram(pou.getName(), "FUNCTION_BLOCK") + elif pou_type == "program": + pou_program = PouProgram(pou.getName(), "PROGRAM") + else: + raise ValueError, "Undefined pou type" + pou_program.GenerateInterface(pou.getInterface()) + pou_program.GenerateProgram(pou) + program += pou_program.GenerateSTProgram() + return program +