Class PluginContext
Defined in File plugin_context.hpp
Class Documentation
-
class PluginContext
Context interface providing plugins access to gateway data and utilities.
Passed to plugins during lifecycle via set_context(). Replaces the old set_node() by providing both ROS 2 node access and gateway-level abstractions.
- Thread Safety
All methods are safe to call from any thread. Entity and fault queries use the gateway’s thread-safe caches internally.
Note
This interface is versioned alongside PLUGIN_API_VERSION. New methods may be added in future versions (entity data access, configuration queries, etc.).
Public Functions
-
virtual ~PluginContext() = default
-
virtual rclcpp::Node *node() const = 0
Get ROS 2 node pointer for subscriptions, service clients, etc.
-
virtual std::optional<PluginEntityInfo> get_entity(const std::string &id) const = 0
Look up an entity by ID. Returns nullopt if not found.
-
virtual std::vector<PluginEntityInfo> get_child_apps(const std::string &component_id) const = 0
Get all Apps belonging to a Component (for aggregation endpoints)
-
virtual nlohmann::json list_entity_faults(const std::string &entity_id) const = 0
List faults for a given entity. Returns JSON array of fault objects. Empty array if entity has no faults or fault manager is unavailable.
-
virtual std::optional<PluginEntityInfo> validate_entity_for_route(const PluginRequest &req, PluginResponse &res, const std::string &entity_id) const = 0
Validate entity exists and matches route type, sending SOVD error if not.
Use this in get_routes() handlers to validate entity IDs from path params. On failure, an appropriate SOVD GenericError response is sent automatically.
- Parameters:
req – Plugin request (extracts expected entity type from path)
res – Plugin response (error sent here on failure)
entity_id – Entity ID from path parameter (e.g., req.path_param(1))
- Returns:
Entity info if valid, nullopt if error was sent
-
virtual void register_capability(SovdEntityType entity_type, const std::string &capability_name) = 0
Register a custom capability for all entities of a given type.
The capability will appear in the entity’s capabilities array with an auto-generated href. For example, registering “x-medkit-traces” for SovdEntityType::APP produces: {“name”: “x-medkit-traces”, “href”: “/api/v1/apps/{id}/x-medkit-traces”}
The plugin must also return a matching route from get_routes().
- Parameters:
entity_type – Entity type to add the capability to
capability_name – Capability name (use x- prefix for vendor extensions)
-
virtual void register_entity_capability(const std::string &entity_id, const std::string &capability_name) = 0
Register a custom capability for a specific entity.
Like register_capability(entity_type, name) but scoped to a single entity.
- Parameters:
entity_id – Specific entity ID
capability_name – Capability name (use x- prefix for vendor extensions)
-
virtual std::vector<std::string> get_type_capabilities(SovdEntityType entity_type) const = 0
Get plugin-registered capabilities for an entity type.
-
virtual std::vector<std::string> get_entity_capabilities(const std::string &entity_id) const = 0
Get plugin-registered capabilities for a specific entity.
-
virtual LockAccessResult check_lock(const std::string &entity_id, const std::string &client_id, const std::string &collection) const = 0
Check if a lock blocks access to a collection on an entity.
- Parameters:
entity_id – Entity to check
client_id – Client requesting access
collection – Resource collection being accessed (e.g. “configurations”)
- Returns:
LockAccessResult with allowed/denied status and details
-
virtual tl::expected<LockInfo, LockError> acquire_lock(const std::string &entity_id, const std::string &client_id, const std::vector<std::string> &scopes, int expiration_seconds) = 0
Acquire a lock on an entity.
-
virtual tl::expected<void, LockError> release_lock(const std::string &entity_id, const std::string &client_id) = 0
Release a lock on an entity.
- Parameters:
entity_id – Entity to unlock
client_id – Client releasing the lock
- Returns:
void on success, LockError on failure
-
inline virtual IntrospectionInput get_entity_snapshot() const
Get a snapshot of all discovered entities (areas, components, apps, functions). Returns an IntrospectionInput populated from the current entity cache.
-
inline virtual nlohmann::json list_all_faults() const
List all faults across all entities. Returns JSON with “faults” array. Empty object if fault manager is unavailable.
-
inline virtual void register_sampler(const std::string&, const std::function<tl::expected<nlohmann::json, std::string>(const std::string&, const std::string&)>&)
Register a cyclic subscription sampler for a custom collection.
-
virtual ResourceChangeNotifier *get_resource_change_notifier() = 0
Get the ResourceChangeNotifier for publishing or subscribing to resource changes.
Always returns a valid pointer - ResourceChangeNotifier is created unconditionally in GatewayNode regardless of trigger configuration.
-
virtual ConditionRegistry *get_condition_registry() = 0
Get the ConditionRegistry for registering custom trigger condition evaluators.
Always returns a valid pointer - ConditionRegistry is created unconditionally in GatewayNode regardless of trigger configuration.
-
inline virtual void notify_entities_changed(const EntityChangeScope&)
Tell the gateway that this plugin has finished mutating entities.
Call this AFTER any change that adds, removes, or restructures entities on disk or in the running runtime (deploying a new node under a manifest fragment, removing a previously-deployed app, renaming a component). The gateway responds by re-reading its manifest sources (including any configured fragments directory) and running a discovery pass for the affected scope. By the time this returns, subsequent
get_entityand HTTP discovery endpoints reflect the new surface.- Thread safety
Safe to call from any thread. The v7 reference implementation is SYNCHRONOUS: the caller’s thread drives the full manifest reload + discovery refresh before the call returns, under an internal refresh mutex that also serializes the periodic refresh timer. As a result:
the call may block for seconds on large manifests / slow plugins;
plugins MUST NOT hold their own mutexes across this call if any of their own callbacks (introspect, data/operation/fault providers) could try to reacquire that mutex - doing so deadlocks;
calling this method from within your own
IntrospectionProvider:: introspect()callback is detected and skipped with a warning log (seeGatewayNode::handle_entity_change_notification). The design assumes introspect already runs inside a refresh pass, so a nested notification would be redundant.
- Backwards compatibility
Default implementation is a no-op. Plugin source written against plugin API v6 compiles unchanged against v7 headers (source-compatible) - no code changes are required. The plugin loader compares the exported
plugin_api_version()against the gateway’sPLUGIN_API_VERSIONwith strict equality, so a plugin.sopre-compiled against v6 IS rejected; recompilation against v7 headers is required.- Fragment file contract (when used with discovery.manifest.fragments_dir)
Plugins that deploy / remove manifest fragments on disk before calling this method MUST publish the final content atomically. The gateway’s fragment scanner reads each file on the caller’s thread once the notification arrives; a partially-written fragment causes the reload to fail, which rolls back the entire manifest merge (see the design doc’s “all-or-nothing fragment contract” section). The recommended pattern is: write to
fragments_dir/.tmp-<id>.yaml,fsync, thenrename()(POSIX atomic within the same filesystem) tofragments_dir/<id>.yaml. The rename is the commit point that makes the fragment visible to the next reload.
- Parameters:
scope – Hint that lets the gateway limit the rediscovery work.
EntityChangeScope::full_refresh()asks for a global pass.