make#
Overview#
This example applies a design pattern to an existing project to create new operators and diagrams.
The design pattern used in this example is very simple. It outputs a structure flow containing pairs made of a default value and a status, depending on the input.
The input model contains the declaration of the output structure as well as the operator and its interface, with an empty graphical diagram:
package MyPackage
const
NCD : int32 = 2;
NORMAL : int32 = 4;
type
MyType = {
element1 : int32,
element1Status : int32,
element2 : bool,
element2Status : int32
};
function Root(isMaster : bool)
returns (out : MyType)
let
tel
end;
The script produces the following diagram:
The content of the script is described exhaustively hereafter.
Import directives and main#
The main
function allows the script to be used by the wrapper script:
import scade.model.suite as suite
import ansys.scade.apitools.create as create
import ansys.scade.apitools.query as query
def main():
"""Apply the design pattern to a given operator."""
# assume one and only one loaded project
model = suite.get_roots()[0].model
# fill the pattern with existing operator/constants
process_operator(model, 'MyPackage::Root/', 'MyPackage::NORMAL/', 'MyPackage::NCD/')
if __name__ == "__main__":
# launched from scade -script
main()
Checks#
The process_operator
function retrieves the model elements from
their path and verifies that they are consistent with the pattern to apply.
It then calls the fill_diagram
function to apply the pattern and save the
model before returning it:
def process_operator(model: suite.Model, name: str, name_then: str, name_else: str):
"""
Validate the input model and parameters before applying the pattern.
Parameters
----------
name : str
Path of the operator.
name_then : str
Path of the constant to be used as status if the input condition is true.
name_else : str
Path of the constant to be used as status if the input condition is false.
"""
# search the model for the operator
operator = model.get_object_from_path(name)
if not isinstance(operator, suite.Operator):
raise ValueError(name + ': Operator not found')
# get the first diagram of the operator (expect a graphical one)
diagram = None if len(operator.diagrams) == 0 else operator.diagrams[0]
if not isinstance(diagram, suite.NetDiagram):
raise ValueError(name + ': The first diagram is not a net diagram')
# get the output (expected unique output?)
output = None if len(operator.outputs) == 0 else operator.outputs[0]
if output is None:
raise ValueError(name + ': No output')
# the output's type must be a structure
if not query.is_structure(output.type):
raise ValueError(name + ': Incorrect return type')
# get the first input (at least one required)
input = None if len(operator.inputs) == 0 else operator.inputs[0]
if input is None:
raise ValueError(name + ': No input')
# get the constants to be used in the equation
cst_then = model.get_object_from_path(name_then)
if not isinstance(cst_then, suite.Constant):
raise ValueError(name_then + ': Constant not found')
cst_else = model.get_object_from_path(name_else)
if not isinstance(cst_else, suite.Constant):
raise ValueError(name_else + ': Constant not found')
# create the graphical expressions
fill_diagram(operator, diagram, input, output, cst_then, cst_else)
# save the modified files
create.save_all()
Diagram#
The fill_diagram
function creates the equations in the diagram at hard-coded positions.
There are as many textual expressions as there are elements in the structure type. These are a sequence of (default value, status).
Initialization#
Define the positions of the equations and default sizes:
def fill_diagram(
operator: suite.Operator,
diagram: suite.NetDiagram,
input: suite.LocalVariable,
output: suite.LocalVariable,
cst_then: suite.Constant,
cst_else: suite.Constant,
):
"""
Add the equations to the diagram.
Parameters
----------
operator : suite.Operator
Operator to complete.
diagram : suite.NetDiagram
Graphical diagram to modify.
input : suite.LocalVariable
Input defining the status.
output : suite.LocalVariable
Output to define.
cst_then : suite.Constant
Constant to be used as status when the input condition is true.
cst_else : suite.Constant
Constant to be used as status when the input condition is false.
"""
# get the structure type
struct = query.get_leaf_type(output.type)
count = len(struct.elements)
# sizes/positions of the different graphical objects used
x_make = 7000
y_make = 1000
w_make = 5000
element_gap = 650
h_make = count * element_gap
# use the default sizes of the SCADE Editor
w_text = 250
h_text = 500
w_output = 300
h_output = 500
Inputs of the make equation#
Create the equations and cache the defined flows in the parameters
list:
x_text = x_make - w_text - 1000
# parameters for make: list of the internal variables defined by the textual expressions
parameters = []
for index, field in enumerate(struct.elements):
# align the input equations with the input pins: trees and intervals...
y_text = y_make + h_make / (count + 1) * (index + 1) - h_text / 2
if index % 2:
# status
tree = create.create_if(input, cst_then, cst_else)
eq = create.add_data_def_equation(
operator,
diagram,
['int32'],
tree,
(x_text, y_text),
(w_text, h_text),
textual=True,
)
# retrieve the defined variable
parameters.append(eq.lefts[0])
else:
# provide a default value depending on the type, bool or integer in this example
value = False if query.get_leaf_type(field.type).name == 'bool' else 0
eq = create.add_data_def_equation(
operator, diagram, [field.type], value, (x_text, y_text), (w_text, h_text)
)
# retrieve the defined variable
parameters.append(eq.lefts[0])
Main equation#
Create the equation for make
, using the parameters defined previously:
tree = create.create_make(output.type, *parameters)
eq = create.add_data_def_equation(
operator, diagram, [output.type], tree, (x_make, y_make), (w_make, h_make)
)
# retrieve the defined variable
left_make = eq.lefts[0]
Output#
Create the equation defining the output:
x_output = x_make + w_make + 2500
y_output = y_make + h_make / 2 - h_output / 2
eq = create.add_data_def_equation(
operator, diagram, [output], left_make, (x_output, y_output), (w_output, h_output)
)
Edges#
Finally, create all the missing edges from the new equations:
create.add_diagram_missing_edges(diagram)