|
EasyFleet
A simple-by-default framework for multi-robot, multi-capability fleets built on ROS 2
|
Everything one robot's process needs: load its capabilities, bring them up, run them until shutdown, tear them down. More...
#include <deployment.hpp>
Public Member Functions | |
| Deployment () | |
| Robot identity inferred from this process's own ROS namespace (e.g. | |
| Deployment (std::string name) | |
| Constructs a Deployment with an explicit robot identity. | |
| const std::string & | name () const noexcept |
| This robot's identity, as passed to the constructor. | |
| void | add_capability (const std::string &plugin_lookup_name, const rclcpp::NodeOptions &options=rclcpp::NodeOptions()) |
| Loads, constructs and starts hosting one capability. | |
| const std::vector< easyfleet_core::CapabilityNodeBase::SharedPtr > & | capabilities () const noexcept |
| Every capability added so far, in add_capability() order. | |
| void | add_capabilities_from_parameters (const std::string &package_name) |
| Reads which capabilities to host from this process's own "capabilities" (string list) and "config_subdir" parameters – the same convention robot_node.cpp's own hardcoded factory function already reads by hand – resolves each one's capabilities_file JSON path (share/<package_name>/config/<config_subdir>/<name>.json, via ament_index_cpp), and calls add_capability() for each. | |
| void | start () |
| Configures, then activates, every added capability, in the order they were added. | |
| void | run () |
| Adds every capability's node to one shared executor and blocks until SIGINT/SIGTERM (see spin_until_shutdown()), then deactivates and cleans up every capability – in reverse order – before returning. | |
| void | shutdown () |
| Shuts down every capability's lifecycle node, then calls rclcpp::shutdown() – the ROS context teardown, distinct from run()'s own already-completed per-capability deactivate/cleanup. | |
Everything one robot's process needs: load its capabilities, bring them up, run them until shutdown, tear them down.
Replaces the longhand configure/check/activate/check/spin/deactivate/ cleanup/shutdown dance every robot_node.cpp currently repeats by hand, plus the hardcoded if (name == "perception") ... if (name == "manipulation") factory function each one writes to turn a configured name into a concrete capability class – add_capability() does that generically, by loading a pluginlib-registered CapabilityFactory (see capability_factory.hpp).
One Deployment per process, one process per robot – matching reality (a real fleet's robots each have their own onboard computer, so a deployment abstraction spanning several robots in one process would be modeling something that doesn't exist outside a single-machine sim). Each robot's identity comes from wherever the process itself is launched under a ROS namespace (a launch file's push_ros_namespace, or directly on a real robot's own computer) – add_capability() needs no namespace of its own to apply, capabilities just inherit the process's.
Usage, spelling capabilities out by hand:
Usage, letting parameters say which capabilities to load (see add_capabilities_from_parameters()) – the common case, and what makes one deployment.cpp-style executable reusable, unchanged, across every robot in a scenario:
| Deployment | ( | ) |
Robot identity inferred from this process's own ROS namespace (e.g.
a process launched under /robot_1 becomes "robot_1") – matching how that identity is already determined by however the process was launched, with no code needing to repeat it. The common case; see the class-level doc comment's second example.
|
explicit |
Constructs a Deployment with an explicit robot identity.
| name | This robot's identity, e.g. "robot_1" – used for log messages only. Not applied as a ROS namespace: that's already determined by however this process itself was launched (see the class-level doc comment). Prefer the no-arg constructor unless a name other than this process's own namespace is genuinely needed. |
| void add_capabilities_from_parameters | ( | const std::string & | package_name | ) |
Reads which capabilities to host from this process's own "capabilities" (string list) and "config_subdir" parameters – the same convention robot_node.cpp's own hardcoded factory function already reads by hand – resolves each one's capabilities_file JSON path (share/<package_name>/config/<config_subdir>/<name>.json, via ament_index_cpp), and calls add_capability() for each.
Safe to call only before start(), and safe to mix with explicit add_capability() calls (this just calls it in a loop). Exits the process (loud, matching start()'s own philosophy) if "capabilities" turns out empty – nothing configured to host is always a mistake, not a valid "do nothing" request.
| package_name | The package whose share/config/ directory holds each capability's <name>.json description – almost always the caller's own package. |
| void add_capability | ( | const std::string & | plugin_lookup_name, |
| const rclcpp::NodeOptions & | options = rclcpp::NodeOptions() ) |
Loads, constructs and starts hosting one capability.
Safe to call only before start().
| plugin_lookup_name | Pluginlib lookup name for a CapabilityFactory plugin – the exact string in that plugin's <class name="..."> entry, e.g. "perception/cats". The substring before the first / (or the whole string, if there is no /) becomes the capability's actual name – the ROS node name, the action name, and the capability identity field published on /capabilities – so "perception/cats" and "perception/dogs" both still announce themselves as simply "perception", matching whatever a mission script looks up regardless of which concrete implementation is behind it. |
| options | Extra node options (e.g. a capabilities_file parameter override – see any existing robot_node.cpp for how that path is computed) merged onto the capability's constructor call. |
| pluginlib::PluginlibException | if plugin_lookup_name isn't a registered CapabilityFactory plugin visible on this process's plugin search path (i.e. the owning package isn't a dependency, or its plugins.xml isn't exported/found). |
|
noexcept |
Every capability added so far, in add_capability() order.
|
noexcept |
This robot's identity, as passed to the constructor.
| void run | ( | ) |
Adds every capability's node to one shared executor and blocks until SIGINT/SIGTERM (see spin_until_shutdown()), then deactivates and cleans up every capability – in reverse order – before returning.
Requires init() to have been called (its non-default signal handling is what lets this shut lifecycle nodes down cleanly).
| void start | ( | ) |
Configures, then activates, every added capability, in the order they were added.
Fails loud, on purpose: the first capability that doesn't reach the expected state logs exactly which capability failed (and at which transition), then the process exits – a half-started robot silently running with one dead capability is worse than one that refuses to start at all, especially for a beginner who won't think to go looking for it.