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 the Base 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: the header block in a state template is rendered into the header variable of either its parent template or the base template depending on its nesting depth,
  • the body block: the body block in a state template is rendered into the body variable of either its parent state or the base template depending on its nesting depth,
  • the footer block: the footer block in a state template is rendered into the footer 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.