# HG changeset patch # User lbessard # Date 1187854758 -7200 # Node ID 049f2e7090a25ecd88eb371ab59e3928a1828b4e # Parent 346a43f179a5134286da9e061bd29001ed4347ce Adding support for adding block types with particular behaviour diff -r 346a43f179a5 -r 049f2e7090a2 PLCGenerator.py --- a/PLCGenerator.py Thu Aug 23 09:26:02 2007 +0200 +++ b/PLCGenerator.py Thu Aug 23 09:39:18 2007 +0200 @@ -53,6 +53,111 @@ compute += "\n" return compute + +def GeneratePouProgram(pou_name): + if not pouComputed.get(pou_name, True): + pouComputed[pou_name] = True + global currentProject, currentProgram + pou = currentProject.getPou(pou_name) + pou_type = pou.getPouType().getValue() + if pou_type in pouTypeNames: + pou_program = PouProgram(pou.getName(), pouTypeNames[pou_type]) + else: + raise ValueError, "Undefined pou type" + pou_program.GenerateInterface(pou.getInterface()) + pou_program.GenerateProgram(pou) + currentProgram += pou_program.GenerateSTProgram() + +def GenerateConfiguration(configuration): + config = "\nCONFIGURATION %s\n"%configuration.getName() + for varlist in configuration.getGlobalVars(): + config += " VAR_GLOBAL" + if varlist.getRetain(): + config += " RETAIN" + if varlist.getConstant(): + config += " CONSTANT" + config += "\n" + for var in varlist.getVariable(): + var_type = var.getType().getValue() + config += " %s "%var.getName() + address = var.getAddress() + if address: + config += "AT %s "%address + config += ": %s"%var_type + initial = var.getInitialValue() + if initial: + value = str(initial.getValue()) + value = {"TRUE":"0","FALSE":"1"}.get(value.upper(), value) + if var_type == "STRING": + config += " := '%s'"%value + elif var_type == "WSTRING": + config += " := \"%s\""%value + else: + config += " := %s"%value + config += ";\n" + config += " END_VAR\n" + for resource in configuration.getResource(): + config += GenerateResource(resource) + config += "END_CONFIGURATION\n" + return config + +def GenerateResource(resource): + resrce = "\n RESOURCE %s ON BEREMIZ\n"%resource.getName() + for varlist in resource.getGlobalVars(): + resrce += " VAR_GLOBAL" + if varlist.getRetain(): + resrce += " RETAIN" + if varlist.getConstant(): + resrce += " CONSTANT" + resrce += "\n" + for var in varlist.getVariable(): + var_type = var.getType().getValue() + resrce += " %s "%var.getName() + address = var.getAddress() + if address: + resrce += "AT %s "%address + resrce += ": %s"%var.getType().getValue() + initial = var.getInitialValue() + if initial: + value = str(initial.getValue()) + value = {"TRUE":"0","FALSE":"1"}.get(value.upper(), value) + if var_type == "STRING": + resrce += " := '%s'"%value + elif var_type == "WSTRING": + resrce += " := \"%s\""%value + else: + resrce += " := %s"%value + resrce += ";\n" + resrce += " END_VAR\n" + tasks = resource.getTask() + for task in tasks: + resrce += " TASK %s("%task.getName() + args = [] + single = task.getSingle() + if single: + args.append("SINGLE := %s"%single) + interval = task.getInterval() + if interval: + text = "t#" + if interval.hour != 0: + text += "%dh"%interval.hour + if interval.minute != 0: + text += "%dm"%interval.minute + if interval.second != 0: + text += "%ds"%interval.second + if interval.microsecond != 0: + text += "%dms"%(interval.microsecond / 1000) + args.append("INTERVAL := %s"%text) + args.append("PRIORITY := %s"%str(task.priority.getValue())) + resrce += ",".join(args) + ");\n" + for task in tasks: + for instance in task.getPouInstance(): + resrce += " PROGRAM %s WITH %s : %s;\n"%(instance.getName(), task.getName(), instance.getType()) + for instance in resource.getPouInstance(): + resrce += " PROGRAM %s : %s;\n"%(instance.getName(), instance.getType()) + resrce += " END_RESOURCE\n" + return resrce + """ Module implementing methods for generating PLC programs in ST or IL """ @@ -120,7 +225,11 @@ if connections and len(connections) == 1: expression = self.ComputeFBDExpression(body, connections[0]) self.Program += " %s := %s;\n"%(var, expression) - if isinstance(instance, plcopen.connector): + elif isinstance(instance, plcopen.block): + type = instance.getTypeName() + block_infos = GetBlockType(type) + block_infos["generate"](self, instance, body, None) + elif isinstance(instance, plcopen.connector): connector = instance.getName() if self.ComputedConnectors.get(connector, None): continue @@ -161,36 +270,9 @@ if isinstance(instance, (plcopen.inVariable, plcopen.inOutVariable)): return instance.getExpression() elif isinstance(instance, plcopen.block): - name = instance.getInstanceName() type = instance.getTypeName() block_infos = GetBlockType(type) - if block_infos["type"] == "function": - GeneratePouProgram(type) - 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 not self.ComputedBlocks.get(name, False): - 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)) - self.ComputedBlocks[name] = True - 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" + return block_infos["generate"](self, instance, body, link) elif isinstance(instance, plcopen.continuation): name = instance.getName() computed_value = self.ComputedConnectors.get(name, None) @@ -557,109 +639,8 @@ program += "END_%s\n\n"%self.Type return program -def GeneratePouProgram(pou_name): - if not pouComputed.get(pou_name, True): - pouComputed[pou_name] = True - global currentProject, currentProgram - pou = currentProject.getPou(pou_name) - pou_type = pou.getPouType().getValue() - if pou_type in pouTypeNames: - pou_program = PouProgram(pou.getName(), pouTypeNames[pou_type]) - else: - raise ValueError, "Undefined pou type" - pou_program.GenerateInterface(pou.getInterface()) - pou_program.GenerateProgram(pou) - currentProgram += pou_program.GenerateSTProgram() - -def GenerateConfiguration(configuration): - config = "\nCONFIGURATION %s\n"%configuration.getName() - for varlist in configuration.getGlobalVars(): - config += " VAR_GLOBAL" - if varlist.getRetain(): - config += " RETAIN" - if varlist.getConstant(): - config += " CONSTANT" - config += "\n" - for var in varlist.getVariable(): - var_type = var.getType().getValue() - config += " %s "%var.getName() - address = var.getAddress() - if address: - config += "AT %s "%address - config += ": %s"%var_type - initial = var.getInitialValue() - if initial: - value = str(initial.getValue()) - value = {"TRUE":"0","FALSE":"1"}.get(value.upper(), value) - if var_type == "STRING": - config += " := '%s'"%value - elif var_type == "WSTRING": - config += " := \"%s\""%value - else: - config += " := %s"%value - config += ";\n" - config += " END_VAR\n" - for resource in configuration.getResource(): - config += GenerateResource(resource) - config += "END_CONFIGURATION\n" - return config - -def GenerateResource(resource): - resrce = "\n RESOURCE %s ON BEREMIZ\n"%resource.getName() - for varlist in resource.getGlobalVars(): - resrce += " VAR_GLOBAL" - if varlist.getRetain(): - resrce += " RETAIN" - if varlist.getConstant(): - resrce += " CONSTANT" - resrce += "\n" - for var in varlist.getVariable(): - var_type = var.getType().getValue() - resrce += " %s "%var.getName() - address = var.getAddress() - if address: - resrce += "AT %s "%address - resrce += ": %s"%var.getType().getValue() - initial = var.getInitialValue() - if initial: - value = str(initial.getValue()) - value = {"TRUE":"0","FALSE":"1"}.get(value.upper(), value) - if var_type == "STRING": - resrce += " := '%s'"%value - elif var_type == "WSTRING": - resrce += " := \"%s\""%value - else: - resrce += " := %s"%value - resrce += ";\n" - resrce += " END_VAR\n" - tasks = resource.getTask() - for task in tasks: - resrce += " TASK %s("%task.getName() - args = [] - single = task.getSingle() - if single: - args.append("SINGLE := %s"%single) - interval = task.getInterval() - if interval: - text = "t#" - if interval.hour != 0: - text += "%dh"%interval.hour - if interval.minute != 0: - text += "%dm"%interval.minute - if interval.second != 0: - text += "%ds"%interval.second - if interval.microsecond != 0: - text += "%dms"%(interval.microsecond / 1000) - args.append("INTERVAL := %s"%text) - args.append("PRIORITY := %s"%str(task.priority.getValue())) - resrce += ",".join(args) + ");\n" - for task in tasks: - for instance in task.getPouInstance(): - resrce += " PROGRAM %s WITH %s : %s;\n"%(instance.getName(), task.getName(), instance.getType()) - for instance in resource.getPouInstance(): - resrce += " PROGRAM %s : %s;\n"%(instance.getName(), instance.getType()) - resrce += " END_RESOURCE\n" - return resrce + def GeneratePouProgram(self, pou_name): + GeneratePouProgram(pou_name) def GenerateCurrentProgram(project): global currentProject, currentProgram diff -r 346a43f179a5 -r 049f2e7090a2 plcopen/structures.py --- a/plcopen/structures.py Thu Aug 23 09:26:02 2007 +0200 +++ b/plcopen/structures.py Thu Aug 23 09:39:18 2007 +0200 @@ -27,6 +27,40 @@ LANGUAGES = ["IL","ST","FBD","LD","SFC"] +def generate_block(generator, block, body, link): + name = block.getInstanceName() + type = block.getTypeName() + block_infos = GetBlockType(type) + if block_infos["type"] == "function" and link: + generator.GeneratePouProgram(type) + vars = [] + for variable in block.inputVariables.getVariable(): + connections = variable.connectionPointIn.getConnections() + if connections and len(connections) == 1: + value = generator.ComputeFBDExpression(body, connections[0]) + vars.append(generator.ExtractModifier(variable, value)) + variable = block.outputVariables.getVariable()[0] + return generator.ExtractModifier(variable, "%s(%s)"%(type, ", ".join(vars))) + elif block_infos["type"] == "functionBlock": + if not generator.ComputedBlocks.get(name, False): + vars = [] + for variable in block.inputVariables.getVariable(): + connections = variable.connectionPointIn.getConnections() + if connections and len(connections) == 1: + parameter = variable.getFormalParameter() + value = generator.ComputeFBDExpression(body, connections[0]) + vars.append("%s := %s"%(parameter, generator.ExtractModifier(variable, value))) + generator.Program += " %s(%s);\n"%(name, ", ".join(vars)) + generator.ComputedBlocks[name] = True + if link: + connectionPoint = link.getPosition()[-1] + else: + connectionPoint = None + for variable in block.outputVariables.getVariable(): + blockPointx, blockPointy = variable.connectionPointOut.getRelPosition() + if not connectionPoint or block.getX() + blockPointx == connectionPoint.getX() and block.getY() + blockPointy == connectionPoint.getY(): + return generator.ExtractModifier(variable, "%s.%s"%(name, variable.getFormalParameter())) + raise ValueError, "No output variable found" #------------------------------------------------------------------------------- # Function Block Types definitions @@ -42,6 +76,7 @@ - "inputs" : List of the block inputs - "outputs" : List of the block outputs - "comment" : Comment that will be displayed in the block popup + - "generate" : Method that generator will call for generating ST block code Inputs and outputs are a tuple of characteristics that are in order: - The name - The data type @@ -52,110 +87,104 @@ [{"name" : "SR", "type" : "functionBlock", "extensible" : False, "inputs" : [("S1","BOOL","none"),("R","BOOL","none")], "outputs" : [("Q1","BOOL","none")], - "comment" : "SR bistable\nThe SR bistable is a latch where the Set dominates."}, + "comment" : "SR bistable\nThe SR bistable is a latch where the Set dominates.", + "generate" : generate_block}, {"name" : "RS", "type" : "functionBlock", "extensible" : False, "inputs" : [("S","BOOL","none"),("R1","BOOL","none")], "outputs" : [("Q1","BOOL","none")], - "comment" : "RS bistable\nThe RS bistable is a latch where the Reset dominates."}, + "comment" : "RS bistable\nThe RS bistable is a latch where the Reset dominates.", + "generate" : generate_block}, {"name" : "SEMA", "type" : "functionBlock", "extensible" : False, "inputs" : [("CLAIM","BOOL","none"),("RELEASE","BOOL","none")], "outputs" : [("BUSY","BOOL","none")], - "comment" : "Semaphore\nThe semaphore provides a mechanism to allow software elements mutually exclusive access to certain ressources."}, + "comment" : "Semaphore\nThe semaphore provides a mechanism to allow software elements mutually exclusive access to certain ressources.", + "generate" : generate_block}, {"name" : "R_TRIG", "type" : "functionBlock", "extensible" : False, "inputs" : [("CLK","BOOL","none")], "outputs" : [("Q","BOOL","none")], - "comment" : "Rising edge detector\nThe output produces a single pulse when a rising edge is detected."}, + "comment" : "Rising edge detector\nThe output produces a single pulse when a rising edge is detected.", + "generate" : generate_block}, {"name" : "F_TRIG", "type" : "functionBlock", "extensible" : False, "inputs" : [("CLK","BOOL","none")], "outputs" : [("Q","BOOL","none")], - "comment" : "Falling edge detector\nThe output produces a single pulse when a falling edge is detected."}, + "comment" : "Falling edge detector\nThe output produces a single pulse when a falling edge is detected.", + "generate" : generate_block}, {"name" : "CTU", "type" : "functionBlock", "extensible" : False, "inputs" : [("CU","BOOL","rising"),("R","BOOL","none"),("PV","INT","none")], "outputs" : [("Q","BOOL","none"),("CV","INT","none")], - "comment" : "Up-counter\nThe up-counter can be used to signal when a count has reached a maximum value."}, + "comment" : "Up-counter\nThe up-counter can be used to signal when a count has reached a maximum value.", + "generate" : generate_block}, {"name" : "CTD", "type" : "functionBlock", "extensible" : False, "inputs" : [("CD","BOOL","rising"),("LD","BOOL","none"),("PV","INT","none")], "outputs" : [("Q","BOOL","none"),("CV","INT","none")], - "comment" : "Down-counter\nThe down-counter can be used to signal when a count has reached zero, on counting down from a preset value."}, + "comment" : "Down-counter\nThe down-counter can be used to signal when a count has reached zero, on counting down from a preset value.", + "generate" : generate_block}, {"name" : "CTUD", "type" : "functionBlock", "extensible" : False, "inputs" : [("CU","BOOL","rising"),("CD","BOOL","rising"),("R","BOOL","none"),("LD","BOOL","none"),("PV","INT","none")], "outputs" : [("QU","BOOL","none"),("QD","BOOL","none"),("CV","INT","none")], - "comment" : "Up-down counter\nThe up-down counter has two inputs CU and CD. It can be used to both count up on one input ans down on the other."}, + "comment" : "Up-down counter\nThe up-down counter has two inputs CU and CD. It can be used to both count up on one input ans down on the other.", + "generate" : generate_block}, {"name" : "TP", "type" : "functionBlock", "extensible" : False, "inputs" : [("IN","BOOL","none"),("PT","TIME","none")], "outputs" : [("Q","BOOL","none"),("ET","TIME","none")], - "comment" : "Pulse timer\nThe pulse timer can be used to generate output pulses of a given time duration."}, + "comment" : "Pulse timer\nThe pulse timer can be used to generate output pulses of a given time duration.", + "generate" : generate_block}, {"name" : "TOF", "type" : "functionBlock", "extensible" : False, "inputs" : [("IN","BOOL","none"),("PT","TIME","none")], "outputs" : [("Q","BOOL","none"),("ET","TIME","none")], - "comment" : "On-delay timer\nThe on-delay timer can be used to delay setting an output true, for fixed period after an input becomes true."}, + "comment" : "On-delay timer\nThe on-delay timer can be used to delay setting an output true, for fixed period after an input becomes true.", + "generate" : generate_block}, {"name" : "TON", "type" : "functionBlock", "extensible" : False, "inputs" : [("IN","BOOL","none"),("PT","TIME","none")], "outputs" : [("Q","BOOL","none"),("ET","TIME","none")], - "comment" : "Off-delay timer\nThe off-delay timer can be used to delay setting an output false, for fixed period after input goes false."}, + "comment" : "Off-delay timer\nThe off-delay timer can be used to delay setting an output false, for fixed period after input goes false.", + "generate" : generate_block}, {"name" : "RTC", "type" : "functionBlock", "extensible" : False, "inputs" : [("EN","BOOL","none"),("PDT","DATE_AND_TIME","none")], "outputs" : [("Q","BOOL","none"),("CDT","DATE_AND_TIME","none")], - "comment" : "Real time clock\nThe real time clock has many uses including time stamping, setting dates and times of day in batch reports, in alarm messages and so on."}, + "comment" : "Real time clock\nThe real time clock has many uses including time stamping, setting dates and times of day in batch reports, in alarm messages and so on.", + "generate" : generate_block}, {"name" : "INTEGRAL", "type" : "functionBlock", "extensible" : False, "inputs" : [("RUN","BOOL","none"),("R1","BOOL","none"),("XIN","REAL","none"),("X0","REAL","none"),("CYCLE","TIME","none")], "outputs" : [("Q","BOOL","none"),("XOUT","REAL","none")], - "comment" : "Integral\nThe integral function block integrates the value of input XIN over time."}, + "comment" : "Integral\nThe integral function block integrates the value of input XIN over time.", + "generate" : generate_block}, {"name" : "DERIVATIVE", "type" : "functionBlock", "extensible" : False, "inputs" : [("RUN","BOOL","none"),("XIN","REAL","none"),("CYCLE","TIME","none")], "outputs" : [("XOUT","REAL","none")], - "comment" : "Derivative\nThe derivative function block produces an output XOUT proportional to the rate of change of the input XIN."}, + "comment" : "Derivative\nThe derivative function block produces an output XOUT proportional to the rate of change of the input XIN.", + "generate" : generate_block}, {"name" : "PID", "type" : "functionBlock", "extensible" : False, "inputs" : [("AUTO","BOOL","none"),("PV","REAL","none"),("SP","REAL","none"),("X0","REAL","none"),("KP","REAL","none"),("TR","REAL","none"),("TD","REAL","none"),("CYCLE","TIME","none")], "outputs" : [("XOUT","REAL","none")], - "comment" : "PID\nThe PID (proportional, Integral, Derivative) function block provides the classical three term controller for closed loop control."}, + "comment" : "PID\nThe PID (proportional, Integral, Derivative) function block provides the classical three term controller for closed loop control.", + "generate" : generate_block}, {"name" : "RAMP", "type" : "functionBlock", "extensible" : False, "inputs" : [("RUN","BOOL","none"),("X0","REAL","none"),("X1","REAL","none"),("TR","TIME","none"),("CYCLE","TIME","none"),("HOLDBACK","BOOL","none"),("ERROR","REAL","none"),("PV","REAL","none")], "outputs" : [("RAMP","BOOL","none"),("XOUT","REAL","none")], - "comment" : "Ramp\nThe RAMP function block is modelled on example given in the standard but with the addition of a 'Holdback' feature."}, + "comment" : "Ramp\nThe RAMP function block is modelled on example given in the standard but with the addition of a 'Holdback' feature.", + "generate" : generate_block}, {"name" : "HYSTERESIS", "type" : "functionBlock", "extensible" : False, "inputs" : [("XIN1","REAL","none"),("XIN2","REAL","none"),("EPS","REAL","none")], "outputs" : [("Q","BOOL","none")], - "comment" : "Hysteresis\nThe hysteresis function block provides a hysteresis boolean output driven by the difference of two floating point (REAL) inputs XIN1 and XIN2."}, + "comment" : "Hysteresis\nThe hysteresis function block provides a hysteresis boolean output driven by the difference of two floating point (REAL) inputs XIN1 and XIN2.", + "generate" : generate_block}, {"name" : "RATIO_MONITOR", "type" : "functionBlock", "extensible" : False, "inputs" : [("PV1","REAL","none"),("PV2","REAL","none"),("RATIO","REAL","none"),("TIMON","TIME","none"),("TIMOFF","TIME","none"),("TOLERANCE","BOOL","none"),("RESET","BOOL","none"),("CYCLE","TIME","none")], "outputs" : [("ALARM","BOOL","none"),("TOTAL_ERR","BOOL","none")], - "comment" : "Ratio monitor\nThe ratio_monitor function block checks that one process value PV1 is always a given ratio (defined by input RATIO) of a second process value PV2."} + "comment" : "Ratio monitor\nThe ratio_monitor function block checks that one process value PV1 is always a given ratio (defined by input RATIO) of a second process value PV2.", + "generate" : generate_block} ]}, - {"name" : "SVGUI function blocks", "list": - [{"name" : "Container", "type" : "functionBlock", "extensible" : False, - "inputs" : [("X","FLOAT","none"),("SetX","BOOL","none"),("Y","FLOAT","none"),("SetY","BOOL","none"),("Angle","FLOAT","none"),("SetAngle","BOOL","none")], - "outputs" : [("X","FLOAT","none"),("X Changed","BOOL","none"),("Y","FLOAT","none"),("Y Changed","BOOL","none"),("Angle","FLOAT","none"),("Angle Changed","BOOL","none")], - "comment" : "SVGUI Container"}, - {"name" : "Button", "type" : "functionBlock", "extensible" : False, - "inputs" : [("Show","BOOL","none"),("Toggle","BOOL","none")], - "outputs" : [("Visible","BOOL","none"),("State","BOOL","none")], - "comment" : "SVGUI Button"}, - {"name" : "TextCtrl", "type" : "functionBlock", "extensible" : False, - "inputs" : [("Text","STRING","none"),("Set Text","BOOL","none")], - "outputs" : [("Text","STRING","none"),("Text Changed","BOOL","none")], - "comment" : "SVGUI Text Control"}, - {"name" : "ScrollBar", "type" : "functionBlock", "extensible" : False, - "inputs" : [("Position","UINT","none"),("Set Position","BOOL","none")], - "outputs" : [("Position","UINT","none"),("Position Changed","BOOL","none")], - "comment" : "SVGUI ScrollBar"}, - {"name" : "NoteBook", "type" : "functionBlock", "extensible" : False, - "inputs" : [("Selected","UINT","none"),("Set Selected","BOOL","none")], - "outputs" : [("Selected","UINT","none"),("Selected Changed","BOOL","none")], - "comment" : "SVGUI Notebook"}, - {"name" : "RotatingCtrl", "type" : "functionBlock", "extensible" : False, - "inputs" : [("Angle","FLOAT","none"),("Set Angle","BOOL","none")], - "outputs" : [("Angle","FLOAT","none"),("Angle changed","BOOL","none")], - "comment" : "SVGUI Rotating Control"}, - ]} ] + +PluginTypes = [] """ Function that returns the block definition associated to the block type given """ def GetBlockType(type, inputs = None): - for category in BlockTypes: + for category in BlockTypes + PluginTypes: for blocktype in category["list"]: if inputs: block_inputs = tuple([var_type for name, var_type, modifier in blocktype["inputs"]]) @@ -166,6 +195,12 @@ return blocktype return None +""" +Function that add a new plugin to the plugin list +""" + +def AddPlugin(blocklist): + PluginTypes.append(blocklist) #------------------------------------------------------------------------------- # Data Types definitions @@ -455,6 +490,7 @@ Function_decl_list = [] if Current_section: Function_decl = dict([(champ, val) for champ, val in zip(fonctions, fields[1:]) if champ]) + Function_decl["generate"] = generate_block baseinputnumber = int(Function_decl.get("baseinputnumber",1)) Function_decl["baseinputnumber"] = baseinputnumber for param, value in Function_decl.iteritems():