Files
ros2-assignments/doc/architecture/nodes/LifecycleManager.md

8.1 KiB

LifecycleManager (assignments::two::g2_2025_lifecycle_node)

Overview

The LifecycleManager is the lifecycle node responsible for receiving the data of the ESP32-IMU combination via serial/MQTT and publishing that data to the database management node. For the functionality outside of the lifecycle see HardwareInterface.md

Implementation Details

Parameters

  • device_path (string, default: "/dev/ttyUSB0"): Serial device path for hardware connection (e.g., USB serial adapter).
  • baudrate (int, default: 115200): Serial communication baud rate in bits per second.
  • comm_t (string, default: "serial"): Communication type selector—either "serial" or "mqtt".

Constructor

LifecycleManager()
  • Initializes ROS2 lifecycle node with name lifecycle_manager
  • Declares and reads configuration parameters: device_path, baudrate, and comm_t
  • Creates a shared instance of HardwareInterface for managing all hardware operations
  • Logs initialization status and readiness

Core Functions

CallbackReturn on_configure(const State&)

  • Enters the UnconfiguredInactive transition
  • Checks the comm_t parameter to route initialization:
    • MQTT mode: Calls hw_interface->mqtt_configure() to set up the MQTT client and broker connection
    • Serial mode: Calls hw_interface->open_device(device_path_, baudrate_) to open and configure the serial port
  • Returns SUCCESS if device initialization succeeds, FAILURE if serial/MQTT setup fails
  • Logs configuration status and any errors
  • Example test code (currently commented) demonstrates direct JSON parsing for validation

CallbackReturn on_activate(const State&)

  • Enters the InactiveActive transition
  • Checks the comm_t parameter to start the appropriate reader:
    • MQTT mode: Calls hw_interface->mqtt_reader() to attach MQTT callbacks and begin receiving messages
    • Serial mode: Calls hw_interface->start_read() to spawn a background thread that continuously reads from the serial device
  • Returns SUCCESS after reader startup
  • Logs activation status and selected communication type

CallbackReturn on_deactivate(const State&)

  • Enters the ActiveInactive transition
  • Checks the comm_t parameter to cleanly stop operations:
    • MQTT mode: Calls hw_interface->close_mqtt_conn() to disconnect from the broker and clean up resources
    • Serial mode:
      • Verifies device state with hw_interface->is_device_open()
      • Calls hw_interface->stop_read() to signal the reader thread to exit and joins it
      • Calls hw_interface->close_device() to release the serial port
  • Returns SUCCESS after cleanup completes
  • Logs deactivation and resource release

CallbackReturn on_shutdown(const State&)

  • Enters the InactiveFinalized transition
  • Performs final shutdown logging
  • Returns SUCCESS

CallbackReturn on_cleanup(const State&)

  • Called during error recovery or explicit cleanup commands
  • Performs resource cleanup and state logging
  • Returns SUCCESS

Communication Architecture

The LifecycleManager switches communication type depending on the comm_t parameter:

Serial Communication

  1. Configuration Phase (on_configure):

    • Opens the serial device at the path specified by device_path and baudrate
  2. Activation Phase (on_activate):

    • Spawns a background reader thread via hw_interface->start_read()
    • Continuously polls the serial device with a timeout
    • Reads are accumulated in a partial buffer, split on newline, and parsed as JSON
    • Each valid JSON IMU payload is parsed into a sensor_msgs::msg::Imu and published to the ROS topic imu/data
  3. Deactivation Phase (on_deactivate):

    • Signals the reader thread to stop via atomic flag
    • Joins the thread to ensure clean termination
    • Closes the serial device

