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