PLCGenerator.py
changeset 0 b622defdfd98
child 1 e9d01d824086
--- /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
+