MQTT Communication

  1. Configuration Phase (on_configure):

    • Creates a persistent MQTT async client pointing to the broker at SERVER_ADDRESS (default: tcp://localhost:1883)
    • Initializes MQTT callback infrastructure
  2. Activation Phase (on_activate):

    • Attaches MQTT callbacks to the client
    • Subscribes to the topic specified by TOPIC (default: esp32/imu)
    • The async client runs background threads to receive messages
  3. Deactivation Phase (on_deactivate):

    • Disconnects from the broker
    • Cleans up MQTT client and callback resources

Lifecycle Commands

To interact with the LifecycleManager from the command line, use the following ROS2 lifecycle service calls:

# List current lifecycle state
ros2 lifecycle list /lifecycle_manager

# Transition: UNCONFIGURED -> INACTIVE
ros2 lifecycle set /lifecycle_manager configure

# Transition: INACTIVE -> ACTIVE
ros2 lifecycle set /lifecycle_manager activate

# Transition: ACTIVE -> INACTIVE
ros2 lifecycle set /lifecycle_manager deactivate

# Transition: INACTIVE -> FINALIZED
ros2 lifecycle set /lifecycle_manager shutdown

Data Flow

Serial Data Flow

Hardware Device
      ↓
Serial Port (/dev/ttyUSB0)
      ↓
Background Reader Thread (start_read)
      ↓
Partial Buffer Accumulation
      ↓
JSON Line Extraction & Sanitization
      ↓
parse_data() ← Deserializes JSON to sensor_msgs::msg::Imu
      ↓
imu_publisher → ROS2 Topic (`imu_data`)

MQTT Data Flow

MQTT Broker (localhost:1883)
      ↓
MQTT Async Client (Background Thread)
      ↓
Subscription to Topic (esp32/imu)
      ↓
MQTT Callback Handler
      ↓
parse_data() ← Deserializes JSON to sensor_msgs::msg::Imu
      ↓
imu_publisher → ROS2 Topic (`imu_data`)

Error Handling

  • Serial Device Failures: If open_device() fails during configuration, on_configure() returns FAILURE and the system remains in the UNCONFIGURED state
  • Communication Errors: JSON parse errors from invalid payloads are caught and logged without crashing the node; the reader continues listening for the next message
  • Thread Safety: The reader thread uses an atomic flag (reading_) for clean stop signaling and ensures all resources are properly joined before returning from on_deactivate()

Design Patterns

  1. Strategy Pattern: The comm_t parameter enables runtime selection of communication backend without changing node code
  2. Lifecycle Pattern: Follows ROS2 managed node pattern for predictable initialization, startup, and shutdown sequences
  3. Thread Safety: Atomic flags and resource cleanup ensure the reader thread can be safely started and stopped
  4. Buffer Accumulation: Partial message buffering handles fragmented serial reads and ensures complete JSON objects are parsed

Integration with HardwareInterface

The LifecycleManager delegates all hardware operations to the HardwareInterface class:

Operation Method Lifecycle Phase
Open serial device open_device(path, baud) on_configure
Start reading start_read() on_activate
Stop reading stop_read() on_deactivate
Close serial device close_device() on_deactivate
Configure MQTT mqtt_configure() on_configure
Start MQTT reading mqtt_reader() on_activate
Close MQTT connection close_mqtt_conn() on_deactivate
Parse JSON payload parse_data(json_string) on_activate (continuous)
Publish IMU message publish_imu_data(imu_msg) on_activate (continuous)

Usage Example

# Launch the node with serial communication at /dev/ttyUSB0, 115200 baud
ros2 run g2_2025_imu_reader_pkg g2_2025_lifecycle_node \
  --ros-args \
  -p device_path:=/dev/ttyUSB0 \
  -p baudrate:=115200 \
  -p comm_t:=serial

# Alternatively, launch with MQTT communication
ros2 run g2_2025_imu_reader_pkg g2_2025_lifecycle_node \
  --ros-args \
  -p comm_t:=mqtt

# In another terminal, configure and activate the lifecycle
ros2 lifecycle set /lifecycle_manager configure
ros2 lifecycle set /lifecycle_manager activate

# Subscribe to published IMU data
ros2 topic echo /imu_data

# Deactivate and shutdown
ros2 lifecycle set /lifecycle_manager deactivate
ros2 lifecycle set /lifecycle_manager shutdown