ros_sugar.core.monitor#
Monitor
Module Contents#
Classes#
Monitor is a ROS2 Node (not Lifecycle) responsible of monitoring the status of the stack (rest of the running nodes) and managing requests/responses from the Orchestrator. |
Functions#
Why an action’s arguments cannot reach its component, or None. |
API#
- ros_sugar.core.monitor.unroutable_arguments(action) Optional[str]#
Why an action’s arguments cannot reach its component, or None.
An action of a component is run by that component, so its arguments are sent to it. ROS messages, arrays and bytes all travel, so what is left is a value written into the recipe that has no representation to send at all: an open socket, a file handle, an instance of a class only this process knows.
Only the values the recipe wrote are checked. An argument read from a topic is some field of a ROS message, and every one of those can be sent.
Worth catching before launch rather than at dispatch, because the step that carries it may be a fallback or an on_abort: one that runs only when something has already gone wrong.
- Parameters:
action – The action to check, a component action
- Return type:
Optional[str]
- class ros_sugar.core.monitor.Monitor(components_names: List[str], events_actions: Optional[Dict[ros_sugar.core.event.Event, List[ros_sugar.core.action.Action]]] = None, events_to_emit: Optional[List[ros_sugar.core.event.Event]] = None, config: Optional[ros_sugar.config.BaseConfig] = None, services_components: Optional[List[ros_sugar.core.component.BaseComponent]] = None, action_servers_components: Optional[List[ros_sugar.core.component.BaseComponent]] = None, activate_on_start: Optional[List[str]] = None, activation_timeout: Optional[float] = None, activation_attempt_time: float = 1.0, component_name: Optional[str] = None, action_registry: Optional[ros_sugar.core._action_registry.SystemActionRegistry] = None, **_)#
Bases:
rclpy.node.NodeMonitor is a ROS2 Node (not Lifecycle) responsible of monitoring the status of the stack (rest of the running nodes) and managing requests/responses from the Orchestrator.
Note
When launching the stack using the Launcher, the user is not required to configure the Monitor. The Launcher will configure and launch its own Monitor internally.
Main Functionalities:
Creates Subscribers to registered Events. The Monitor is configured to declare an InternalEvent back to the Launcher so the corresponding Action can be executed (see source implementation in launch_actions.py)
Creates Subscribers to all registered Components health status topics
Creates clients for all components main services and main action servers
Creates service clients to components reconfiguration services to handle actions sent from the Launcher
- add_internal_event_action_pair(event_id: str, action: ros_sugar.core.action.Action) None#
Adds an internal event action pair to the monitor configuration.
- Parameters:
event_id (str) – ID of the event to be monitored
action (Action) – Action to be executed on event trigger
- set_static_transforms(transforms) None#
Static transforms to broadcast once this node is up.
The Monitor is constructed while the launch description is still being built, before
rclpy_init_node, so it has no clock to stamp with and no publisher to send on yet. They are held here and sent fromactivate./tf_staticis latched, so subscribers that come up afterwards still receive them.- Parameters:
transforms –
TransformStampedlist, stamped on broadcast
- rclpy_init_node(*args, **kwargs)#
To init the node with rclpy and activate default services
- activate()#
Activate all subscribers/publishers/etc…
- watch_and_activate_component(component_name: str) None#
Poll for a respawned component and drive it back to the
activestate via direct lifecycle service calls once it is reachable.Called by the Launcher after a process-level respawn. Bypasses the
LifecycleTransitionlaunch action to avoid duplicate ChangeState dispatch from staleLifecycleEventManagerinstances left behind by the crashed process.
- configure_component(component: ros_sugar.core.component.BaseComponent, new_config: Union[object, str], keep_alive: bool) ros_sugar.utils.ActionReturnType#
Configure a given component from config instance or config file Creates and send the request to the component service
- Parameters:
component (BaseComponent) – Component to configure
config (object | str) – Config instance or path to config file
keep_alive (bool) – To keep the component running while configuring
executor (ROS Executor, optional) – Used to spin the monitor node until the service response is received, defaults to None
- update_parameter(component: Union[ros_sugar.core.component.BaseComponent, str], param_name: str, new_value: Any, keep_alive: bool = True) ros_sugar.utils.ActionReturnType#
Sends a ChangeParameter service request to given component
- Parameters:
component (Union[BaseComponent, str]) – description
param_name (str) – description
new_value (Any) – description
keep_alive (bool, optional) – description, defaults to True
- update_parameters(component: Union[ros_sugar.core.component.BaseComponent, str], params_names: List[str], new_values: List, keep_alive: bool = True, **_) ros_sugar.utils.ActionReturnType#
Sends a ChangeParameters service request to given component
- Parameters:
component (BaseComponent) – description
params_names (List[str]) – description
new_values (List) – description
keep_alive (bool, optional) – description, defaults to True
- get_action_client(action_name: str, action_type: type) ros_sugar.base_clients.ActionClientHandler#
Helper method to get a ros action client handler for the provided service name/type
- Parameters:
action_name (str) – Action name
action_type (type) – Action type (ROS2 action)
- Returns:
Action client handler
- Return type:
- get_component_action_client(component_name: str) ros_sugar.base_clients.ActionClientHandler#
Client for a component’s main action server
- Parameters:
component_name – Node name of the component
- Raises:
KeyError – If that component has no main action server, naming the ones that do
- Return type:
- send_srv_request(srv_request_msg: Any = None, srv_name: Optional[str] = None, srv_type: Optional[type] = None, **_) ros_sugar.utils.ActionReturnType#
Action to send a ROS2 service request during runtime
- Parameters:
srv_name (str) – Service name
srv_type (type) – Service type (ROS2 service)
srv_request_msg (Any) – Service request message
- Return type:
ActionReturnType
- send_action_goal(action_request_msg: Any = None, action_name: Optional[str] = None, action_type: Optional[type] = None, **_) ros_sugar.utils.ActionReturnType#
Action to send a ROS2 action goal during runtime
- Parameters:
action_name (str) – ROS2 action name
action_type (type) – ROS2 action type
action_request_msg (Any) – ROS2 action goal message
- Return type:
ActionReturnType
- send_component_action_goal(component_name: str, action_request_msg: Any = None, **_) Tuple[bool, str]#
Action to send a ROS2 action goal during runtime
- Parameters:
action_name (str) – ROS2 action name
action_type (type) – ROS2 action type
action_request_msg (Any) – ROS2 action goal message
- publish_message(topic: ros_sugar.io.topic.Topic, msg: Any, publish_rate: Optional[float] = None, publish_period: Optional[float] = None, **_) ros_sugar.utils.ActionReturnType#
Action to publish a message to a given topic
- Parameters:
topic (Topic) – Published topic
msg (Any) – Published message
publish_rate (Optional[float], optional) – Publishing rate, if None the message is published once, defaults to None
publish_period (Optional[float], optional) – Publishing period, if none and rate is given the message is published forever, defaults to None
- Return type:
ActionReturnType
- register_external_topic(topic: ros_sugar.io.topic.Topic) None#
Register a topic fed by a robot plugin feedback bus rather than a ROS subscription.
Events referencing this topic are still tracked and evaluated normally;
- Meth:
_activate_event_monitoringsimply skips creating a ROS subscription for it. Must be called before the Monitor is activated.- Parameters:
topic – The synthetic feedback topic (
Feedback.as_topic()).
- feed_external_topic(topic_name: str, msg: Any) None#
Inject a decoded non-ROS message into the event blackboard.
Called by a robot plugin HOST when it decodes a telemetry packet, so that Events and Conditions over plugin feedback topics are evaluated exactly as if a ROS message had arrived. Event topics declared with
use_pluginand served by this feedback receive it under their own name too.- Parameters:
topic_name – Synthetic topic name (the feedback’s bus channel).
msg – The decoded ROS message instance.
- set_plugins(plugins: Dict[str, Any]) None#
Hand the Monitor the recipe’s plugins, by id, so events on topics declared with
use_pluginare fed from what the plugin serves. Must be called before the Monitor is activated.NOTE: An attribute rather than a constructor argument, as downstream packages override
Launcher._init_monitor_nodeto build their own- Parameters:
plugins – The attached plugins, by id
- host_routines(routines: List[ros_sugar.core.routine.Routine]) None#
Host routines that no event triggers, once the Monitor is active.
For routines that are only ever started by name: from the UI, from the runtime API or from another routine. A routine an event also triggers is hosted once. Must be called before activation
- Parameters:
routines (List[Routine]) – The routines to host
- get_topics_snapshot() Dict[str, Any]#
The latest message seen on every topic the Monitor is watching.
Read by a routine when it enters a step, so a step acts on what is true when it runs rather than on what was true when the routine was triggered, which may be minutes earlier.
- Return type:
Dict[str, Any]
- wait(duration: float, **_) ros_sugar.utils.ActionReturnType#
Do nothing for a while, so a routine can dwell between steps.
A step that waits has no other spelling. Every other action settles as soon as it is called, so “wait here for thirty seconds” cannot be expressed as a timeout on one of them: the timeout would never be reached. Waiting on a condition is different and needs no help, since a step’s success condition already holds it open.
NOTE: this holds one of the dispatch pool’s workers while it waits. It is the seconds-to-minutes dwell of a mission, not a scheduler. A wait stopped with its step, by an abort, a pause or its routine being removed, returns within one slice and gives its worker back; waiting out the time used to keep that worker from every other action.
- Parameters:
duration – Seconds to wait
- Return type:
ActionReturnType
- add_event(event: ros_sugar.core.event.Event, actions: Union[ros_sugar.core.action.Action, ros_sugar.core.routine.Routine, List], event_id: Optional[str] = None, **_) ros_sugar.utils.ActionReturnType#
Start watching an event the recipe did not declare.
Everything
add_runtime_event_listenerskips, because that one exists only to let an action watch its own success condition: the actions are bound to this node, the topics their arguments read are subscribed alongside the condition’s, and an action based event gets its timer.- Parameters:
event – What to watch for
actions – What to do when it fires, one or several
event_id – Name to remove it by later, defaults to the event’s id
- Return type:
ActionReturnType
- remove_event(event_id: str, **_) ros_sugar.utils.ActionReturnType#
Stop watching an event that was added at runtime.
Its subscriptions stay: a topic is subscribed once and shared by every event reading it, so dropping one here would blind the others.
- Parameters:
event_id – The name it was registered under
- Return type:
ActionReturnType
- add_routine(routine: ros_sugar.core.routine.Routine, replace: bool = False, **_) ros_sugar.utils.ActionReturnType#
Take ownership of a routine that the recipe did not declare.
- Parameters:
routine – The routine to host
replace – Replace one already registered under this name
- Return type:
ActionReturnType
- remove_routine(routine_name: str, force: bool = False, **_) ros_sugar.utils.ActionReturnType#
Drop a routine and take its cursor topic down.
- Parameters:
routine_name – Name it was registered under
force – Remove it even if it is running, aborting it first. Without this a running routine is kept, because removing one mid-step would leave whatever it started running with nothing watching it. The
on_abortaction is not run: the routine, its cursor and its success watches are going away, so an action dispatched now would report into nothing
- Return type:
ActionReturnType
- set_action_registry(registry: ros_sugar.core._action_registry.SystemActionRegistry) None#
Install what the stack can be asked to do by name.
For the Launcher, when the Monitor that an override of its
_init_monitor_nodeinstalled was built without the registry. Must happen before activation, which is when anything is first resolved against it- Parameters:
registry (SystemActionRegistry) – The stack’s registry
- list_actions(**_) ros_sugar.utils.ActionReturnType#
Every action addressable by name, as JSON
- get_routines() List[Dict]#
Every registered routine: what it is for, and where it has got to.
Each entry is the routine’s state (its name, steps, status and so on) plus its
description, which is None if it was given none. Plain data, copied, so a caller can read it without reaching into the routines- Return type:
List[Dict]
- list_routines(**_) ros_sugar.utils.ActionReturnType#
Every registered routine, as
get_routinesgives it, as JSON
- list_plugin_events(**_) ros_sugar.utils.ActionReturnType#
Every condition a plugin offers, as JSON.
What an event spec’s ‘ref’ may name, as opposed to
list_events, which is what is being watched for right now
- list_events(**_) ros_sugar.utils.ActionReturnType#
Every event registered at runtime, as JSON
- start_routine(routine_name: str, **_) ros_sugar.utils.ActionReturnType#
Start a routine by name
- Parameters:
routine_name – Name the routine was declared with
- Return type:
ActionReturnType
- pause_routine(routine_name: str, **_) ros_sugar.utils.ActionReturnType#
Pause a running routine, preempting the step in flight
- Parameters:
routine_name – Name the routine was declared with
- Return type:
ActionReturnType
- resume_routine(routine_name: str, **_) ros_sugar.utils.ActionReturnType#
Resume a paused routine, re-entering the step it stopped at
- Parameters:
routine_name – Name the routine was declared with
- Return type:
ActionReturnType
- abort_routine(routine_name: str, reason: str = 'aborted by request', **_) ros_sugar.utils.ActionReturnType#
End a routine now, preempting the step in flight and running its on_abort
- Parameters:
routine_name – Name the routine was declared with
reason – Recorded in the cursor and logged
- Return type:
ActionReturnType
- get_routine_state(routine_name: str, **_) ros_sugar.utils.ActionReturnType#
Where a routine has got to, as JSON
- Parameters:
routine_name – Name the routine was declared with
- Return type:
ActionReturnType
- destroy_node()#
Abort the routines still running before the node goes away.
A step waiting on a goal would otherwise keep its dispatch worker, which the interpreter waits for at exit, until the step times out, and only then cancel the goal through a client that no longer exists.
- add_runtime_event_listener(event: ros_sugar.core.event.Event) None#
Start monitoring an event that was not known at activation.
Unlike the events wired up in
_activate_event_monitoring, this is called while the Monitor is already running, from a worker thread. Used by a monitoredActionto begin watching its success condition on first dispatch. A topic subscribed here is released byremove_runtime_event_listeneronce nothing reads it anymore.- Parameters:
event (Event) – Event to start monitoring
- remove_runtime_event_listener(event: ros_sugar.core.event.Event) None#
Stop monitoring an event added with
add_runtime_event_listener.A topic that was subscribed for runtime events is released along with the last event reading it, unless a registered routine still needs it. Topics subscribed at activation are kept.
- Parameters:
event (Event) – Event to stop monitoring