Template Anatomy¶
The Base.tpl.py
file looks like this:
{% block meta %}
name: Base
description: SMACH base template.
language: Python
framework: SMACH
type: Base
tags: [core]
includes: []
extends: []
variables:
- - manifest:
description: ROS manifest name.
type: str
- - node_name:
description: ROS node name for the state machine.
type: str
- outcomes:
description: A list of possible outcomes of the state machine.
type: list
- - userdata:
description: Definitions for userdata needed by child states.
type: dict
- - function_name:
description: A name for the main executable state machine function.
type: str
input_keys: []
output_keys: []
{% endblock meta %}
{% from "Utils.tpl.py" import import_module, render_outcomes, render_userdata %}
{% set defined_headers = [] %}
{% set local_vars = [] %}
{% block base_header %}
#!/usr/bin/env python
{{ base_header }}
{% endblock base_header %}
{% block imports %}
{{ import_module(defined_headers, 'smach') }}
{{ imports }}
{% endblock imports %}
{% block defs %}
{{ defs }}
{% endblock defs %}
{% block class_defs %}
{{ class_defs }}
{% endblock class_defs %}
{% block cb_defs %}
{{ cb_defs }}
{% endblock cb_defs %}
{% if name is defined %}{% set sm_name = name | lower() %}{% else %}{% set sm_name = 'sm' %}{% endif %}
{% block main_def %}
def {% if function_name is defined %}{{ function_name | lower() }}{% else %}main{% endif %}():
{{ main_def | indent(4) }}
{% endblock main_def %}
{% block body %}
{{ sm_name }} = smach.StateMachine({{ render_outcomes(outcomes) }})
{# Container header insertion variable indexed by container state name #}
{% if name in header %}{{ header[name] | indent(4, true) }}{% endif %}
{# Render container userdata #}
{% if userdata is defined %}{{ render_userdata(name | lower(), userdata) | indent(4) }}{% endif %}
{# Render state userdata #}
{% if name in header_userdata %}{{ header_userdata[name] | indent(4, true) }}{% endif %}
with {{ sm_name }}:
{# Container body insertion variable #}
{{ body | indent(8) }}
{% endblock body %}
{% block footer %}
{{ footer | indent(8) }}
{% endblock footer %}
{% block execute %}
{{ execute | indent(4) }}
outcome = {{ sm_name }}.execute()
{% endblock execute %}
{% block base_footer %}
{{ base_footer | indent(4) }}
{% endblock base_footer %}
{% block main %}
if __name__ == '__main__':
{{ '' | indent(4, true) }}{% if function_name is defined %}{{ function_name | lower() }}{% else %}main{% endif %}()
{% endblock main %}
The Meta Block¶
Core Variables¶
Some variables are quite important for core template functioning:
- the
name
variable: this is always filled with the state name from the script, - the
defined_headers
variable: this is a list that is defined at the top of theBase
template and is used to keep a record of the names of items that have been defined throughout the code generation process such that they are not re-defined, e.g. module imports, - the
local_vars
variable: this is a list that is defined at the top of various templates that is used to contain variable names in templates that should be non-persistent between states (i.e. local). Variables that are set in templates that are not contained in this list are assumed to be persistent between states (i.e. global) by default.
Core Blocks and Code Insertion Variables¶
There are a number of core blocks and code insertion variables present in the
Base
template above, as well as in many of the other core API state
templates, that enable the code generation engine to
produce code in the appropriate places:
- the
base_header
block: used to specify any code that must appear near the top of the program script, - the
imports
block: used to position module imports, - the
defs
block: used to position function definitions, - the
class_defs
block: used to position class definitions, - the
main_def
block: used to position the main function definition, - the
header
block: theheader
block in a state template is rendered into theheader
variable of either its parent template or the base template depending on its nesting depth, - the
body
block: thebody
block in a state template is rendered into thebody
variable of either its parent state or the base template depending on its nesting depth, - the
footer
block: thefooter
block in a state template is rendered into thefooter
variable of either its parent template or the base template depending on its nesting depth, - the
execute
block: used to position the code necessary for executing the state machine, - the
base_footer
block: used to specify any code that must appear near the bottom of the program script, - the
main
block: used to specify the code necessary to execute the main function.
Some additional blocks may be optionally included, such as the introspection_server and ROS spin blocks, if an introspection server is required for use with the SMACH viewer, or comment blocks, used to decorate the generated source code.
Note
All of the above code insertion variables and code blocks may be either removed, modified or arbitrarily customized within the API for particular use cases. The code insertion order may also be specified within the API, i.e. code may be either prepended or appended to a variable.