Factory Demo (Python)

This tutorial demonstrates how to load YASMIN state machines from XML files using the YasminFactory. This approach separates the FSM structure from the code, making it easier to design, share, and modify state machines without recompiling.

Background

The YASMIN Factory provides:

1. Create an XML State Machine

Define your state machine structure in an XML file (e.g., demo_1.xml):

<StateMachine outcomes="outcome4">
    <State name="Foo" type="py" module="yasmin_demos.foo_state" class="FooState">
        <Outcome name="outcome1" target="Bar"/>
        <Outcome name="outcome2" target="outcome4"/>
    </State>
    
    <State name="Bar" type="py" module="yasmin_demos.bar_state" class="BarState">
        <Outcome name="outcome3" target="Foo"/>
    </State>
</StateMachine>

2. Load the State Machine with YasminFactory

Use the factory to create the FSM from the XML file. The YasminFactory class parses the XML structure and dynamically loads the state classes using Python's plugin system. It reads the XML file from the package's shared directory using get_package_share_directory, which ensures the path works regardless of installation location. The factory instantiates each state class specified in the XML (FooState and BarState), creates the transitions between states based on the <Outcome> tags, and assembles them into a fully functional StateMachine object. This approach separates FSM logic (in Python/C++ state classes) from FSM structure (in XML), enabling non-programmers to modify behavior by editing XML files while developers maintain reusable state implementations.

import os
import rclpy
import yasmin
from yasmin_ros import set_ros_loggers
from yasmin_viewer import YasminViewerPub
from yasmin_factory import YasminFactory
from ament_index_python import get_package_share_directory

yasmin.YASMIN_LOG_INFO("YASMIN_FACTORY_DEMO")

rclpy.init()
set_ros_loggers()

# Create factory and load state machine from XML
factory = YasminFactory()
sm = factory.create_sm_from_file(
    os.path.join(
        get_package_share_directory("yasmin_demos"), 
        "state_machines", 
        "demo_1.xml"
    )
)

3. Execute the State Machine

Run the state machine created from the XML file. The YasminViewerPub publishes the FSM structure to a ROS 2 topic, allowing the YASMIN Viewer web interface to display real-time execution flow. The execution follows the standard pattern: the try block runs the state machine through its states (Foo ↔ Bar transitions) until reaching a terminal outcome, the except catches keyboard interrupts and gracefully cancels execution, and the finally block ensures all resources are freed - the viewer's background publishing thread is stopped, the state machine is deleted to release memory, and ROS 2 is properly shut down. This demonstrates how XML-defined state machines execute identically to code-defined ones, with the same monitoring and error handling capabilities.

    # Publish FSM information for visualization
    viewer = YasminViewerPub(sm, "plugin_demo")

    # Execute the FSM
    try:
        outcome = sm()
        yasmin.YASMIN_LOG_INFO(outcome)
    except KeyboardInterrupt:
        if sm.is_running():
            sm.cancel_state()
    finally:
        viewer.cleanup()
        del sm

        # Shutdown ROS 2 if it's running
        if rclpy.ok():
            rclpy.shutdown()


if __name__ == "__main__":
    main()

4. Run the Demo

Execute the factory demo to see how state machines can be dynamically constructed from XML definitions, enabling configuration-driven behavior without code changes.

ros2 run yasmin_demos factory_demo.py

XML State Attributes

Attribute Description
name Unique identifier for the state
type "py" for Python or "cpp" for C++ states
module Python module path (for Python states)
class Class name of the state

Benefits of XML-Based FSMs

See Also