Package Summary

Version 0.6.0
License Apache-2.0
Build type AMENT_CMAKE
Use RECOMMENDED

Repository Summary

Checkout URI https://github.com/selfpatch/ros2_medkit.git
VCS Type git
VCS Version main
Last Updated 2026-07-07
Dev Status DEVELOPED
Released RELEASED
Contributing Help Wanted (-)
Good First Issues (-)
Pull Requests to Review (-)

Package Description

Bridge node turning terminal ROS2 action goal states (aborted) into FaultManager faults

Maintainers

  • mfaferek93

Authors

No additional authors.

ros2_medkit_action_status_bridge

Generic drop-in bridge that turns terminal ROS 2 action goal states into structured medkit faults. It watches the /<action>/_action/status (action_msgs/msg/GoalStatusArray) topic that every action server publishes, so it works across Nav2, MoveIt 2, ros2_control’s controller actions, and any custom action with no per-project code.

Why this matters

For the core ROS 2 league the authoritative “the goal failed” verdict lives on the action-result channel, not on /diagnostics or /rosout:

  • Nav2 NavigateToPose -> GoalStatus = ABORTED
  • MoveIt MoveGroup -> GoalStatus = ABORTED (with MoveItErrorCode in the result)
  • ros2_control controller actions -> aborted goals

Neither the diagnostic bridge nor the log bridge sees this. This bridge does, generically, by observing the goal-status topic.

What it does

  • Discovers every action on the graph by scanning for */_action/status topics (re-scanned on a timer to catch actions that appear later, to drop actions that vanish, e.g. a Nav2 lifecycle deactivate or a one-shot node, and to retry any fault whose delivery to the FaultManager was deferred).
  • ABORTED (6) -> fault (SEVERITY_ERROR by default).
  • CANCELED (5) -> fault only if canceled_is_fault (off by default; cancel is usually intentional). When enabled it emits a _CANCELED code.
  • SUCCEEDED (4) -> PASSED to heal the action’s fault code (if enabled).
  • fault_code is <PREFIX>_<ACTION>_ABORTED (or _CANCELED), e.g. ACTION_NAVIGATE_TO_POSE_ABORTED. source_id is the action server’s node FQN (resolved from the status topic’s publisher, e.g. /bt_navigator), so the fault associates with that node’s SOVD entity; it falls back to the action name only if no publisher is visible.

Per-action state, not per-goal

The fault is a property of the action, not of an individual goal. On every status message the whole GoalStatusArray is scanned for the net state:

  • if any goal is ABORTED (or CANCELED when canceled_is_fault), the action is failed (order of goals in the array does not matter);
  • otherwise, if the array has terminal goals and none are failing, the action is healthy.

A fault is raised only on the healthy -> failed transition and healed only on failed -> healthy. This means one failed goal cannot heal an action while another goal is still failed, and a dropped terminal message cannot leave a fault stuck. Per-goal dedup (keyed on goal_id:status) only suppresses duplicate log lines; it never gates the transition.

The bridge tracks the latest observed state (the desired state) separately from what the FaultManager was actually told. The transition is committed only once the report is delivered: if the FaultManager service is not discovered yet

  • e.g. an action’s status topic is transient_local (latched) and its terminal status reaches the bridge during the startup discovery window, before the report client connects - the transition stays pending and is retried, rather than the high-severity fault being silently dropped. (The latched status is delivered to a subscription only once, so without this retry the dropped report would never recover.) See Delivery latency and freeze-frame.

Scope: the terminal verdict, not the reason

This bridge delivers the generic “it aborted” event. The action-specific reason (e.g. MoveItErrorCode.val = -26 START_STATE_IN_COLLISION, Nav2 error_code on Iron/Jazzy) lives in the action result message and is a separate enrichment concern (a future action-result reader using runtime message introspection / dynmsg). Per-project plugins are only needed for human-readable labels, not to surface the fault.

Delivery latency and freeze-frame

The FaultManager captures the freeze-frame (topic snapshot, rosbag) when it receives the report, so the value of that context decays with delivery latency: a late fault is reported against state that has already moved on. The bridge therefore optimises for delivering a fault to the FaultManager as soon as physically possible.

  • Happy path (service discovered): the fault is reported synchronously in the status callback, the instant the terminal status arrives. This is the normal runtime case and is already ASAP - nothing is deferred.
  • Degraded path (service not yet discovered): a fault cannot physically be delivered before the FaultManager report_fault service is discovered (startup before the FaultManager is up, or a FaultManager restart). The transition is kept pending and retried on a dedicated fast retry timer (retry_period_sec, default 50 ms), decoupled from the slow discovery rescan. So the fault lands within one short tick of the service appearing - keeping the freeze-frame as contemporaneous as the discovery floor allows.

We deliberately do not block the (single-threaded) executor waiting for the service: blocking would stall detection of other actions’ faults during an outage to shave the last few milliseconds off one fault - a bad trade when the freeze-frame is already only ~tens of ms fresher. The retry timer is armed only while a delivery is pending, so an idle bridge does no periodic work.

Known boundaries (narrow, and shrunk further by the fast retry; both are consequences of the level-triggered, net-state model, not silent loss in the common case):

File truncated at 100 lines see the full file

CHANGELOG

Changelog for package ros2_medkit_action_status_bridge

0.6.0 (2026-06-22)

  • Initial release: generic action-status bridge. Watches every /<action>/_action/status topic and turns ABORTED goals into FaultManager faults (<PREFIX>_<ACTION>_ABORTED), heals on a non-failing terminal state. Captures the terminal action verdict that the diagnostic and log bridges cannot see (#423).
  • Fault state is per-ACTION, derived from the whole GoalStatusArray on each message: a fault is raised only on the healthy -> failed transition and healed only on failed -> healthy, so it is order-independent and resilient to dropped terminal messages. Per-goal dedup now only suppresses duplicate log lines.
  • canceled_is_fault emits a status-aware <PREFIX>_<ACTION>_CANCELED code; such a fault heals on the next non-failing terminal or when the canceled goal ages out of the retained status array.
  • Vanished actions (lifecycle deactivate, one-shot nodes) are pruned on rescan.
  • Parameters are range-checked/normalized at load; an incompatible action status QoS is now warned about instead of silently dropping faults.
  • Ships a default configuration so the bridge starts out of the box (#459)
  • Fault source is fixed at first report instead of being re-attributed later: faults raised before discovery settles register the server FQN, faults are never attributed to the discovery placeholder node name, and the subscriptions and timer are reset in the destructor (#466)
  • Contributors: \@bburda, \@mfaferek93

Launch files

No launch files found

Messages

No message files found.

Services

No service files found

Plugins

No plugins found.

Recent questions tagged ros2_medkit_action_status_bridge at Robotics Stack Exchange

Package Summary

Version 0.6.0
License Apache-2.0
Build type AMENT_CMAKE
Use RECOMMENDED

Repository Summary

Checkout URI https://github.com/selfpatch/ros2_medkit.git
VCS Type git
VCS Version main
Last Updated 2026-07-07
Dev Status DEVELOPED
Released RELEASED
Contributing Help Wanted (-)
Good First Issues (-)
Pull Requests to Review (-)

Package Description

Bridge node turning terminal ROS2 action goal states (aborted) into FaultManager faults

Maintainers

  • mfaferek93

Authors

No additional authors.

ros2_medkit_action_status_bridge

Generic drop-in bridge that turns terminal ROS 2 action goal states into structured medkit faults. It watches the /<action>/_action/status (action_msgs/msg/GoalStatusArray) topic that every action server publishes, so it works across Nav2, MoveIt 2, ros2_control’s controller actions, and any custom action with no per-project code.

Why this matters

For the core ROS 2 league the authoritative “the goal failed” verdict lives on the action-result channel, not on /diagnostics or /rosout:

  • Nav2 NavigateToPose -> GoalStatus = ABORTED
  • MoveIt MoveGroup -> GoalStatus = ABORTED (with MoveItErrorCode in the result)
  • ros2_control controller actions -> aborted goals

Neither the diagnostic bridge nor the log bridge sees this. This bridge does, generically, by observing the goal-status topic.

What it does

  • Discovers every action on the graph by scanning for */_action/status topics (re-scanned on a timer to catch actions that appear later, to drop actions that vanish, e.g. a Nav2 lifecycle deactivate or a one-shot node, and to retry any fault whose delivery to the FaultManager was deferred).
  • ABORTED (6) -> fault (SEVERITY_ERROR by default).
  • CANCELED (5) -> fault only if canceled_is_fault (off by default; cancel is usually intentional). When enabled it emits a _CANCELED code.
  • SUCCEEDED (4) -> PASSED to heal the action’s fault code (if enabled).
  • fault_code is <PREFIX>_<ACTION>_ABORTED (or _CANCELED), e.g. ACTION_NAVIGATE_TO_POSE_ABORTED. source_id is the action server’s node FQN (resolved from the status topic’s publisher, e.g. /bt_navigator), so the fault associates with that node’s SOVD entity; it falls back to the action name only if no publisher is visible.

Per-action state, not per-goal

The fault is a property of the action, not of an individual goal. On every status message the whole GoalStatusArray is scanned for the net state:

  • if any goal is ABORTED (or CANCELED when canceled_is_fault), the action is failed (order of goals in the array does not matter);
  • otherwise, if the array has terminal goals and none are failing, the action is healthy.

A fault is raised only on the healthy -> failed transition and healed only on failed -> healthy. This means one failed goal cannot heal an action while another goal is still failed, and a dropped terminal message cannot leave a fault stuck. Per-goal dedup (keyed on goal_id:status) only suppresses duplicate log lines; it never gates the transition.

The bridge tracks the latest observed state (the desired state) separately from what the FaultManager was actually told. The transition is committed only once the report is delivered: if the FaultManager service is not discovered yet

  • e.g. an action’s status topic is transient_local (latched) and its terminal status reaches the bridge during the startup discovery window, before the report client connects - the transition stays pending and is retried, rather than the high-severity fault being silently dropped. (The latched status is delivered to a subscription only once, so without this retry the dropped report would never recover.) See Delivery latency and freeze-frame.

Scope: the terminal verdict, not the reason

This bridge delivers the generic “it aborted” event. The action-specific reason (e.g. MoveItErrorCode.val = -26 START_STATE_IN_COLLISION, Nav2 error_code on Iron/Jazzy) lives in the action result message and is a separate enrichment concern (a future action-result reader using runtime message introspection / dynmsg). Per-project plugins are only needed for human-readable labels, not to surface the fault.

Delivery latency and freeze-frame

The FaultManager captures the freeze-frame (topic snapshot, rosbag) when it receives the report, so the value of that context decays with delivery latency: a late fault is reported against state that has already moved on. The bridge therefore optimises for delivering a fault to the FaultManager as soon as physically possible.

  • Happy path (service discovered): the fault is reported synchronously in the status callback, the instant the terminal status arrives. This is the normal runtime case and is already ASAP - nothing is deferred.
  • Degraded path (service not yet discovered): a fault cannot physically be delivered before the FaultManager report_fault service is discovered (startup before the FaultManager is up, or a FaultManager restart). The transition is kept pending and retried on a dedicated fast retry timer (retry_period_sec, default 50 ms), decoupled from the slow discovery rescan. So the fault lands within one short tick of the service appearing - keeping the freeze-frame as contemporaneous as the discovery floor allows.

We deliberately do not block the (single-threaded) executor waiting for the service: blocking would stall detection of other actions’ faults during an outage to shave the last few milliseconds off one fault - a bad trade when the freeze-frame is already only ~tens of ms fresher. The retry timer is armed only while a delivery is pending, so an idle bridge does no periodic work.

Known boundaries (narrow, and shrunk further by the fast retry; both are consequences of the level-triggered, net-state model, not silent loss in the common case):

File truncated at 100 lines see the full file

CHANGELOG

Changelog for package ros2_medkit_action_status_bridge

0.6.0 (2026-06-22)

  • Initial release: generic action-status bridge. Watches every /<action>/_action/status topic and turns ABORTED goals into FaultManager faults (<PREFIX>_<ACTION>_ABORTED), heals on a non-failing terminal state. Captures the terminal action verdict that the diagnostic and log bridges cannot see (#423).
  • Fault state is per-ACTION, derived from the whole GoalStatusArray on each message: a fault is raised only on the healthy -> failed transition and healed only on failed -> healthy, so it is order-independent and resilient to dropped terminal messages. Per-goal dedup now only suppresses duplicate log lines.
  • canceled_is_fault emits a status-aware <PREFIX>_<ACTION>_CANCELED code; such a fault heals on the next non-failing terminal or when the canceled goal ages out of the retained status array.
  • Vanished actions (lifecycle deactivate, one-shot nodes) are pruned on rescan.
  • Parameters are range-checked/normalized at load; an incompatible action status QoS is now warned about instead of silently dropping faults.
  • Ships a default configuration so the bridge starts out of the box (#459)
  • Fault source is fixed at first report instead of being re-attributed later: faults raised before discovery settles register the server FQN, faults are never attributed to the discovery placeholder node name, and the subscriptions and timer are reset in the destructor (#466)
  • Contributors: \@bburda, \@mfaferek93

Launch files

No launch files found

Messages

No message files found.

Services

No service files found

Plugins

No plugins found.

Recent questions tagged ros2_medkit_action_status_bridge at Robotics Stack Exchange

No version for distro kilted showing humble. Known supported distros are highlighted in the buttons above.

Package Summary

Version 0.6.0
License Apache-2.0
Build type AMENT_CMAKE
Use RECOMMENDED

Repository Summary

Checkout URI https://github.com/selfpatch/ros2_medkit.git
VCS Type git
VCS Version main
Last Updated 2026-07-07
Dev Status DEVELOPED
Released RELEASED
Contributing Help Wanted (-)
Good First Issues (-)
Pull Requests to Review (-)

Package Description

Bridge node turning terminal ROS2 action goal states (aborted) into FaultManager faults

Maintainers

  • mfaferek93

Authors

No additional authors.

ros2_medkit_action_status_bridge

Generic drop-in bridge that turns terminal ROS 2 action goal states into structured medkit faults. It watches the /<action>/_action/status (action_msgs/msg/GoalStatusArray) topic that every action server publishes, so it works across Nav2, MoveIt 2, ros2_control’s controller actions, and any custom action with no per-project code.

Why this matters

For the core ROS 2 league the authoritative “the goal failed” verdict lives on the action-result channel, not on /diagnostics or /rosout:

  • Nav2 NavigateToPose -> GoalStatus = ABORTED
  • MoveIt MoveGroup -> GoalStatus = ABORTED (with MoveItErrorCode in the result)
  • ros2_control controller actions -> aborted goals

Neither the diagnostic bridge nor the log bridge sees this. This bridge does, generically, by observing the goal-status topic.

What it does

  • Discovers every action on the graph by scanning for */_action/status topics (re-scanned on a timer to catch actions that appear later, to drop actions that vanish, e.g. a Nav2 lifecycle deactivate or a one-shot node, and to retry any fault whose delivery to the FaultManager was deferred).
  • ABORTED (6) -> fault (SEVERITY_ERROR by default).
  • CANCELED (5) -> fault only if canceled_is_fault (off by default; cancel is usually intentional). When enabled it emits a _CANCELED code.
  • SUCCEEDED (4) -> PASSED to heal the action’s fault code (if enabled).
  • fault_code is <PREFIX>_<ACTION>_ABORTED (or _CANCELED), e.g. ACTION_NAVIGATE_TO_POSE_ABORTED. source_id is the action server’s node FQN (resolved from the status topic’s publisher, e.g. /bt_navigator), so the fault associates with that node’s SOVD entity; it falls back to the action name only if no publisher is visible.

Per-action state, not per-goal

The fault is a property of the action, not of an individual goal. On every status message the whole GoalStatusArray is scanned for the net state:

  • if any goal is ABORTED (or CANCELED when canceled_is_fault), the action is failed (order of goals in the array does not matter);
  • otherwise, if the array has terminal goals and none are failing, the action is healthy.

A fault is raised only on the healthy -> failed transition and healed only on failed -> healthy. This means one failed goal cannot heal an action while another goal is still failed, and a dropped terminal message cannot leave a fault stuck. Per-goal dedup (keyed on goal_id:status) only suppresses duplicate log lines; it never gates the transition.

The bridge tracks the latest observed state (the desired state) separately from what the FaultManager was actually told. The transition is committed only once the report is delivered: if the FaultManager service is not discovered yet

  • e.g. an action’s status topic is transient_local (latched) and its terminal status reaches the bridge during the startup discovery window, before the report client connects - the transition stays pending and is retried, rather than the high-severity fault being silently dropped. (The latched status is delivered to a subscription only once, so without this retry the dropped report would never recover.) See Delivery latency and freeze-frame.

Scope: the terminal verdict, not the reason

This bridge delivers the generic “it aborted” event. The action-specific reason (e.g. MoveItErrorCode.val = -26 START_STATE_IN_COLLISION, Nav2 error_code on Iron/Jazzy) lives in the action result message and is a separate enrichment concern (a future action-result reader using runtime message introspection / dynmsg). Per-project plugins are only needed for human-readable labels, not to surface the fault.

Delivery latency and freeze-frame

The FaultManager captures the freeze-frame (topic snapshot, rosbag) when it receives the report, so the value of that context decays with delivery latency: a late fault is reported against state that has already moved on. The bridge therefore optimises for delivering a fault to the FaultManager as soon as physically possible.

  • Happy path (service discovered): the fault is reported synchronously in the status callback, the instant the terminal status arrives. This is the normal runtime case and is already ASAP - nothing is deferred.
  • Degraded path (service not yet discovered): a fault cannot physically be delivered before the FaultManager report_fault service is discovered (startup before the FaultManager is up, or a FaultManager restart). The transition is kept pending and retried on a dedicated fast retry timer (retry_period_sec, default 50 ms), decoupled from the slow discovery rescan. So the fault lands within one short tick of the service appearing - keeping the freeze-frame as contemporaneous as the discovery floor allows.

We deliberately do not block the (single-threaded) executor waiting for the service: blocking would stall detection of other actions’ faults during an outage to shave the last few milliseconds off one fault - a bad trade when the freeze-frame is already only ~tens of ms fresher. The retry timer is armed only while a delivery is pending, so an idle bridge does no periodic work.

Known boundaries (narrow, and shrunk further by the fast retry; both are consequences of the level-triggered, net-state model, not silent loss in the common case):

File truncated at 100 lines see the full file

CHANGELOG

Changelog for package ros2_medkit_action_status_bridge

0.6.0 (2026-06-22)

  • Initial release: generic action-status bridge. Watches every /<action>/_action/status topic and turns ABORTED goals into FaultManager faults (<PREFIX>_<ACTION>_ABORTED), heals on a non-failing terminal state. Captures the terminal action verdict that the diagnostic and log bridges cannot see (#423).
  • Fault state is per-ACTION, derived from the whole GoalStatusArray on each message: a fault is raised only on the healthy -> failed transition and healed only on failed -> healthy, so it is order-independent and resilient to dropped terminal messages. Per-goal dedup now only suppresses duplicate log lines.
  • canceled_is_fault emits a status-aware <PREFIX>_<ACTION>_CANCELED code; such a fault heals on the next non-failing terminal or when the canceled goal ages out of the retained status array.
  • Vanished actions (lifecycle deactivate, one-shot nodes) are pruned on rescan.
  • Parameters are range-checked/normalized at load; an incompatible action status QoS is now warned about instead of silently dropping faults.
  • Ships a default configuration so the bridge starts out of the box (#459)
  • Fault source is fixed at first report instead of being re-attributed later: faults raised before discovery settles register the server FQN, faults are never attributed to the discovery placeholder node name, and the subscriptions and timer are reset in the destructor (#466)
  • Contributors: \@bburda, \@mfaferek93

Launch files

No launch files found

Messages

No message files found.

Services

No service files found

Plugins

No plugins found.

Recent questions tagged ros2_medkit_action_status_bridge at Robotics Stack Exchange

Package Summary

Version 0.6.0
License Apache-2.0
Build type AMENT_CMAKE
Use RECOMMENDED

Repository Summary

Checkout URI https://github.com/selfpatch/ros2_medkit.git
VCS Type git
VCS Version main
Last Updated 2026-07-07
Dev Status DEVELOPED
Released RELEASED
Contributing Help Wanted (-)
Good First Issues (-)
Pull Requests to Review (-)

Package Description

Bridge node turning terminal ROS2 action goal states (aborted) into FaultManager faults

Maintainers

  • mfaferek93

Authors

No additional authors.

ros2_medkit_action_status_bridge

Generic drop-in bridge that turns terminal ROS 2 action goal states into structured medkit faults. It watches the /<action>/_action/status (action_msgs/msg/GoalStatusArray) topic that every action server publishes, so it works across Nav2, MoveIt 2, ros2_control’s controller actions, and any custom action with no per-project code.

Why this matters

For the core ROS 2 league the authoritative “the goal failed” verdict lives on the action-result channel, not on /diagnostics or /rosout:

  • Nav2 NavigateToPose -> GoalStatus = ABORTED
  • MoveIt MoveGroup -> GoalStatus = ABORTED (with MoveItErrorCode in the result)
  • ros2_control controller actions -> aborted goals

Neither the diagnostic bridge nor the log bridge sees this. This bridge does, generically, by observing the goal-status topic.

What it does

  • Discovers every action on the graph by scanning for */_action/status topics (re-scanned on a timer to catch actions that appear later, to drop actions that vanish, e.g. a Nav2 lifecycle deactivate or a one-shot node, and to retry any fault whose delivery to the FaultManager was deferred).
  • ABORTED (6) -> fault (SEVERITY_ERROR by default).
  • CANCELED (5) -> fault only if canceled_is_fault (off by default; cancel is usually intentional). When enabled it emits a _CANCELED code.
  • SUCCEEDED (4) -> PASSED to heal the action’s fault code (if enabled).
  • fault_code is <PREFIX>_<ACTION>_ABORTED (or _CANCELED), e.g. ACTION_NAVIGATE_TO_POSE_ABORTED. source_id is the action server’s node FQN (resolved from the status topic’s publisher, e.g. /bt_navigator), so the fault associates with that node’s SOVD entity; it falls back to the action name only if no publisher is visible.

Per-action state, not per-goal

The fault is a property of the action, not of an individual goal. On every status message the whole GoalStatusArray is scanned for the net state:

  • if any goal is ABORTED (or CANCELED when canceled_is_fault), the action is failed (order of goals in the array does not matter);
  • otherwise, if the array has terminal goals and none are failing, the action is healthy.

A fault is raised only on the healthy -> failed transition and healed only on failed -> healthy. This means one failed goal cannot heal an action while another goal is still failed, and a dropped terminal message cannot leave a fault stuck. Per-goal dedup (keyed on goal_id:status) only suppresses duplicate log lines; it never gates the transition.

The bridge tracks the latest observed state (the desired state) separately from what the FaultManager was actually told. The transition is committed only once the report is delivered: if the FaultManager service is not discovered yet

  • e.g. an action’s status topic is transient_local (latched) and its terminal status reaches the bridge during the startup discovery window, before the report client connects - the transition stays pending and is retried, rather than the high-severity fault being silently dropped. (The latched status is delivered to a subscription only once, so without this retry the dropped report would never recover.) See Delivery latency and freeze-frame.

Scope: the terminal verdict, not the reason

This bridge delivers the generic “it aborted” event. The action-specific reason (e.g. MoveItErrorCode.val = -26 START_STATE_IN_COLLISION, Nav2 error_code on Iron/Jazzy) lives in the action result message and is a separate enrichment concern (a future action-result reader using runtime message introspection / dynmsg). Per-project plugins are only needed for human-readable labels, not to surface the fault.

Delivery latency and freeze-frame

The FaultManager captures the freeze-frame (topic snapshot, rosbag) when it receives the report, so the value of that context decays with delivery latency: a late fault is reported against state that has already moved on. The bridge therefore optimises for delivering a fault to the FaultManager as soon as physically possible.

  • Happy path (service discovered): the fault is reported synchronously in the status callback, the instant the terminal status arrives. This is the normal runtime case and is already ASAP - nothing is deferred.
  • Degraded path (service not yet discovered): a fault cannot physically be delivered before the FaultManager report_fault service is discovered (startup before the FaultManager is up, or a FaultManager restart). The transition is kept pending and retried on a dedicated fast retry timer (retry_period_sec, default 50 ms), decoupled from the slow discovery rescan. So the fault lands within one short tick of the service appearing - keeping the freeze-frame as contemporaneous as the discovery floor allows.

We deliberately do not block the (single-threaded) executor waiting for the service: blocking would stall detection of other actions’ faults during an outage to shave the last few milliseconds off one fault - a bad trade when the freeze-frame is already only ~tens of ms fresher. The retry timer is armed only while a delivery is pending, so an idle bridge does no periodic work.

Known boundaries (narrow, and shrunk further by the fast retry; both are consequences of the level-triggered, net-state model, not silent loss in the common case):

File truncated at 100 lines see the full file

CHANGELOG

Changelog for package ros2_medkit_action_status_bridge

0.6.0 (2026-06-22)

  • Initial release: generic action-status bridge. Watches every /<action>/_action/status topic and turns ABORTED goals into FaultManager faults (<PREFIX>_<ACTION>_ABORTED), heals on a non-failing terminal state. Captures the terminal action verdict that the diagnostic and log bridges cannot see (#423).
  • Fault state is per-ACTION, derived from the whole GoalStatusArray on each message: a fault is raised only on the healthy -> failed transition and healed only on failed -> healthy, so it is order-independent and resilient to dropped terminal messages. Per-goal dedup now only suppresses duplicate log lines.
  • canceled_is_fault emits a status-aware <PREFIX>_<ACTION>_CANCELED code; such a fault heals on the next non-failing terminal or when the canceled goal ages out of the retained status array.
  • Vanished actions (lifecycle deactivate, one-shot nodes) are pruned on rescan.
  • Parameters are range-checked/normalized at load; an incompatible action status QoS is now warned about instead of silently dropping faults.
  • Ships a default configuration so the bridge starts out of the box (#459)
  • Fault source is fixed at first report instead of being re-attributed later: faults raised before discovery settles register the server FQN, faults are never attributed to the discovery placeholder node name, and the subscriptions and timer are reset in the destructor (#466)
  • Contributors: \@bburda, \@mfaferek93

Launch files

No launch files found

Messages

No message files found.

Services

No service files found

Plugins

No plugins found.

Recent questions tagged ros2_medkit_action_status_bridge at Robotics Stack Exchange

No version for distro rolling showing humble. Known supported distros are highlighted in the buttons above.

Package Summary

Version 0.6.0
License Apache-2.0
Build type AMENT_CMAKE
Use RECOMMENDED

Repository Summary

Checkout URI https://github.com/selfpatch/ros2_medkit.git
VCS Type git
VCS Version main
Last Updated 2026-07-07
Dev Status DEVELOPED
Released RELEASED
Contributing Help Wanted (-)
Good First Issues (-)
Pull Requests to Review (-)

Package Description

Bridge node turning terminal ROS2 action goal states (aborted) into FaultManager faults

Maintainers

  • mfaferek93

Authors

No additional authors.

ros2_medkit_action_status_bridge

Generic drop-in bridge that turns terminal ROS 2 action goal states into structured medkit faults. It watches the /<action>/_action/status (action_msgs/msg/GoalStatusArray) topic that every action server publishes, so it works across Nav2, MoveIt 2, ros2_control’s controller actions, and any custom action with no per-project code.

Why this matters

For the core ROS 2 league the authoritative “the goal failed” verdict lives on the action-result channel, not on /diagnostics or /rosout:

  • Nav2 NavigateToPose -> GoalStatus = ABORTED
  • MoveIt MoveGroup -> GoalStatus = ABORTED (with MoveItErrorCode in the result)
  • ros2_control controller actions -> aborted goals

Neither the diagnostic bridge nor the log bridge sees this. This bridge does, generically, by observing the goal-status topic.

What it does

  • Discovers every action on the graph by scanning for */_action/status topics (re-scanned on a timer to catch actions that appear later, to drop actions that vanish, e.g. a Nav2 lifecycle deactivate or a one-shot node, and to retry any fault whose delivery to the FaultManager was deferred).
  • ABORTED (6) -> fault (SEVERITY_ERROR by default).
  • CANCELED (5) -> fault only if canceled_is_fault (off by default; cancel is usually intentional). When enabled it emits a _CANCELED code.
  • SUCCEEDED (4) -> PASSED to heal the action’s fault code (if enabled).
  • fault_code is <PREFIX>_<ACTION>_ABORTED (or _CANCELED), e.g. ACTION_NAVIGATE_TO_POSE_ABORTED. source_id is the action server’s node FQN (resolved from the status topic’s publisher, e.g. /bt_navigator), so the fault associates with that node’s SOVD entity; it falls back to the action name only if no publisher is visible.

Per-action state, not per-goal

The fault is a property of the action, not of an individual goal. On every status message the whole GoalStatusArray is scanned for the net state:

  • if any goal is ABORTED (or CANCELED when canceled_is_fault), the action is failed (order of goals in the array does not matter);
  • otherwise, if the array has terminal goals and none are failing, the action is healthy.

A fault is raised only on the healthy -> failed transition and healed only on failed -> healthy. This means one failed goal cannot heal an action while another goal is still failed, and a dropped terminal message cannot leave a fault stuck. Per-goal dedup (keyed on goal_id:status) only suppresses duplicate log lines; it never gates the transition.

The bridge tracks the latest observed state (the desired state) separately from what the FaultManager was actually told. The transition is committed only once the report is delivered: if the FaultManager service is not discovered yet

  • e.g. an action’s status topic is transient_local (latched) and its terminal status reaches the bridge during the startup discovery window, before the report client connects - the transition stays pending and is retried, rather than the high-severity fault being silently dropped. (The latched status is delivered to a subscription only once, so without this retry the dropped report would never recover.) See Delivery latency and freeze-frame.

Scope: the terminal verdict, not the reason

This bridge delivers the generic “it aborted” event. The action-specific reason (e.g. MoveItErrorCode.val = -26 START_STATE_IN_COLLISION, Nav2 error_code on Iron/Jazzy) lives in the action result message and is a separate enrichment concern (a future action-result reader using runtime message introspection / dynmsg). Per-project plugins are only needed for human-readable labels, not to surface the fault.

Delivery latency and freeze-frame

The FaultManager captures the freeze-frame (topic snapshot, rosbag) when it receives the report, so the value of that context decays with delivery latency: a late fault is reported against state that has already moved on. The bridge therefore optimises for delivering a fault to the FaultManager as soon as physically possible.

  • Happy path (service discovered): the fault is reported synchronously in the status callback, the instant the terminal status arrives. This is the normal runtime case and is already ASAP - nothing is deferred.
  • Degraded path (service not yet discovered): a fault cannot physically be delivered before the FaultManager report_fault service is discovered (startup before the FaultManager is up, or a FaultManager restart). The transition is kept pending and retried on a dedicated fast retry timer (retry_period_sec, default 50 ms), decoupled from the slow discovery rescan. So the fault lands within one short tick of the service appearing - keeping the freeze-frame as contemporaneous as the discovery floor allows.

We deliberately do not block the (single-threaded) executor waiting for the service: blocking would stall detection of other actions’ faults during an outage to shave the last few milliseconds off one fault - a bad trade when the freeze-frame is already only ~tens of ms fresher. The retry timer is armed only while a delivery is pending, so an idle bridge does no periodic work.

Known boundaries (narrow, and shrunk further by the fast retry; both are consequences of the level-triggered, net-state model, not silent loss in the common case):

File truncated at 100 lines see the full file

CHANGELOG

Changelog for package ros2_medkit_action_status_bridge

0.6.0 (2026-06-22)

  • Initial release: generic action-status bridge. Watches every /<action>/_action/status topic and turns ABORTED goals into FaultManager faults (<PREFIX>_<ACTION>_ABORTED), heals on a non-failing terminal state. Captures the terminal action verdict that the diagnostic and log bridges cannot see (#423).
  • Fault state is per-ACTION, derived from the whole GoalStatusArray on each message: a fault is raised only on the healthy -> failed transition and healed only on failed -> healthy, so it is order-independent and resilient to dropped terminal messages. Per-goal dedup now only suppresses duplicate log lines.
  • canceled_is_fault emits a status-aware <PREFIX>_<ACTION>_CANCELED code; such a fault heals on the next non-failing terminal or when the canceled goal ages out of the retained status array.
  • Vanished actions (lifecycle deactivate, one-shot nodes) are pruned on rescan.
  • Parameters are range-checked/normalized at load; an incompatible action status QoS is now warned about instead of silently dropping faults.
  • Ships a default configuration so the bridge starts out of the box (#459)
  • Fault source is fixed at first report instead of being re-attributed later: faults raised before discovery settles register the server FQN, faults are never attributed to the discovery placeholder node name, and the subscriptions and timer are reset in the destructor (#466)
  • Contributors: \@bburda, \@mfaferek93

Launch files

No launch files found

Messages

No message files found.

Services

No service files found

Plugins

No plugins found.

Recent questions tagged ros2_medkit_action_status_bridge at Robotics Stack Exchange

No version for distro ardent showing humble. Known supported distros are highlighted in the buttons above.

Package Summary

Version 0.6.0
License Apache-2.0
Build type AMENT_CMAKE
Use RECOMMENDED

Repository Summary

Checkout URI https://github.com/selfpatch/ros2_medkit.git
VCS Type git
VCS Version main
Last Updated 2026-07-07
Dev Status DEVELOPED
Released RELEASED
Contributing Help Wanted (-)
Good First Issues (-)
Pull Requests to Review (-)

Package Description

Bridge node turning terminal ROS2 action goal states (aborted) into FaultManager faults

Maintainers

  • mfaferek93

Authors

No additional authors.

ros2_medkit_action_status_bridge

Generic drop-in bridge that turns terminal ROS 2 action goal states into structured medkit faults. It watches the /<action>/_action/status (action_msgs/msg/GoalStatusArray) topic that every action server publishes, so it works across Nav2, MoveIt 2, ros2_control’s controller actions, and any custom action with no per-project code.

Why this matters

For the core ROS 2 league the authoritative “the goal failed” verdict lives on the action-result channel, not on /diagnostics or /rosout:

  • Nav2 NavigateToPose -> GoalStatus = ABORTED
  • MoveIt MoveGroup -> GoalStatus = ABORTED (with MoveItErrorCode in the result)
  • ros2_control controller actions -> aborted goals

Neither the diagnostic bridge nor the log bridge sees this. This bridge does, generically, by observing the goal-status topic.

What it does

  • Discovers every action on the graph by scanning for */_action/status topics (re-scanned on a timer to catch actions that appear later, to drop actions that vanish, e.g. a Nav2 lifecycle deactivate or a one-shot node, and to retry any fault whose delivery to the FaultManager was deferred).
  • ABORTED (6) -> fault (SEVERITY_ERROR by default).
  • CANCELED (5) -> fault only if canceled_is_fault (off by default; cancel is usually intentional). When enabled it emits a _CANCELED code.
  • SUCCEEDED (4) -> PASSED to heal the action’s fault code (if enabled).
  • fault_code is <PREFIX>_<ACTION>_ABORTED (or _CANCELED), e.g. ACTION_NAVIGATE_TO_POSE_ABORTED. source_id is the action server’s node FQN (resolved from the status topic’s publisher, e.g. /bt_navigator), so the fault associates with that node’s SOVD entity; it falls back to the action name only if no publisher is visible.

Per-action state, not per-goal

The fault is a property of the action, not of an individual goal. On every status message the whole GoalStatusArray is scanned for the net state:

  • if any goal is ABORTED (or CANCELED when canceled_is_fault), the action is failed (order of goals in the array does not matter);
  • otherwise, if the array has terminal goals and none are failing, the action is healthy.

A fault is raised only on the healthy -> failed transition and healed only on failed -> healthy. This means one failed goal cannot heal an action while another goal is still failed, and a dropped terminal message cannot leave a fault stuck. Per-goal dedup (keyed on goal_id:status) only suppresses duplicate log lines; it never gates the transition.

The bridge tracks the latest observed state (the desired state) separately from what the FaultManager was actually told. The transition is committed only once the report is delivered: if the FaultManager service is not discovered yet

  • e.g. an action’s status topic is transient_local (latched) and its terminal status reaches the bridge during the startup discovery window, before the report client connects - the transition stays pending and is retried, rather than the high-severity fault being silently dropped. (The latched status is delivered to a subscription only once, so without this retry the dropped report would never recover.) See Delivery latency and freeze-frame.

Scope: the terminal verdict, not the reason

This bridge delivers the generic “it aborted” event. The action-specific reason (e.g. MoveItErrorCode.val = -26 START_STATE_IN_COLLISION, Nav2 error_code on Iron/Jazzy) lives in the action result message and is a separate enrichment concern (a future action-result reader using runtime message introspection / dynmsg). Per-project plugins are only needed for human-readable labels, not to surface the fault.

Delivery latency and freeze-frame

The FaultManager captures the freeze-frame (topic snapshot, rosbag) when it receives the report, so the value of that context decays with delivery latency: a late fault is reported against state that has already moved on. The bridge therefore optimises for delivering a fault to the FaultManager as soon as physically possible.

  • Happy path (service discovered): the fault is reported synchronously in the status callback, the instant the terminal status arrives. This is the normal runtime case and is already ASAP - nothing is deferred.
  • Degraded path (service not yet discovered): a fault cannot physically be delivered before the FaultManager report_fault service is discovered (startup before the FaultManager is up, or a FaultManager restart). The transition is kept pending and retried on a dedicated fast retry timer (retry_period_sec, default 50 ms), decoupled from the slow discovery rescan. So the fault lands within one short tick of the service appearing - keeping the freeze-frame as contemporaneous as the discovery floor allows.

We deliberately do not block the (single-threaded) executor waiting for the service: blocking would stall detection of other actions’ faults during an outage to shave the last few milliseconds off one fault - a bad trade when the freeze-frame is already only ~tens of ms fresher. The retry timer is armed only while a delivery is pending, so an idle bridge does no periodic work.

Known boundaries (narrow, and shrunk further by the fast retry; both are consequences of the level-triggered, net-state model, not silent loss in the common case):

File truncated at 100 lines see the full file

CHANGELOG

Changelog for package ros2_medkit_action_status_bridge

0.6.0 (2026-06-22)

  • Initial release: generic action-status bridge. Watches every /<action>/_action/status topic and turns ABORTED goals into FaultManager faults (<PREFIX>_<ACTION>_ABORTED), heals on a non-failing terminal state. Captures the terminal action verdict that the diagnostic and log bridges cannot see (#423).
  • Fault state is per-ACTION, derived from the whole GoalStatusArray on each message: a fault is raised only on the healthy -> failed transition and healed only on failed -> healthy, so it is order-independent and resilient to dropped terminal messages. Per-goal dedup now only suppresses duplicate log lines.
  • canceled_is_fault emits a status-aware <PREFIX>_<ACTION>_CANCELED code; such a fault heals on the next non-failing terminal or when the canceled goal ages out of the retained status array.
  • Vanished actions (lifecycle deactivate, one-shot nodes) are pruned on rescan.
  • Parameters are range-checked/normalized at load; an incompatible action status QoS is now warned about instead of silently dropping faults.
  • Ships a default configuration so the bridge starts out of the box (#459)
  • Fault source is fixed at first report instead of being re-attributed later: faults raised before discovery settles register the server FQN, faults are never attributed to the discovery placeholder node name, and the subscriptions and timer are reset in the destructor (#466)
  • Contributors: \@bburda, \@mfaferek93

Launch files

No launch files found

Messages

No message files found.

Services

No service files found

Plugins

No plugins found.

Recent questions tagged ros2_medkit_action_status_bridge at Robotics Stack Exchange

No version for distro bouncy showing humble. Known supported distros are highlighted in the buttons above.

Package Summary

Version 0.6.0
License Apache-2.0
Build type AMENT_CMAKE
Use RECOMMENDED

Repository Summary

Checkout URI https://github.com/selfpatch/ros2_medkit.git
VCS Type git
VCS Version main
Last Updated 2026-07-07
Dev Status DEVELOPED
Released RELEASED
Contributing Help Wanted (-)
Good First Issues (-)
Pull Requests to Review (-)

Package Description

Bridge node turning terminal ROS2 action goal states (aborted) into FaultManager faults

Maintainers

  • mfaferek93

Authors

No additional authors.

ros2_medkit_action_status_bridge

Generic drop-in bridge that turns terminal ROS 2 action goal states into structured medkit faults. It watches the /<action>/_action/status (action_msgs/msg/GoalStatusArray) topic that every action server publishes, so it works across Nav2, MoveIt 2, ros2_control’s controller actions, and any custom action with no per-project code.

Why this matters

For the core ROS 2 league the authoritative “the goal failed” verdict lives on the action-result channel, not on /diagnostics or /rosout:

  • Nav2 NavigateToPose -> GoalStatus = ABORTED
  • MoveIt MoveGroup -> GoalStatus = ABORTED (with MoveItErrorCode in the result)
  • ros2_control controller actions -> aborted goals

Neither the diagnostic bridge nor the log bridge sees this. This bridge does, generically, by observing the goal-status topic.

What it does

  • Discovers every action on the graph by scanning for */_action/status topics (re-scanned on a timer to catch actions that appear later, to drop actions that vanish, e.g. a Nav2 lifecycle deactivate or a one-shot node, and to retry any fault whose delivery to the FaultManager was deferred).
  • ABORTED (6) -> fault (SEVERITY_ERROR by default).
  • CANCELED (5) -> fault only if canceled_is_fault (off by default; cancel is usually intentional). When enabled it emits a _CANCELED code.
  • SUCCEEDED (4) -> PASSED to heal the action’s fault code (if enabled).
  • fault_code is <PREFIX>_<ACTION>_ABORTED (or _CANCELED), e.g. ACTION_NAVIGATE_TO_POSE_ABORTED. source_id is the action server’s node FQN (resolved from the status topic’s publisher, e.g. /bt_navigator), so the fault associates with that node’s SOVD entity; it falls back to the action name only if no publisher is visible.

Per-action state, not per-goal

The fault is a property of the action, not of an individual goal. On every status message the whole GoalStatusArray is scanned for the net state:

  • if any goal is ABORTED (or CANCELED when canceled_is_fault), the action is failed (order of goals in the array does not matter);
  • otherwise, if the array has terminal goals and none are failing, the action is healthy.

A fault is raised only on the healthy -> failed transition and healed only on failed -> healthy. This means one failed goal cannot heal an action while another goal is still failed, and a dropped terminal message cannot leave a fault stuck. Per-goal dedup (keyed on goal_id:status) only suppresses duplicate log lines; it never gates the transition.

The bridge tracks the latest observed state (the desired state) separately from what the FaultManager was actually told. The transition is committed only once the report is delivered: if the FaultManager service is not discovered yet

  • e.g. an action’s status topic is transient_local (latched) and its terminal status reaches the bridge during the startup discovery window, before the report client connects - the transition stays pending and is retried, rather than the high-severity fault being silently dropped. (The latched status is delivered to a subscription only once, so without this retry the dropped report would never recover.) See Delivery latency and freeze-frame.

Scope: the terminal verdict, not the reason

This bridge delivers the generic “it aborted” event. The action-specific reason (e.g. MoveItErrorCode.val = -26 START_STATE_IN_COLLISION, Nav2 error_code on Iron/Jazzy) lives in the action result message and is a separate enrichment concern (a future action-result reader using runtime message introspection / dynmsg). Per-project plugins are only needed for human-readable labels, not to surface the fault.

Delivery latency and freeze-frame

The FaultManager captures the freeze-frame (topic snapshot, rosbag) when it receives the report, so the value of that context decays with delivery latency: a late fault is reported against state that has already moved on. The bridge therefore optimises for delivering a fault to the FaultManager as soon as physically possible.

  • Happy path (service discovered): the fault is reported synchronously in the status callback, the instant the terminal status arrives. This is the normal runtime case and is already ASAP - nothing is deferred.
  • Degraded path (service not yet discovered): a fault cannot physically be delivered before the FaultManager report_fault service is discovered (startup before the FaultManager is up, or a FaultManager restart). The transition is kept pending and retried on a dedicated fast retry timer (retry_period_sec, default 50 ms), decoupled from the slow discovery rescan. So the fault lands within one short tick of the service appearing - keeping the freeze-frame as contemporaneous as the discovery floor allows.

We deliberately do not block the (single-threaded) executor waiting for the service: blocking would stall detection of other actions’ faults during an outage to shave the last few milliseconds off one fault - a bad trade when the freeze-frame is already only ~tens of ms fresher. The retry timer is armed only while a delivery is pending, so an idle bridge does no periodic work.

Known boundaries (narrow, and shrunk further by the fast retry; both are consequences of the level-triggered, net-state model, not silent loss in the common case):

File truncated at 100 lines see the full file

CHANGELOG

Changelog for package ros2_medkit_action_status_bridge

0.6.0 (2026-06-22)

  • Initial release: generic action-status bridge. Watches every /<action>/_action/status topic and turns ABORTED goals into FaultManager faults (<PREFIX>_<ACTION>_ABORTED), heals on a non-failing terminal state. Captures the terminal action verdict that the diagnostic and log bridges cannot see (#423).
  • Fault state is per-ACTION, derived from the whole GoalStatusArray on each message: a fault is raised only on the healthy -> failed transition and healed only on failed -> healthy, so it is order-independent and resilient to dropped terminal messages. Per-goal dedup now only suppresses duplicate log lines.
  • canceled_is_fault emits a status-aware <PREFIX>_<ACTION>_CANCELED code; such a fault heals on the next non-failing terminal or when the canceled goal ages out of the retained status array.
  • Vanished actions (lifecycle deactivate, one-shot nodes) are pruned on rescan.
  • Parameters are range-checked/normalized at load; an incompatible action status QoS is now warned about instead of silently dropping faults.
  • Ships a default configuration so the bridge starts out of the box (#459)
  • Fault source is fixed at first report instead of being re-attributed later: faults raised before discovery settles register the server FQN, faults are never attributed to the discovery placeholder node name, and the subscriptions and timer are reset in the destructor (#466)
  • Contributors: \@bburda, \@mfaferek93

Launch files

No launch files found

Messages

No message files found.

Services

No service files found

Plugins

No plugins found.

Recent questions tagged ros2_medkit_action_status_bridge at Robotics Stack Exchange

No version for distro crystal showing humble. Known supported distros are highlighted in the buttons above.

Package Summary

Version 0.6.0
License Apache-2.0
Build type AMENT_CMAKE
Use RECOMMENDED

Repository Summary

Checkout URI https://github.com/selfpatch/ros2_medkit.git
VCS Type git
VCS Version main
Last Updated 2026-07-07
Dev Status DEVELOPED
Released RELEASED
Contributing Help Wanted (-)
Good First Issues (-)
Pull Requests to Review (-)

Package Description

Bridge node turning terminal ROS2 action goal states (aborted) into FaultManager faults

Maintainers

  • mfaferek93

Authors

No additional authors.

ros2_medkit_action_status_bridge

Generic drop-in bridge that turns terminal ROS 2 action goal states into structured medkit faults. It watches the /<action>/_action/status (action_msgs/msg/GoalStatusArray) topic that every action server publishes, so it works across Nav2, MoveIt 2, ros2_control’s controller actions, and any custom action with no per-project code.

Why this matters

For the core ROS 2 league the authoritative “the goal failed” verdict lives on the action-result channel, not on /diagnostics or /rosout:

  • Nav2 NavigateToPose -> GoalStatus = ABORTED
  • MoveIt MoveGroup -> GoalStatus = ABORTED (with MoveItErrorCode in the result)
  • ros2_control controller actions -> aborted goals

Neither the diagnostic bridge nor the log bridge sees this. This bridge does, generically, by observing the goal-status topic.

What it does

  • Discovers every action on the graph by scanning for */_action/status topics (re-scanned on a timer to catch actions that appear later, to drop actions that vanish, e.g. a Nav2 lifecycle deactivate or a one-shot node, and to retry any fault whose delivery to the FaultManager was deferred).
  • ABORTED (6) -> fault (SEVERITY_ERROR by default).
  • CANCELED (5) -> fault only if canceled_is_fault (off by default; cancel is usually intentional). When enabled it emits a _CANCELED code.
  • SUCCEEDED (4) -> PASSED to heal the action’s fault code (if enabled).
  • fault_code is <PREFIX>_<ACTION>_ABORTED (or _CANCELED), e.g. ACTION_NAVIGATE_TO_POSE_ABORTED. source_id is the action server’s node FQN (resolved from the status topic’s publisher, e.g. /bt_navigator), so the fault associates with that node’s SOVD entity; it falls back to the action name only if no publisher is visible.

Per-action state, not per-goal

The fault is a property of the action, not of an individual goal. On every status message the whole GoalStatusArray is scanned for the net state:

  • if any goal is ABORTED (or CANCELED when canceled_is_fault), the action is failed (order of goals in the array does not matter);
  • otherwise, if the array has terminal goals and none are failing, the action is healthy.

A fault is raised only on the healthy -> failed transition and healed only on failed -> healthy. This means one failed goal cannot heal an action while another goal is still failed, and a dropped terminal message cannot leave a fault stuck. Per-goal dedup (keyed on goal_id:status) only suppresses duplicate log lines; it never gates the transition.

The bridge tracks the latest observed state (the desired state) separately from what the FaultManager was actually told. The transition is committed only once the report is delivered: if the FaultManager service is not discovered yet

  • e.g. an action’s status topic is transient_local (latched) and its terminal status reaches the bridge during the startup discovery window, before the report client connects - the transition stays pending and is retried, rather than the high-severity fault being silently dropped. (The latched status is delivered to a subscription only once, so without this retry the dropped report would never recover.) See Delivery latency and freeze-frame.

Scope: the terminal verdict, not the reason

This bridge delivers the generic “it aborted” event. The action-specific reason (e.g. MoveItErrorCode.val = -26 START_STATE_IN_COLLISION, Nav2 error_code on Iron/Jazzy) lives in the action result message and is a separate enrichment concern (a future action-result reader using runtime message introspection / dynmsg). Per-project plugins are only needed for human-readable labels, not to surface the fault.

Delivery latency and freeze-frame

The FaultManager captures the freeze-frame (topic snapshot, rosbag) when it receives the report, so the value of that context decays with delivery latency: a late fault is reported against state that has already moved on. The bridge therefore optimises for delivering a fault to the FaultManager as soon as physically possible.

  • Happy path (service discovered): the fault is reported synchronously in the status callback, the instant the terminal status arrives. This is the normal runtime case and is already ASAP - nothing is deferred.
  • Degraded path (service not yet discovered): a fault cannot physically be delivered before the FaultManager report_fault service is discovered (startup before the FaultManager is up, or a FaultManager restart). The transition is kept pending and retried on a dedicated fast retry timer (retry_period_sec, default 50 ms), decoupled from the slow discovery rescan. So the fault lands within one short tick of the service appearing - keeping the freeze-frame as contemporaneous as the discovery floor allows.

We deliberately do not block the (single-threaded) executor waiting for the service: blocking would stall detection of other actions’ faults during an outage to shave the last few milliseconds off one fault - a bad trade when the freeze-frame is already only ~tens of ms fresher. The retry timer is armed only while a delivery is pending, so an idle bridge does no periodic work.

Known boundaries (narrow, and shrunk further by the fast retry; both are consequences of the level-triggered, net-state model, not silent loss in the common case):

File truncated at 100 lines see the full file

CHANGELOG

Changelog for package ros2_medkit_action_status_bridge

0.6.0 (2026-06-22)

  • Initial release: generic action-status bridge. Watches every /<action>/_action/status topic and turns ABORTED goals into FaultManager faults (<PREFIX>_<ACTION>_ABORTED), heals on a non-failing terminal state. Captures the terminal action verdict that the diagnostic and log bridges cannot see (#423).
  • Fault state is per-ACTION, derived from the whole GoalStatusArray on each message: a fault is raised only on the healthy -> failed transition and healed only on failed -> healthy, so it is order-independent and resilient to dropped terminal messages. Per-goal dedup now only suppresses duplicate log lines.
  • canceled_is_fault emits a status-aware <PREFIX>_<ACTION>_CANCELED code; such a fault heals on the next non-failing terminal or when the canceled goal ages out of the retained status array.
  • Vanished actions (lifecycle deactivate, one-shot nodes) are pruned on rescan.
  • Parameters are range-checked/normalized at load; an incompatible action status QoS is now warned about instead of silently dropping faults.
  • Ships a default configuration so the bridge starts out of the box (#459)
  • Fault source is fixed at first report instead of being re-attributed later: faults raised before discovery settles register the server FQN, faults are never attributed to the discovery placeholder node name, and the subscriptions and timer are reset in the destructor (#466)
  • Contributors: \@bburda, \@mfaferek93

Launch files

No launch files found

Messages

No message files found.

Services

No service files found

Plugins

No plugins found.

Recent questions tagged ros2_medkit_action_status_bridge at Robotics Stack Exchange

No version for distro eloquent showing humble. Known supported distros are highlighted in the buttons above.

Package Summary

Version 0.6.0
License Apache-2.0
Build type AMENT_CMAKE
Use RECOMMENDED

Repository Summary

Checkout URI https://github.com/selfpatch/ros2_medkit.git
VCS Type git
VCS Version main
Last Updated 2026-07-07
Dev Status DEVELOPED
Released RELEASED
Contributing Help Wanted (-)
Good First Issues (-)
Pull Requests to Review (-)

Package Description

Bridge node turning terminal ROS2 action goal states (aborted) into FaultManager faults

Maintainers

  • mfaferek93

Authors

No additional authors.

ros2_medkit_action_status_bridge

Generic drop-in bridge that turns terminal ROS 2 action goal states into structured medkit faults. It watches the /<action>/_action/status (action_msgs/msg/GoalStatusArray) topic that every action server publishes, so it works across Nav2, MoveIt 2, ros2_control’s controller actions, and any custom action with no per-project code.

Why this matters

For the core ROS 2 league the authoritative “the goal failed” verdict lives on the action-result channel, not on /diagnostics or /rosout:

  • Nav2 NavigateToPose -> GoalStatus = ABORTED
  • MoveIt MoveGroup -> GoalStatus = ABORTED (with MoveItErrorCode in the result)
  • ros2_control controller actions -> aborted goals

Neither the diagnostic bridge nor the log bridge sees this. This bridge does, generically, by observing the goal-status topic.

What it does

  • Discovers every action on the graph by scanning for */_action/status topics (re-scanned on a timer to catch actions that appear later, to drop actions that vanish, e.g. a Nav2 lifecycle deactivate or a one-shot node, and to retry any fault whose delivery to the FaultManager was deferred).
  • ABORTED (6) -> fault (SEVERITY_ERROR by default).
  • CANCELED (5) -> fault only if canceled_is_fault (off by default; cancel is usually intentional). When enabled it emits a _CANCELED code.
  • SUCCEEDED (4) -> PASSED to heal the action’s fault code (if enabled).
  • fault_code is <PREFIX>_<ACTION>_ABORTED (or _CANCELED), e.g. ACTION_NAVIGATE_TO_POSE_ABORTED. source_id is the action server’s node FQN (resolved from the status topic’s publisher, e.g. /bt_navigator), so the fault associates with that node’s SOVD entity; it falls back to the action name only if no publisher is visible.

Per-action state, not per-goal

The fault is a property of the action, not of an individual goal. On every status message the whole GoalStatusArray is scanned for the net state:

  • if any goal is ABORTED (or CANCELED when canceled_is_fault), the action is failed (order of goals in the array does not matter);
  • otherwise, if the array has terminal goals and none are failing, the action is healthy.

A fault is raised only on the healthy -> failed transition and healed only on failed -> healthy. This means one failed goal cannot heal an action while another goal is still failed, and a dropped terminal message cannot leave a fault stuck. Per-goal dedup (keyed on goal_id:status) only suppresses duplicate log lines; it never gates the transition.

The bridge tracks the latest observed state (the desired state) separately from what the FaultManager was actually told. The transition is committed only once the report is delivered: if the FaultManager service is not discovered yet

  • e.g. an action’s status topic is transient_local (latched) and its terminal status reaches the bridge during the startup discovery window, before the report client connects - the transition stays pending and is retried, rather than the high-severity fault being silently dropped. (The latched status is delivered to a subscription only once, so without this retry the dropped report would never recover.) See Delivery latency and freeze-frame.

Scope: the terminal verdict, not the reason

This bridge delivers the generic “it aborted” event. The action-specific reason (e.g. MoveItErrorCode.val = -26 START_STATE_IN_COLLISION, Nav2 error_code on Iron/Jazzy) lives in the action result message and is a separate enrichment concern (a future action-result reader using runtime message introspection / dynmsg). Per-project plugins are only needed for human-readable labels, not to surface the fault.

Delivery latency and freeze-frame

The FaultManager captures the freeze-frame (topic snapshot, rosbag) when it receives the report, so the value of that context decays with delivery latency: a late fault is reported against state that has already moved on. The bridge therefore optimises for delivering a fault to the FaultManager as soon as physically possible.

  • Happy path (service discovered): the fault is reported synchronously in the status callback, the instant the terminal status arrives. This is the normal runtime case and is already ASAP - nothing is deferred.
  • Degraded path (service not yet discovered): a fault cannot physically be delivered before the FaultManager report_fault service is discovered (startup before the FaultManager is up, or a FaultManager restart). The transition is kept pending and retried on a dedicated fast retry timer (retry_period_sec, default 50 ms), decoupled from the slow discovery rescan. So the fault lands within one short tick of the service appearing - keeping the freeze-frame as contemporaneous as the discovery floor allows.

We deliberately do not block the (single-threaded) executor waiting for the service: blocking would stall detection of other actions’ faults during an outage to shave the last few milliseconds off one fault - a bad trade when the freeze-frame is already only ~tens of ms fresher. The retry timer is armed only while a delivery is pending, so an idle bridge does no periodic work.

Known boundaries (narrow, and shrunk further by the fast retry; both are consequences of the level-triggered, net-state model, not silent loss in the common case):

File truncated at 100 lines see the full file

CHANGELOG

Changelog for package ros2_medkit_action_status_bridge

0.6.0 (2026-06-22)

  • Initial release: generic action-status bridge. Watches every /<action>/_action/status topic and turns ABORTED goals into FaultManager faults (<PREFIX>_<ACTION>_ABORTED), heals on a non-failing terminal state. Captures the terminal action verdict that the diagnostic and log bridges cannot see (#423).
  • Fault state is per-ACTION, derived from the whole GoalStatusArray on each message: a fault is raised only on the healthy -> failed transition and healed only on failed -> healthy, so it is order-independent and resilient to dropped terminal messages. Per-goal dedup now only suppresses duplicate log lines.
  • canceled_is_fault emits a status-aware <PREFIX>_<ACTION>_CANCELED code; such a fault heals on the next non-failing terminal or when the canceled goal ages out of the retained status array.
  • Vanished actions (lifecycle deactivate, one-shot nodes) are pruned on rescan.
  • Parameters are range-checked/normalized at load; an incompatible action status QoS is now warned about instead of silently dropping faults.
  • Ships a default configuration so the bridge starts out of the box (#459)
  • Fault source is fixed at first report instead of being re-attributed later: faults raised before discovery settles register the server FQN, faults are never attributed to the discovery placeholder node name, and the subscriptions and timer are reset in the destructor (#466)
  • Contributors: \@bburda, \@mfaferek93

Launch files

No launch files found

Messages

No message files found.

Services

No service files found

Plugins

No plugins found.

Recent questions tagged ros2_medkit_action_status_bridge at Robotics Stack Exchange

No version for distro dashing showing humble. Known supported distros are highlighted in the buttons above.

Package Summary

Version 0.6.0
License Apache-2.0
Build type AMENT_CMAKE
Use RECOMMENDED

Repository Summary

Checkout URI https://github.com/selfpatch/ros2_medkit.git
VCS Type git
VCS Version main
Last Updated 2026-07-07
Dev Status DEVELOPED
Released RELEASED
Contributing Help Wanted (-)
Good First Issues (-)
Pull Requests to Review (-)

Package Description

Bridge node turning terminal ROS2 action goal states (aborted) into FaultManager faults

Maintainers

  • mfaferek93

Authors

No additional authors.

ros2_medkit_action_status_bridge

Generic drop-in bridge that turns terminal ROS 2 action goal states into structured medkit faults. It watches the /<action>/_action/status (action_msgs/msg/GoalStatusArray) topic that every action server publishes, so it works across Nav2, MoveIt 2, ros2_control’s controller actions, and any custom action with no per-project code.

Why this matters

For the core ROS 2 league the authoritative “the goal failed” verdict lives on the action-result channel, not on /diagnostics or /rosout:

  • Nav2 NavigateToPose -> GoalStatus = ABORTED
  • MoveIt MoveGroup -> GoalStatus = ABORTED (with MoveItErrorCode in the result)
  • ros2_control controller actions -> aborted goals

Neither the diagnostic bridge nor the log bridge sees this. This bridge does, generically, by observing the goal-status topic.

What it does

  • Discovers every action on the graph by scanning for */_action/status topics (re-scanned on a timer to catch actions that appear later, to drop actions that vanish, e.g. a Nav2 lifecycle deactivate or a one-shot node, and to retry any fault whose delivery to the FaultManager was deferred).
  • ABORTED (6) -> fault (SEVERITY_ERROR by default).
  • CANCELED (5) -> fault only if canceled_is_fault (off by default; cancel is usually intentional). When enabled it emits a _CANCELED code.
  • SUCCEEDED (4) -> PASSED to heal the action’s fault code (if enabled).
  • fault_code is <PREFIX>_<ACTION>_ABORTED (or _CANCELED), e.g. ACTION_NAVIGATE_TO_POSE_ABORTED. source_id is the action server’s node FQN (resolved from the status topic’s publisher, e.g. /bt_navigator), so the fault associates with that node’s SOVD entity; it falls back to the action name only if no publisher is visible.

Per-action state, not per-goal

The fault is a property of the action, not of an individual goal. On every status message the whole GoalStatusArray is scanned for the net state:

  • if any goal is ABORTED (or CANCELED when canceled_is_fault), the action is failed (order of goals in the array does not matter);
  • otherwise, if the array has terminal goals and none are failing, the action is healthy.

A fault is raised only on the healthy -> failed transition and healed only on failed -> healthy. This means one failed goal cannot heal an action while another goal is still failed, and a dropped terminal message cannot leave a fault stuck. Per-goal dedup (keyed on goal_id:status) only suppresses duplicate log lines; it never gates the transition.

The bridge tracks the latest observed state (the desired state) separately from what the FaultManager was actually told. The transition is committed only once the report is delivered: if the FaultManager service is not discovered yet

  • e.g. an action’s status topic is transient_local (latched) and its terminal status reaches the bridge during the startup discovery window, before the report client connects - the transition stays pending and is retried, rather than the high-severity fault being silently dropped. (The latched status is delivered to a subscription only once, so without this retry the dropped report would never recover.) See Delivery latency and freeze-frame.

Scope: the terminal verdict, not the reason

This bridge delivers the generic “it aborted” event. The action-specific reason (e.g. MoveItErrorCode.val = -26 START_STATE_IN_COLLISION, Nav2 error_code on Iron/Jazzy) lives in the action result message and is a separate enrichment concern (a future action-result reader using runtime message introspection / dynmsg). Per-project plugins are only needed for human-readable labels, not to surface the fault.

Delivery latency and freeze-frame

The FaultManager captures the freeze-frame (topic snapshot, rosbag) when it receives the report, so the value of that context decays with delivery latency: a late fault is reported against state that has already moved on. The bridge therefore optimises for delivering a fault to the FaultManager as soon as physically possible.

  • Happy path (service discovered): the fault is reported synchronously in the status callback, the instant the terminal status arrives. This is the normal runtime case and is already ASAP - nothing is deferred.
  • Degraded path (service not yet discovered): a fault cannot physically be delivered before the FaultManager report_fault service is discovered (startup before the FaultManager is up, or a FaultManager restart). The transition is kept pending and retried on a dedicated fast retry timer (retry_period_sec, default 50 ms), decoupled from the slow discovery rescan. So the fault lands within one short tick of the service appearing - keeping the freeze-frame as contemporaneous as the discovery floor allows.

We deliberately do not block the (single-threaded) executor waiting for the service: blocking would stall detection of other actions’ faults during an outage to shave the last few milliseconds off one fault - a bad trade when the freeze-frame is already only ~tens of ms fresher. The retry timer is armed only while a delivery is pending, so an idle bridge does no periodic work.

Known boundaries (narrow, and shrunk further by the fast retry; both are consequences of the level-triggered, net-state model, not silent loss in the common case):

File truncated at 100 lines see the full file

CHANGELOG

Changelog for package ros2_medkit_action_status_bridge

0.6.0 (2026-06-22)

  • Initial release: generic action-status bridge. Watches every /<action>/_action/status topic and turns ABORTED goals into FaultManager faults (<PREFIX>_<ACTION>_ABORTED), heals on a non-failing terminal state. Captures the terminal action verdict that the diagnostic and log bridges cannot see (#423).
  • Fault state is per-ACTION, derived from the whole GoalStatusArray on each message: a fault is raised only on the healthy -> failed transition and healed only on failed -> healthy, so it is order-independent and resilient to dropped terminal messages. Per-goal dedup now only suppresses duplicate log lines.
  • canceled_is_fault emits a status-aware <PREFIX>_<ACTION>_CANCELED code; such a fault heals on the next non-failing terminal or when the canceled goal ages out of the retained status array.
  • Vanished actions (lifecycle deactivate, one-shot nodes) are pruned on rescan.
  • Parameters are range-checked/normalized at load; an incompatible action status QoS is now warned about instead of silently dropping faults.
  • Ships a default configuration so the bridge starts out of the box (#459)
  • Fault source is fixed at first report instead of being re-attributed later: faults raised before discovery settles register the server FQN, faults are never attributed to the discovery placeholder node name, and the subscriptions and timer are reset in the destructor (#466)
  • Contributors: \@bburda, \@mfaferek93

Launch files

No launch files found

Messages

No message files found.

Services

No service files found

Plugins

No plugins found.

Recent questions tagged ros2_medkit_action_status_bridge at Robotics Stack Exchange

No version for distro galactic showing humble. Known supported distros are highlighted in the buttons above.

Package Summary

Version 0.6.0
License Apache-2.0
Build type AMENT_CMAKE
Use RECOMMENDED

Repository Summary

Checkout URI https://github.com/selfpatch/ros2_medkit.git
VCS Type git
VCS Version main
Last Updated 2026-07-07
Dev Status DEVELOPED
Released RELEASED
Contributing Help Wanted (-)
Good First Issues (-)
Pull Requests to Review (-)

Package Description

Bridge node turning terminal ROS2 action goal states (aborted) into FaultManager faults

Maintainers

  • mfaferek93

Authors

No additional authors.

ros2_medkit_action_status_bridge

Generic drop-in bridge that turns terminal ROS 2 action goal states into structured medkit faults. It watches the /<action>/_action/status (action_msgs/msg/GoalStatusArray) topic that every action server publishes, so it works across Nav2, MoveIt 2, ros2_control’s controller actions, and any custom action with no per-project code.

Why this matters

For the core ROS 2 league the authoritative “the goal failed” verdict lives on the action-result channel, not on /diagnostics or /rosout:

  • Nav2 NavigateToPose -> GoalStatus = ABORTED
  • MoveIt MoveGroup -> GoalStatus = ABORTED (with MoveItErrorCode in the result)
  • ros2_control controller actions -> aborted goals

Neither the diagnostic bridge nor the log bridge sees this. This bridge does, generically, by observing the goal-status topic.

What it does

  • Discovers every action on the graph by scanning for */_action/status topics (re-scanned on a timer to catch actions that appear later, to drop actions that vanish, e.g. a Nav2 lifecycle deactivate or a one-shot node, and to retry any fault whose delivery to the FaultManager was deferred).
  • ABORTED (6) -> fault (SEVERITY_ERROR by default).
  • CANCELED (5) -> fault only if canceled_is_fault (off by default; cancel is usually intentional). When enabled it emits a _CANCELED code.
  • SUCCEEDED (4) -> PASSED to heal the action’s fault code (if enabled).
  • fault_code is <PREFIX>_<ACTION>_ABORTED (or _CANCELED), e.g. ACTION_NAVIGATE_TO_POSE_ABORTED. source_id is the action server’s node FQN (resolved from the status topic’s publisher, e.g. /bt_navigator), so the fault associates with that node’s SOVD entity; it falls back to the action name only if no publisher is visible.

Per-action state, not per-goal

The fault is a property of the action, not of an individual goal. On every status message the whole GoalStatusArray is scanned for the net state:

  • if any goal is ABORTED (or CANCELED when canceled_is_fault), the action is failed (order of goals in the array does not matter);
  • otherwise, if the array has terminal goals and none are failing, the action is healthy.

A fault is raised only on the healthy -> failed transition and healed only on failed -> healthy. This means one failed goal cannot heal an action while another goal is still failed, and a dropped terminal message cannot leave a fault stuck. Per-goal dedup (keyed on goal_id:status) only suppresses duplicate log lines; it never gates the transition.

The bridge tracks the latest observed state (the desired state) separately from what the FaultManager was actually told. The transition is committed only once the report is delivered: if the FaultManager service is not discovered yet

  • e.g. an action’s status topic is transient_local (latched) and its terminal status reaches the bridge during the startup discovery window, before the report client connects - the transition stays pending and is retried, rather than the high-severity fault being silently dropped. (The latched status is delivered to a subscription only once, so without this retry the dropped report would never recover.) See Delivery latency and freeze-frame.

Scope: the terminal verdict, not the reason

This bridge delivers the generic “it aborted” event. The action-specific reason (e.g. MoveItErrorCode.val = -26 START_STATE_IN_COLLISION, Nav2 error_code on Iron/Jazzy) lives in the action result message and is a separate enrichment concern (a future action-result reader using runtime message introspection / dynmsg). Per-project plugins are only needed for human-readable labels, not to surface the fault.

Delivery latency and freeze-frame

The FaultManager captures the freeze-frame (topic snapshot, rosbag) when it receives the report, so the value of that context decays with delivery latency: a late fault is reported against state that has already moved on. The bridge therefore optimises for delivering a fault to the FaultManager as soon as physically possible.

  • Happy path (service discovered): the fault is reported synchronously in the status callback, the instant the terminal status arrives. This is the normal runtime case and is already ASAP - nothing is deferred.
  • Degraded path (service not yet discovered): a fault cannot physically be delivered before the FaultManager report_fault service is discovered (startup before the FaultManager is up, or a FaultManager restart). The transition is kept pending and retried on a dedicated fast retry timer (retry_period_sec, default 50 ms), decoupled from the slow discovery rescan. So the fault lands within one short tick of the service appearing - keeping the freeze-frame as contemporaneous as the discovery floor allows.

We deliberately do not block the (single-threaded) executor waiting for the service: blocking would stall detection of other actions’ faults during an outage to shave the last few milliseconds off one fault - a bad trade when the freeze-frame is already only ~tens of ms fresher. The retry timer is armed only while a delivery is pending, so an idle bridge does no periodic work.

Known boundaries (narrow, and shrunk further by the fast retry; both are consequences of the level-triggered, net-state model, not silent loss in the common case):

File truncated at 100 lines see the full file

CHANGELOG

Changelog for package ros2_medkit_action_status_bridge

0.6.0 (2026-06-22)

  • Initial release: generic action-status bridge. Watches every /<action>/_action/status topic and turns ABORTED goals into FaultManager faults (<PREFIX>_<ACTION>_ABORTED), heals on a non-failing terminal state. Captures the terminal action verdict that the diagnostic and log bridges cannot see (#423).
  • Fault state is per-ACTION, derived from the whole GoalStatusArray on each message: a fault is raised only on the healthy -> failed transition and healed only on failed -> healthy, so it is order-independent and resilient to dropped terminal messages. Per-goal dedup now only suppresses duplicate log lines.
  • canceled_is_fault emits a status-aware <PREFIX>_<ACTION>_CANCELED code; such a fault heals on the next non-failing terminal or when the canceled goal ages out of the retained status array.
  • Vanished actions (lifecycle deactivate, one-shot nodes) are pruned on rescan.
  • Parameters are range-checked/normalized at load; an incompatible action status QoS is now warned about instead of silently dropping faults.
  • Ships a default configuration so the bridge starts out of the box (#459)
  • Fault source is fixed at first report instead of being re-attributed later: faults raised before discovery settles register the server FQN, faults are never attributed to the discovery placeholder node name, and the subscriptions and timer are reset in the destructor (#466)
  • Contributors: \@bburda, \@mfaferek93

Launch files

No launch files found

Messages

No message files found.

Services

No service files found

Plugins

No plugins found.

Recent questions tagged ros2_medkit_action_status_bridge at Robotics Stack Exchange

No version for distro foxy showing humble. Known supported distros are highlighted in the buttons above.

Package Summary

Version 0.6.0
License Apache-2.0
Build type AMENT_CMAKE
Use RECOMMENDED

Repository Summary

Checkout URI https://github.com/selfpatch/ros2_medkit.git
VCS Type git
VCS Version main
Last Updated 2026-07-07
Dev Status DEVELOPED
Released RELEASED
Contributing Help Wanted (-)
Good First Issues (-)
Pull Requests to Review (-)

Package Description

Bridge node turning terminal ROS2 action goal states (aborted) into FaultManager faults

Maintainers

  • mfaferek93

Authors

No additional authors.

ros2_medkit_action_status_bridge

Generic drop-in bridge that turns terminal ROS 2 action goal states into structured medkit faults. It watches the /<action>/_action/status (action_msgs/msg/GoalStatusArray) topic that every action server publishes, so it works across Nav2, MoveIt 2, ros2_control’s controller actions, and any custom action with no per-project code.

Why this matters

For the core ROS 2 league the authoritative “the goal failed” verdict lives on the action-result channel, not on /diagnostics or /rosout:

  • Nav2 NavigateToPose -> GoalStatus = ABORTED
  • MoveIt MoveGroup -> GoalStatus = ABORTED (with MoveItErrorCode in the result)
  • ros2_control controller actions -> aborted goals

Neither the diagnostic bridge nor the log bridge sees this. This bridge does, generically, by observing the goal-status topic.

What it does

  • Discovers every action on the graph by scanning for */_action/status topics (re-scanned on a timer to catch actions that appear later, to drop actions that vanish, e.g. a Nav2 lifecycle deactivate or a one-shot node, and to retry any fault whose delivery to the FaultManager was deferred).
  • ABORTED (6) -> fault (SEVERITY_ERROR by default).
  • CANCELED (5) -> fault only if canceled_is_fault (off by default; cancel is usually intentional). When enabled it emits a _CANCELED code.
  • SUCCEEDED (4) -> PASSED to heal the action’s fault code (if enabled).
  • fault_code is <PREFIX>_<ACTION>_ABORTED (or _CANCELED), e.g. ACTION_NAVIGATE_TO_POSE_ABORTED. source_id is the action server’s node FQN (resolved from the status topic’s publisher, e.g. /bt_navigator), so the fault associates with that node’s SOVD entity; it falls back to the action name only if no publisher is visible.

Per-action state, not per-goal

The fault is a property of the action, not of an individual goal. On every status message the whole GoalStatusArray is scanned for the net state:

  • if any goal is ABORTED (or CANCELED when canceled_is_fault), the action is failed (order of goals in the array does not matter);
  • otherwise, if the array has terminal goals and none are failing, the action is healthy.

A fault is raised only on the healthy -> failed transition and healed only on failed -> healthy. This means one failed goal cannot heal an action while another goal is still failed, and a dropped terminal message cannot leave a fault stuck. Per-goal dedup (keyed on goal_id:status) only suppresses duplicate log lines; it never gates the transition.

The bridge tracks the latest observed state (the desired state) separately from what the FaultManager was actually told. The transition is committed only once the report is delivered: if the FaultManager service is not discovered yet

  • e.g. an action’s status topic is transient_local (latched) and its terminal status reaches the bridge during the startup discovery window, before the report client connects - the transition stays pending and is retried, rather than the high-severity fault being silently dropped. (The latched status is delivered to a subscription only once, so without this retry the dropped report would never recover.) See Delivery latency and freeze-frame.

Scope: the terminal verdict, not the reason

This bridge delivers the generic “it aborted” event. The action-specific reason (e.g. MoveItErrorCode.val = -26 START_STATE_IN_COLLISION, Nav2 error_code on Iron/Jazzy) lives in the action result message and is a separate enrichment concern (a future action-result reader using runtime message introspection / dynmsg). Per-project plugins are only needed for human-readable labels, not to surface the fault.

Delivery latency and freeze-frame

The FaultManager captures the freeze-frame (topic snapshot, rosbag) when it receives the report, so the value of that context decays with delivery latency: a late fault is reported against state that has already moved on. The bridge therefore optimises for delivering a fault to the FaultManager as soon as physically possible.

  • Happy path (service discovered): the fault is reported synchronously in the status callback, the instant the terminal status arrives. This is the normal runtime case and is already ASAP - nothing is deferred.
  • Degraded path (service not yet discovered): a fault cannot physically be delivered before the FaultManager report_fault service is discovered (startup before the FaultManager is up, or a FaultManager restart). The transition is kept pending and retried on a dedicated fast retry timer (retry_period_sec, default 50 ms), decoupled from the slow discovery rescan. So the fault lands within one short tick of the service appearing - keeping the freeze-frame as contemporaneous as the discovery floor allows.

We deliberately do not block the (single-threaded) executor waiting for the service: blocking would stall detection of other actions’ faults during an outage to shave the last few milliseconds off one fault - a bad trade when the freeze-frame is already only ~tens of ms fresher. The retry timer is armed only while a delivery is pending, so an idle bridge does no periodic work.

Known boundaries (narrow, and shrunk further by the fast retry; both are consequences of the level-triggered, net-state model, not silent loss in the common case):

File truncated at 100 lines see the full file

CHANGELOG

Changelog for package ros2_medkit_action_status_bridge

0.6.0 (2026-06-22)

  • Initial release: generic action-status bridge. Watches every /<action>/_action/status topic and turns ABORTED goals into FaultManager faults (<PREFIX>_<ACTION>_ABORTED), heals on a non-failing terminal state. Captures the terminal action verdict that the diagnostic and log bridges cannot see (#423).
  • Fault state is per-ACTION, derived from the whole GoalStatusArray on each message: a fault is raised only on the healthy -> failed transition and healed only on failed -> healthy, so it is order-independent and resilient to dropped terminal messages. Per-goal dedup now only suppresses duplicate log lines.
  • canceled_is_fault emits a status-aware <PREFIX>_<ACTION>_CANCELED code; such a fault heals on the next non-failing terminal or when the canceled goal ages out of the retained status array.
  • Vanished actions (lifecycle deactivate, one-shot nodes) are pruned on rescan.
  • Parameters are range-checked/normalized at load; an incompatible action status QoS is now warned about instead of silently dropping faults.
  • Ships a default configuration so the bridge starts out of the box (#459)
  • Fault source is fixed at first report instead of being re-attributed later: faults raised before discovery settles register the server FQN, faults are never attributed to the discovery placeholder node name, and the subscriptions and timer are reset in the destructor (#466)
  • Contributors: \@bburda, \@mfaferek93

Launch files

No launch files found

Messages

No message files found.

Services

No service files found

Plugins

No plugins found.

Recent questions tagged ros2_medkit_action_status_bridge at Robotics Stack Exchange

No version for distro iron showing humble. Known supported distros are highlighted in the buttons above.

Package Summary

Version 0.6.0
License Apache-2.0
Build type AMENT_CMAKE
Use RECOMMENDED

Repository Summary

Checkout URI https://github.com/selfpatch/ros2_medkit.git
VCS Type git
VCS Version main
Last Updated 2026-07-07
Dev Status DEVELOPED
Released RELEASED
Contributing Help Wanted (-)
Good First Issues (-)
Pull Requests to Review (-)

Package Description

Bridge node turning terminal ROS2 action goal states (aborted) into FaultManager faults

Maintainers

  • mfaferek93

Authors

No additional authors.

ros2_medkit_action_status_bridge

Generic drop-in bridge that turns terminal ROS 2 action goal states into structured medkit faults. It watches the /<action>/_action/status (action_msgs/msg/GoalStatusArray) topic that every action server publishes, so it works across Nav2, MoveIt 2, ros2_control’s controller actions, and any custom action with no per-project code.

Why this matters

For the core ROS 2 league the authoritative “the goal failed” verdict lives on the action-result channel, not on /diagnostics or /rosout:

  • Nav2 NavigateToPose -> GoalStatus = ABORTED
  • MoveIt MoveGroup -> GoalStatus = ABORTED (with MoveItErrorCode in the result)
  • ros2_control controller actions -> aborted goals

Neither the diagnostic bridge nor the log bridge sees this. This bridge does, generically, by observing the goal-status topic.

What it does

  • Discovers every action on the graph by scanning for */_action/status topics (re-scanned on a timer to catch actions that appear later, to drop actions that vanish, e.g. a Nav2 lifecycle deactivate or a one-shot node, and to retry any fault whose delivery to the FaultManager was deferred).
  • ABORTED (6) -> fault (SEVERITY_ERROR by default).
  • CANCELED (5) -> fault only if canceled_is_fault (off by default; cancel is usually intentional). When enabled it emits a _CANCELED code.
  • SUCCEEDED (4) -> PASSED to heal the action’s fault code (if enabled).
  • fault_code is <PREFIX>_<ACTION>_ABORTED (or _CANCELED), e.g. ACTION_NAVIGATE_TO_POSE_ABORTED. source_id is the action server’s node FQN (resolved from the status topic’s publisher, e.g. /bt_navigator), so the fault associates with that node’s SOVD entity; it falls back to the action name only if no publisher is visible.

Per-action state, not per-goal

The fault is a property of the action, not of an individual goal. On every status message the whole GoalStatusArray is scanned for the net state:

  • if any goal is ABORTED (or CANCELED when canceled_is_fault), the action is failed (order of goals in the array does not matter);
  • otherwise, if the array has terminal goals and none are failing, the action is healthy.

A fault is raised only on the healthy -> failed transition and healed only on failed -> healthy. This means one failed goal cannot heal an action while another goal is still failed, and a dropped terminal message cannot leave a fault stuck. Per-goal dedup (keyed on goal_id:status) only suppresses duplicate log lines; it never gates the transition.

The bridge tracks the latest observed state (the desired state) separately from what the FaultManager was actually told. The transition is committed only once the report is delivered: if the FaultManager service is not discovered yet

  • e.g. an action’s status topic is transient_local (latched) and its terminal status reaches the bridge during the startup discovery window, before the report client connects - the transition stays pending and is retried, rather than the high-severity fault being silently dropped. (The latched status is delivered to a subscription only once, so without this retry the dropped report would never recover.) See Delivery latency and freeze-frame.

Scope: the terminal verdict, not the reason

This bridge delivers the generic “it aborted” event. The action-specific reason (e.g. MoveItErrorCode.val = -26 START_STATE_IN_COLLISION, Nav2 error_code on Iron/Jazzy) lives in the action result message and is a separate enrichment concern (a future action-result reader using runtime message introspection / dynmsg). Per-project plugins are only needed for human-readable labels, not to surface the fault.

Delivery latency and freeze-frame

The FaultManager captures the freeze-frame (topic snapshot, rosbag) when it receives the report, so the value of that context decays with delivery latency: a late fault is reported against state that has already moved on. The bridge therefore optimises for delivering a fault to the FaultManager as soon as physically possible.

  • Happy path (service discovered): the fault is reported synchronously in the status callback, the instant the terminal status arrives. This is the normal runtime case and is already ASAP - nothing is deferred.
  • Degraded path (service not yet discovered): a fault cannot physically be delivered before the FaultManager report_fault service is discovered (startup before the FaultManager is up, or a FaultManager restart). The transition is kept pending and retried on a dedicated fast retry timer (retry_period_sec, default 50 ms), decoupled from the slow discovery rescan. So the fault lands within one short tick of the service appearing - keeping the freeze-frame as contemporaneous as the discovery floor allows.

We deliberately do not block the (single-threaded) executor waiting for the service: blocking would stall detection of other actions’ faults during an outage to shave the last few milliseconds off one fault - a bad trade when the freeze-frame is already only ~tens of ms fresher. The retry timer is armed only while a delivery is pending, so an idle bridge does no periodic work.

Known boundaries (narrow, and shrunk further by the fast retry; both are consequences of the level-triggered, net-state model, not silent loss in the common case):

File truncated at 100 lines see the full file

CHANGELOG

Changelog for package ros2_medkit_action_status_bridge

0.6.0 (2026-06-22)

  • Initial release: generic action-status bridge. Watches every /<action>/_action/status topic and turns ABORTED goals into FaultManager faults (<PREFIX>_<ACTION>_ABORTED), heals on a non-failing terminal state. Captures the terminal action verdict that the diagnostic and log bridges cannot see (#423).
  • Fault state is per-ACTION, derived from the whole GoalStatusArray on each message: a fault is raised only on the healthy -> failed transition and healed only on failed -> healthy, so it is order-independent and resilient to dropped terminal messages. Per-goal dedup now only suppresses duplicate log lines.
  • canceled_is_fault emits a status-aware <PREFIX>_<ACTION>_CANCELED code; such a fault heals on the next non-failing terminal or when the canceled goal ages out of the retained status array.
  • Vanished actions (lifecycle deactivate, one-shot nodes) are pruned on rescan.
  • Parameters are range-checked/normalized at load; an incompatible action status QoS is now warned about instead of silently dropping faults.
  • Ships a default configuration so the bridge starts out of the box (#459)
  • Fault source is fixed at first report instead of being re-attributed later: faults raised before discovery settles register the server FQN, faults are never attributed to the discovery placeholder node name, and the subscriptions and timer are reset in the destructor (#466)
  • Contributors: \@bburda, \@mfaferek93

Launch files

No launch files found

Messages

No message files found.

Services

No service files found

Plugins

No plugins found.

Recent questions tagged ros2_medkit_action_status_bridge at Robotics Stack Exchange

No version for distro lunar showing humble. Known supported distros are highlighted in the buttons above.

Package Summary

Version 0.6.0
License Apache-2.0
Build type AMENT_CMAKE
Use RECOMMENDED

Repository Summary

Checkout URI https://github.com/selfpatch/ros2_medkit.git
VCS Type git
VCS Version main
Last Updated 2026-07-07
Dev Status DEVELOPED
Released RELEASED
Contributing Help Wanted (-)
Good First Issues (-)
Pull Requests to Review (-)

Package Description

Bridge node turning terminal ROS2 action goal states (aborted) into FaultManager faults

Maintainers

  • mfaferek93

Authors

No additional authors.

ros2_medkit_action_status_bridge

Generic drop-in bridge that turns terminal ROS 2 action goal states into structured medkit faults. It watches the /<action>/_action/status (action_msgs/msg/GoalStatusArray) topic that every action server publishes, so it works across Nav2, MoveIt 2, ros2_control’s controller actions, and any custom action with no per-project code.

Why this matters

For the core ROS 2 league the authoritative “the goal failed” verdict lives on the action-result channel, not on /diagnostics or /rosout:

  • Nav2 NavigateToPose -> GoalStatus = ABORTED
  • MoveIt MoveGroup -> GoalStatus = ABORTED (with MoveItErrorCode in the result)
  • ros2_control controller actions -> aborted goals

Neither the diagnostic bridge nor the log bridge sees this. This bridge does, generically, by observing the goal-status topic.

What it does

  • Discovers every action on the graph by scanning for */_action/status topics (re-scanned on a timer to catch actions that appear later, to drop actions that vanish, e.g. a Nav2 lifecycle deactivate or a one-shot node, and to retry any fault whose delivery to the FaultManager was deferred).
  • ABORTED (6) -> fault (SEVERITY_ERROR by default).
  • CANCELED (5) -> fault only if canceled_is_fault (off by default; cancel is usually intentional). When enabled it emits a _CANCELED code.
  • SUCCEEDED (4) -> PASSED to heal the action’s fault code (if enabled).
  • fault_code is <PREFIX>_<ACTION>_ABORTED (or _CANCELED), e.g. ACTION_NAVIGATE_TO_POSE_ABORTED. source_id is the action server’s node FQN (resolved from the status topic’s publisher, e.g. /bt_navigator), so the fault associates with that node’s SOVD entity; it falls back to the action name only if no publisher is visible.

Per-action state, not per-goal

The fault is a property of the action, not of an individual goal. On every status message the whole GoalStatusArray is scanned for the net state:

  • if any goal is ABORTED (or CANCELED when canceled_is_fault), the action is failed (order of goals in the array does not matter);
  • otherwise, if the array has terminal goals and none are failing, the action is healthy.

A fault is raised only on the healthy -> failed transition and healed only on failed -> healthy. This means one failed goal cannot heal an action while another goal is still failed, and a dropped terminal message cannot leave a fault stuck. Per-goal dedup (keyed on goal_id:status) only suppresses duplicate log lines; it never gates the transition.

The bridge tracks the latest observed state (the desired state) separately from what the FaultManager was actually told. The transition is committed only once the report is delivered: if the FaultManager service is not discovered yet

  • e.g. an action’s status topic is transient_local (latched) and its terminal status reaches the bridge during the startup discovery window, before the report client connects - the transition stays pending and is retried, rather than the high-severity fault being silently dropped. (The latched status is delivered to a subscription only once, so without this retry the dropped report would never recover.) See Delivery latency and freeze-frame.

Scope: the terminal verdict, not the reason

This bridge delivers the generic “it aborted” event. The action-specific reason (e.g. MoveItErrorCode.val = -26 START_STATE_IN_COLLISION, Nav2 error_code on Iron/Jazzy) lives in the action result message and is a separate enrichment concern (a future action-result reader using runtime message introspection / dynmsg). Per-project plugins are only needed for human-readable labels, not to surface the fault.

Delivery latency and freeze-frame

The FaultManager captures the freeze-frame (topic snapshot, rosbag) when it receives the report, so the value of that context decays with delivery latency: a late fault is reported against state that has already moved on. The bridge therefore optimises for delivering a fault to the FaultManager as soon as physically possible.

  • Happy path (service discovered): the fault is reported synchronously in the status callback, the instant the terminal status arrives. This is the normal runtime case and is already ASAP - nothing is deferred.
  • Degraded path (service not yet discovered): a fault cannot physically be delivered before the FaultManager report_fault service is discovered (startup before the FaultManager is up, or a FaultManager restart). The transition is kept pending and retried on a dedicated fast retry timer (retry_period_sec, default 50 ms), decoupled from the slow discovery rescan. So the fault lands within one short tick of the service appearing - keeping the freeze-frame as contemporaneous as the discovery floor allows.

We deliberately do not block the (single-threaded) executor waiting for the service: blocking would stall detection of other actions’ faults during an outage to shave the last few milliseconds off one fault - a bad trade when the freeze-frame is already only ~tens of ms fresher. The retry timer is armed only while a delivery is pending, so an idle bridge does no periodic work.

Known boundaries (narrow, and shrunk further by the fast retry; both are consequences of the level-triggered, net-state model, not silent loss in the common case):

File truncated at 100 lines see the full file

CHANGELOG

Changelog for package ros2_medkit_action_status_bridge

0.6.0 (2026-06-22)

  • Initial release: generic action-status bridge. Watches every /<action>/_action/status topic and turns ABORTED goals into FaultManager faults (<PREFIX>_<ACTION>_ABORTED), heals on a non-failing terminal state. Captures the terminal action verdict that the diagnostic and log bridges cannot see (#423).
  • Fault state is per-ACTION, derived from the whole GoalStatusArray on each message: a fault is raised only on the healthy -> failed transition and healed only on failed -> healthy, so it is order-independent and resilient to dropped terminal messages. Per-goal dedup now only suppresses duplicate log lines.
  • canceled_is_fault emits a status-aware <PREFIX>_<ACTION>_CANCELED code; such a fault heals on the next non-failing terminal or when the canceled goal ages out of the retained status array.
  • Vanished actions (lifecycle deactivate, one-shot nodes) are pruned on rescan.
  • Parameters are range-checked/normalized at load; an incompatible action status QoS is now warned about instead of silently dropping faults.
  • Ships a default configuration so the bridge starts out of the box (#459)
  • Fault source is fixed at first report instead of being re-attributed later: faults raised before discovery settles register the server FQN, faults are never attributed to the discovery placeholder node name, and the subscriptions and timer are reset in the destructor (#466)
  • Contributors: \@bburda, \@mfaferek93

Launch files

No launch files found

Messages

No message files found.

Services

No service files found

Plugins

No plugins found.

Recent questions tagged ros2_medkit_action_status_bridge at Robotics Stack Exchange

No version for distro jade showing humble. Known supported distros are highlighted in the buttons above.

Package Summary

Version 0.6.0
License Apache-2.0
Build type AMENT_CMAKE
Use RECOMMENDED

Repository Summary

Checkout URI https://github.com/selfpatch/ros2_medkit.git
VCS Type git
VCS Version main
Last Updated 2026-07-07
Dev Status DEVELOPED
Released RELEASED
Contributing Help Wanted (-)
Good First Issues (-)
Pull Requests to Review (-)

Package Description

Bridge node turning terminal ROS2 action goal states (aborted) into FaultManager faults

Maintainers

  • mfaferek93

Authors

No additional authors.

ros2_medkit_action_status_bridge

Generic drop-in bridge that turns terminal ROS 2 action goal states into structured medkit faults. It watches the /<action>/_action/status (action_msgs/msg/GoalStatusArray) topic that every action server publishes, so it works across Nav2, MoveIt 2, ros2_control’s controller actions, and any custom action with no per-project code.

Why this matters

For the core ROS 2 league the authoritative “the goal failed” verdict lives on the action-result channel, not on /diagnostics or /rosout:

  • Nav2 NavigateToPose -> GoalStatus = ABORTED
  • MoveIt MoveGroup -> GoalStatus = ABORTED (with MoveItErrorCode in the result)
  • ros2_control controller actions -> aborted goals

Neither the diagnostic bridge nor the log bridge sees this. This bridge does, generically, by observing the goal-status topic.

What it does

  • Discovers every action on the graph by scanning for */_action/status topics (re-scanned on a timer to catch actions that appear later, to drop actions that vanish, e.g. a Nav2 lifecycle deactivate or a one-shot node, and to retry any fault whose delivery to the FaultManager was deferred).
  • ABORTED (6) -> fault (SEVERITY_ERROR by default).
  • CANCELED (5) -> fault only if canceled_is_fault (off by default; cancel is usually intentional). When enabled it emits a _CANCELED code.
  • SUCCEEDED (4) -> PASSED to heal the action’s fault code (if enabled).
  • fault_code is <PREFIX>_<ACTION>_ABORTED (or _CANCELED), e.g. ACTION_NAVIGATE_TO_POSE_ABORTED. source_id is the action server’s node FQN (resolved from the status topic’s publisher, e.g. /bt_navigator), so the fault associates with that node’s SOVD entity; it falls back to the action name only if no publisher is visible.

Per-action state, not per-goal

The fault is a property of the action, not of an individual goal. On every status message the whole GoalStatusArray is scanned for the net state:

  • if any goal is ABORTED (or CANCELED when canceled_is_fault), the action is failed (order of goals in the array does not matter);
  • otherwise, if the array has terminal goals and none are failing, the action is healthy.

A fault is raised only on the healthy -> failed transition and healed only on failed -> healthy. This means one failed goal cannot heal an action while another goal is still failed, and a dropped terminal message cannot leave a fault stuck. Per-goal dedup (keyed on goal_id:status) only suppresses duplicate log lines; it never gates the transition.

The bridge tracks the latest observed state (the desired state) separately from what the FaultManager was actually told. The transition is committed only once the report is delivered: if the FaultManager service is not discovered yet

  • e.g. an action’s status topic is transient_local (latched) and its terminal status reaches the bridge during the startup discovery window, before the report client connects - the transition stays pending and is retried, rather than the high-severity fault being silently dropped. (The latched status is delivered to a subscription only once, so without this retry the dropped report would never recover.) See Delivery latency and freeze-frame.

Scope: the terminal verdict, not the reason

This bridge delivers the generic “it aborted” event. The action-specific reason (e.g. MoveItErrorCode.val = -26 START_STATE_IN_COLLISION, Nav2 error_code on Iron/Jazzy) lives in the action result message and is a separate enrichment concern (a future action-result reader using runtime message introspection / dynmsg). Per-project plugins are only needed for human-readable labels, not to surface the fault.

Delivery latency and freeze-frame

The FaultManager captures the freeze-frame (topic snapshot, rosbag) when it receives the report, so the value of that context decays with delivery latency: a late fault is reported against state that has already moved on. The bridge therefore optimises for delivering a fault to the FaultManager as soon as physically possible.

  • Happy path (service discovered): the fault is reported synchronously in the status callback, the instant the terminal status arrives. This is the normal runtime case and is already ASAP - nothing is deferred.
  • Degraded path (service not yet discovered): a fault cannot physically be delivered before the FaultManager report_fault service is discovered (startup before the FaultManager is up, or a FaultManager restart). The transition is kept pending and retried on a dedicated fast retry timer (retry_period_sec, default 50 ms), decoupled from the slow discovery rescan. So the fault lands within one short tick of the service appearing - keeping the freeze-frame as contemporaneous as the discovery floor allows.

We deliberately do not block the (single-threaded) executor waiting for the service: blocking would stall detection of other actions’ faults during an outage to shave the last few milliseconds off one fault - a bad trade when the freeze-frame is already only ~tens of ms fresher. The retry timer is armed only while a delivery is pending, so an idle bridge does no periodic work.

Known boundaries (narrow, and shrunk further by the fast retry; both are consequences of the level-triggered, net-state model, not silent loss in the common case):

File truncated at 100 lines see the full file

CHANGELOG

Changelog for package ros2_medkit_action_status_bridge

0.6.0 (2026-06-22)

  • Initial release: generic action-status bridge. Watches every /<action>/_action/status topic and turns ABORTED goals into FaultManager faults (<PREFIX>_<ACTION>_ABORTED), heals on a non-failing terminal state. Captures the terminal action verdict that the diagnostic and log bridges cannot see (#423).
  • Fault state is per-ACTION, derived from the whole GoalStatusArray on each message: a fault is raised only on the healthy -> failed transition and healed only on failed -> healthy, so it is order-independent and resilient to dropped terminal messages. Per-goal dedup now only suppresses duplicate log lines.
  • canceled_is_fault emits a status-aware <PREFIX>_<ACTION>_CANCELED code; such a fault heals on the next non-failing terminal or when the canceled goal ages out of the retained status array.
  • Vanished actions (lifecycle deactivate, one-shot nodes) are pruned on rescan.
  • Parameters are range-checked/normalized at load; an incompatible action status QoS is now warned about instead of silently dropping faults.
  • Ships a default configuration so the bridge starts out of the box (#459)
  • Fault source is fixed at first report instead of being re-attributed later: faults raised before discovery settles register the server FQN, faults are never attributed to the discovery placeholder node name, and the subscriptions and timer are reset in the destructor (#466)
  • Contributors: \@bburda, \@mfaferek93

Launch files

No launch files found

Messages

No message files found.

Services

No service files found

Plugins

No plugins found.

Recent questions tagged ros2_medkit_action_status_bridge at Robotics Stack Exchange

No version for distro indigo showing humble. Known supported distros are highlighted in the buttons above.

Package Summary

Version 0.6.0
License Apache-2.0
Build type AMENT_CMAKE
Use RECOMMENDED

Repository Summary

Checkout URI https://github.com/selfpatch/ros2_medkit.git
VCS Type git
VCS Version main
Last Updated 2026-07-07
Dev Status DEVELOPED
Released RELEASED
Contributing Help Wanted (-)
Good First Issues (-)
Pull Requests to Review (-)

Package Description

Bridge node turning terminal ROS2 action goal states (aborted) into FaultManager faults

Maintainers

  • mfaferek93

Authors

No additional authors.

ros2_medkit_action_status_bridge

Generic drop-in bridge that turns terminal ROS 2 action goal states into structured medkit faults. It watches the /<action>/_action/status (action_msgs/msg/GoalStatusArray) topic that every action server publishes, so it works across Nav2, MoveIt 2, ros2_control’s controller actions, and any custom action with no per-project code.

Why this matters

For the core ROS 2 league the authoritative “the goal failed” verdict lives on the action-result channel, not on /diagnostics or /rosout:

  • Nav2 NavigateToPose -> GoalStatus = ABORTED
  • MoveIt MoveGroup -> GoalStatus = ABORTED (with MoveItErrorCode in the result)
  • ros2_control controller actions -> aborted goals

Neither the diagnostic bridge nor the log bridge sees this. This bridge does, generically, by observing the goal-status topic.

What it does

  • Discovers every action on the graph by scanning for */_action/status topics (re-scanned on a timer to catch actions that appear later, to drop actions that vanish, e.g. a Nav2 lifecycle deactivate or a one-shot node, and to retry any fault whose delivery to the FaultManager was deferred).
  • ABORTED (6) -> fault (SEVERITY_ERROR by default).
  • CANCELED (5) -> fault only if canceled_is_fault (off by default; cancel is usually intentional). When enabled it emits a _CANCELED code.
  • SUCCEEDED (4) -> PASSED to heal the action’s fault code (if enabled).
  • fault_code is <PREFIX>_<ACTION>_ABORTED (or _CANCELED), e.g. ACTION_NAVIGATE_TO_POSE_ABORTED. source_id is the action server’s node FQN (resolved from the status topic’s publisher, e.g. /bt_navigator), so the fault associates with that node’s SOVD entity; it falls back to the action name only if no publisher is visible.

Per-action state, not per-goal

The fault is a property of the action, not of an individual goal. On every status message the whole GoalStatusArray is scanned for the net state:

  • if any goal is ABORTED (or CANCELED when canceled_is_fault), the action is failed (order of goals in the array does not matter);
  • otherwise, if the array has terminal goals and none are failing, the action is healthy.

A fault is raised only on the healthy -> failed transition and healed only on failed -> healthy. This means one failed goal cannot heal an action while another goal is still failed, and a dropped terminal message cannot leave a fault stuck. Per-goal dedup (keyed on goal_id:status) only suppresses duplicate log lines; it never gates the transition.

The bridge tracks the latest observed state (the desired state) separately from what the FaultManager was actually told. The transition is committed only once the report is delivered: if the FaultManager service is not discovered yet

  • e.g. an action’s status topic is transient_local (latched) and its terminal status reaches the bridge during the startup discovery window, before the report client connects - the transition stays pending and is retried, rather than the high-severity fault being silently dropped. (The latched status is delivered to a subscription only once, so without this retry the dropped report would never recover.) See Delivery latency and freeze-frame.

Scope: the terminal verdict, not the reason

This bridge delivers the generic “it aborted” event. The action-specific reason (e.g. MoveItErrorCode.val = -26 START_STATE_IN_COLLISION, Nav2 error_code on Iron/Jazzy) lives in the action result message and is a separate enrichment concern (a future action-result reader using runtime message introspection / dynmsg). Per-project plugins are only needed for human-readable labels, not to surface the fault.

Delivery latency and freeze-frame

The FaultManager captures the freeze-frame (topic snapshot, rosbag) when it receives the report, so the value of that context decays with delivery latency: a late fault is reported against state that has already moved on. The bridge therefore optimises for delivering a fault to the FaultManager as soon as physically possible.

  • Happy path (service discovered): the fault is reported synchronously in the status callback, the instant the terminal status arrives. This is the normal runtime case and is already ASAP - nothing is deferred.
  • Degraded path (service not yet discovered): a fault cannot physically be delivered before the FaultManager report_fault service is discovered (startup before the FaultManager is up, or a FaultManager restart). The transition is kept pending and retried on a dedicated fast retry timer (retry_period_sec, default 50 ms), decoupled from the slow discovery rescan. So the fault lands within one short tick of the service appearing - keeping the freeze-frame as contemporaneous as the discovery floor allows.

We deliberately do not block the (single-threaded) executor waiting for the service: blocking would stall detection of other actions’ faults during an outage to shave the last few milliseconds off one fault - a bad trade when the freeze-frame is already only ~tens of ms fresher. The retry timer is armed only while a delivery is pending, so an idle bridge does no periodic work.

Known boundaries (narrow, and shrunk further by the fast retry; both are consequences of the level-triggered, net-state model, not silent loss in the common case):

File truncated at 100 lines see the full file

CHANGELOG

Changelog for package ros2_medkit_action_status_bridge

0.6.0 (2026-06-22)

  • Initial release: generic action-status bridge. Watches every /<action>/_action/status topic and turns ABORTED goals into FaultManager faults (<PREFIX>_<ACTION>_ABORTED), heals on a non-failing terminal state. Captures the terminal action verdict that the diagnostic and log bridges cannot see (#423).
  • Fault state is per-ACTION, derived from the whole GoalStatusArray on each message: a fault is raised only on the healthy -> failed transition and healed only on failed -> healthy, so it is order-independent and resilient to dropped terminal messages. Per-goal dedup now only suppresses duplicate log lines.
  • canceled_is_fault emits a status-aware <PREFIX>_<ACTION>_CANCELED code; such a fault heals on the next non-failing terminal or when the canceled goal ages out of the retained status array.
  • Vanished actions (lifecycle deactivate, one-shot nodes) are pruned on rescan.
  • Parameters are range-checked/normalized at load; an incompatible action status QoS is now warned about instead of silently dropping faults.
  • Ships a default configuration so the bridge starts out of the box (#459)
  • Fault source is fixed at first report instead of being re-attributed later: faults raised before discovery settles register the server FQN, faults are never attributed to the discovery placeholder node name, and the subscriptions and timer are reset in the destructor (#466)
  • Contributors: \@bburda, \@mfaferek93

Launch files

No launch files found

Messages

No message files found.

Services

No service files found

Plugins

No plugins found.

Recent questions tagged ros2_medkit_action_status_bridge at Robotics Stack Exchange

No version for distro hydro showing humble. Known supported distros are highlighted in the buttons above.

Package Summary

Version 0.6.0
License Apache-2.0
Build type AMENT_CMAKE
Use RECOMMENDED

Repository Summary

Checkout URI https://github.com/selfpatch/ros2_medkit.git
VCS Type git
VCS Version main
Last Updated 2026-07-07
Dev Status DEVELOPED
Released RELEASED
Contributing Help Wanted (-)
Good First Issues (-)
Pull Requests to Review (-)

Package Description

Bridge node turning terminal ROS2 action goal states (aborted) into FaultManager faults

Maintainers

  • mfaferek93

Authors

No additional authors.

ros2_medkit_action_status_bridge

Generic drop-in bridge that turns terminal ROS 2 action goal states into structured medkit faults. It watches the /<action>/_action/status (action_msgs/msg/GoalStatusArray) topic that every action server publishes, so it works across Nav2, MoveIt 2, ros2_control’s controller actions, and any custom action with no per-project code.

Why this matters

For the core ROS 2 league the authoritative “the goal failed” verdict lives on the action-result channel, not on /diagnostics or /rosout:

  • Nav2 NavigateToPose -> GoalStatus = ABORTED
  • MoveIt MoveGroup -> GoalStatus = ABORTED (with MoveItErrorCode in the result)
  • ros2_control controller actions -> aborted goals

Neither the diagnostic bridge nor the log bridge sees this. This bridge does, generically, by observing the goal-status topic.

What it does

  • Discovers every action on the graph by scanning for */_action/status topics (re-scanned on a timer to catch actions that appear later, to drop actions that vanish, e.g. a Nav2 lifecycle deactivate or a one-shot node, and to retry any fault whose delivery to the FaultManager was deferred).
  • ABORTED (6) -> fault (SEVERITY_ERROR by default).
  • CANCELED (5) -> fault only if canceled_is_fault (off by default; cancel is usually intentional). When enabled it emits a _CANCELED code.
  • SUCCEEDED (4) -> PASSED to heal the action’s fault code (if enabled).
  • fault_code is <PREFIX>_<ACTION>_ABORTED (or _CANCELED), e.g. ACTION_NAVIGATE_TO_POSE_ABORTED. source_id is the action server’s node FQN (resolved from the status topic’s publisher, e.g. /bt_navigator), so the fault associates with that node’s SOVD entity; it falls back to the action name only if no publisher is visible.

Per-action state, not per-goal

The fault is a property of the action, not of an individual goal. On every status message the whole GoalStatusArray is scanned for the net state:

  • if any goal is ABORTED (or CANCELED when canceled_is_fault), the action is failed (order of goals in the array does not matter);
  • otherwise, if the array has terminal goals and none are failing, the action is healthy.

A fault is raised only on the healthy -> failed transition and healed only on failed -> healthy. This means one failed goal cannot heal an action while another goal is still failed, and a dropped terminal message cannot leave a fault stuck. Per-goal dedup (keyed on goal_id:status) only suppresses duplicate log lines; it never gates the transition.

The bridge tracks the latest observed state (the desired state) separately from what the FaultManager was actually told. The transition is committed only once the report is delivered: if the FaultManager service is not discovered yet

  • e.g. an action’s status topic is transient_local (latched) and its terminal status reaches the bridge during the startup discovery window, before the report client connects - the transition stays pending and is retried, rather than the high-severity fault being silently dropped. (The latched status is delivered to a subscription only once, so without this retry the dropped report would never recover.) See Delivery latency and freeze-frame.

Scope: the terminal verdict, not the reason

This bridge delivers the generic “it aborted” event. The action-specific reason (e.g. MoveItErrorCode.val = -26 START_STATE_IN_COLLISION, Nav2 error_code on Iron/Jazzy) lives in the action result message and is a separate enrichment concern (a future action-result reader using runtime message introspection / dynmsg). Per-project plugins are only needed for human-readable labels, not to surface the fault.

Delivery latency and freeze-frame

The FaultManager captures the freeze-frame (topic snapshot, rosbag) when it receives the report, so the value of that context decays with delivery latency: a late fault is reported against state that has already moved on. The bridge therefore optimises for delivering a fault to the FaultManager as soon as physically possible.

  • Happy path (service discovered): the fault is reported synchronously in the status callback, the instant the terminal status arrives. This is the normal runtime case and is already ASAP - nothing is deferred.
  • Degraded path (service not yet discovered): a fault cannot physically be delivered before the FaultManager report_fault service is discovered (startup before the FaultManager is up, or a FaultManager restart). The transition is kept pending and retried on a dedicated fast retry timer (retry_period_sec, default 50 ms), decoupled from the slow discovery rescan. So the fault lands within one short tick of the service appearing - keeping the freeze-frame as contemporaneous as the discovery floor allows.

We deliberately do not block the (single-threaded) executor waiting for the service: blocking would stall detection of other actions’ faults during an outage to shave the last few milliseconds off one fault - a bad trade when the freeze-frame is already only ~tens of ms fresher. The retry timer is armed only while a delivery is pending, so an idle bridge does no periodic work.

Known boundaries (narrow, and shrunk further by the fast retry; both are consequences of the level-triggered, net-state model, not silent loss in the common case):

File truncated at 100 lines see the full file

CHANGELOG

Changelog for package ros2_medkit_action_status_bridge

0.6.0 (2026-06-22)

  • Initial release: generic action-status bridge. Watches every /<action>/_action/status topic and turns ABORTED goals into FaultManager faults (<PREFIX>_<ACTION>_ABORTED), heals on a non-failing terminal state. Captures the terminal action verdict that the diagnostic and log bridges cannot see (#423).
  • Fault state is per-ACTION, derived from the whole GoalStatusArray on each message: a fault is raised only on the healthy -> failed transition and healed only on failed -> healthy, so it is order-independent and resilient to dropped terminal messages. Per-goal dedup now only suppresses duplicate log lines.
  • canceled_is_fault emits a status-aware <PREFIX>_<ACTION>_CANCELED code; such a fault heals on the next non-failing terminal or when the canceled goal ages out of the retained status array.
  • Vanished actions (lifecycle deactivate, one-shot nodes) are pruned on rescan.
  • Parameters are range-checked/normalized at load; an incompatible action status QoS is now warned about instead of silently dropping faults.
  • Ships a default configuration so the bridge starts out of the box (#459)
  • Fault source is fixed at first report instead of being re-attributed later: faults raised before discovery settles register the server FQN, faults are never attributed to the discovery placeholder node name, and the subscriptions and timer are reset in the destructor (#466)
  • Contributors: \@bburda, \@mfaferek93

Launch files

No launch files found

Messages

No message files found.

Services

No service files found

Plugins

No plugins found.

Recent questions tagged ros2_medkit_action_status_bridge at Robotics Stack Exchange

No version for distro kinetic showing humble. Known supported distros are highlighted in the buttons above.

Package Summary

Version 0.6.0
License Apache-2.0
Build type AMENT_CMAKE
Use RECOMMENDED

Repository Summary

Checkout URI https://github.com/selfpatch/ros2_medkit.git
VCS Type git
VCS Version main
Last Updated 2026-07-07
Dev Status DEVELOPED
Released RELEASED
Contributing Help Wanted (-)
Good First Issues (-)
Pull Requests to Review (-)

Package Description

Bridge node turning terminal ROS2 action goal states (aborted) into FaultManager faults

Maintainers

  • mfaferek93

Authors

No additional authors.

ros2_medkit_action_status_bridge

Generic drop-in bridge that turns terminal ROS 2 action goal states into structured medkit faults. It watches the /<action>/_action/status (action_msgs/msg/GoalStatusArray) topic that every action server publishes, so it works across Nav2, MoveIt 2, ros2_control’s controller actions, and any custom action with no per-project code.

Why this matters

For the core ROS 2 league the authoritative “the goal failed” verdict lives on the action-result channel, not on /diagnostics or /rosout:

  • Nav2 NavigateToPose -> GoalStatus = ABORTED
  • MoveIt MoveGroup -> GoalStatus = ABORTED (with MoveItErrorCode in the result)
  • ros2_control controller actions -> aborted goals

Neither the diagnostic bridge nor the log bridge sees this. This bridge does, generically, by observing the goal-status topic.

What it does

  • Discovers every action on the graph by scanning for */_action/status topics (re-scanned on a timer to catch actions that appear later, to drop actions that vanish, e.g. a Nav2 lifecycle deactivate or a one-shot node, and to retry any fault whose delivery to the FaultManager was deferred).
  • ABORTED (6) -> fault (SEVERITY_ERROR by default).
  • CANCELED (5) -> fault only if canceled_is_fault (off by default; cancel is usually intentional). When enabled it emits a _CANCELED code.
  • SUCCEEDED (4) -> PASSED to heal the action’s fault code (if enabled).
  • fault_code is <PREFIX>_<ACTION>_ABORTED (or _CANCELED), e.g. ACTION_NAVIGATE_TO_POSE_ABORTED. source_id is the action server’s node FQN (resolved from the status topic’s publisher, e.g. /bt_navigator), so the fault associates with that node’s SOVD entity; it falls back to the action name only if no publisher is visible.

Per-action state, not per-goal

The fault is a property of the action, not of an individual goal. On every status message the whole GoalStatusArray is scanned for the net state:

  • if any goal is ABORTED (or CANCELED when canceled_is_fault), the action is failed (order of goals in the array does not matter);
  • otherwise, if the array has terminal goals and none are failing, the action is healthy.

A fault is raised only on the healthy -> failed transition and healed only on failed -> healthy. This means one failed goal cannot heal an action while another goal is still failed, and a dropped terminal message cannot leave a fault stuck. Per-goal dedup (keyed on goal_id:status) only suppresses duplicate log lines; it never gates the transition.

The bridge tracks the latest observed state (the desired state) separately from what the FaultManager was actually told. The transition is committed only once the report is delivered: if the FaultManager service is not discovered yet

  • e.g. an action’s status topic is transient_local (latched) and its terminal status reaches the bridge during the startup discovery window, before the report client connects - the transition stays pending and is retried, rather than the high-severity fault being silently dropped. (The latched status is delivered to a subscription only once, so without this retry the dropped report would never recover.) See Delivery latency and freeze-frame.

Scope: the terminal verdict, not the reason

This bridge delivers the generic “it aborted” event. The action-specific reason (e.g. MoveItErrorCode.val = -26 START_STATE_IN_COLLISION, Nav2 error_code on Iron/Jazzy) lives in the action result message and is a separate enrichment concern (a future action-result reader using runtime message introspection / dynmsg). Per-project plugins are only needed for human-readable labels, not to surface the fault.

Delivery latency and freeze-frame

The FaultManager captures the freeze-frame (topic snapshot, rosbag) when it receives the report, so the value of that context decays with delivery latency: a late fault is reported against state that has already moved on. The bridge therefore optimises for delivering a fault to the FaultManager as soon as physically possible.

  • Happy path (service discovered): the fault is reported synchronously in the status callback, the instant the terminal status arrives. This is the normal runtime case and is already ASAP - nothing is deferred.
  • Degraded path (service not yet discovered): a fault cannot physically be delivered before the FaultManager report_fault service is discovered (startup before the FaultManager is up, or a FaultManager restart). The transition is kept pending and retried on a dedicated fast retry timer (retry_period_sec, default 50 ms), decoupled from the slow discovery rescan. So the fault lands within one short tick of the service appearing - keeping the freeze-frame as contemporaneous as the discovery floor allows.

We deliberately do not block the (single-threaded) executor waiting for the service: blocking would stall detection of other actions’ faults during an outage to shave the last few milliseconds off one fault - a bad trade when the freeze-frame is already only ~tens of ms fresher. The retry timer is armed only while a delivery is pending, so an idle bridge does no periodic work.

Known boundaries (narrow, and shrunk further by the fast retry; both are consequences of the level-triggered, net-state model, not silent loss in the common case):

File truncated at 100 lines see the full file

CHANGELOG

Changelog for package ros2_medkit_action_status_bridge

0.6.0 (2026-06-22)

  • Initial release: generic action-status bridge. Watches every /<action>/_action/status topic and turns ABORTED goals into FaultManager faults (<PREFIX>_<ACTION>_ABORTED), heals on a non-failing terminal state. Captures the terminal action verdict that the diagnostic and log bridges cannot see (#423).
  • Fault state is per-ACTION, derived from the whole GoalStatusArray on each message: a fault is raised only on the healthy -> failed transition and healed only on failed -> healthy, so it is order-independent and resilient to dropped terminal messages. Per-goal dedup now only suppresses duplicate log lines.
  • canceled_is_fault emits a status-aware <PREFIX>_<ACTION>_CANCELED code; such a fault heals on the next non-failing terminal or when the canceled goal ages out of the retained status array.
  • Vanished actions (lifecycle deactivate, one-shot nodes) are pruned on rescan.
  • Parameters are range-checked/normalized at load; an incompatible action status QoS is now warned about instead of silently dropping faults.
  • Ships a default configuration so the bridge starts out of the box (#459)
  • Fault source is fixed at first report instead of being re-attributed later: faults raised before discovery settles register the server FQN, faults are never attributed to the discovery placeholder node name, and the subscriptions and timer are reset in the destructor (#466)
  • Contributors: \@bburda, \@mfaferek93

Launch files

No launch files found

Messages

No message files found.

Services

No service files found

Plugins

No plugins found.

Recent questions tagged ros2_medkit_action_status_bridge at Robotics Stack Exchange

No version for distro melodic showing humble. Known supported distros are highlighted in the buttons above.

Package Summary

Version 0.6.0
License Apache-2.0
Build type AMENT_CMAKE
Use RECOMMENDED

Repository Summary

Checkout URI https://github.com/selfpatch/ros2_medkit.git
VCS Type git
VCS Version main
Last Updated 2026-07-07
Dev Status DEVELOPED
Released RELEASED
Contributing Help Wanted (-)
Good First Issues (-)
Pull Requests to Review (-)

Package Description

Bridge node turning terminal ROS2 action goal states (aborted) into FaultManager faults

Maintainers

  • mfaferek93

Authors

No additional authors.

ros2_medkit_action_status_bridge

Generic drop-in bridge that turns terminal ROS 2 action goal states into structured medkit faults. It watches the /<action>/_action/status (action_msgs/msg/GoalStatusArray) topic that every action server publishes, so it works across Nav2, MoveIt 2, ros2_control’s controller actions, and any custom action with no per-project code.

Why this matters

For the core ROS 2 league the authoritative “the goal failed” verdict lives on the action-result channel, not on /diagnostics or /rosout:

  • Nav2 NavigateToPose -> GoalStatus = ABORTED
  • MoveIt MoveGroup -> GoalStatus = ABORTED (with MoveItErrorCode in the result)
  • ros2_control controller actions -> aborted goals

Neither the diagnostic bridge nor the log bridge sees this. This bridge does, generically, by observing the goal-status topic.

What it does

  • Discovers every action on the graph by scanning for */_action/status topics (re-scanned on a timer to catch actions that appear later, to drop actions that vanish, e.g. a Nav2 lifecycle deactivate or a one-shot node, and to retry any fault whose delivery to the FaultManager was deferred).
  • ABORTED (6) -> fault (SEVERITY_ERROR by default).
  • CANCELED (5) -> fault only if canceled_is_fault (off by default; cancel is usually intentional). When enabled it emits a _CANCELED code.
  • SUCCEEDED (4) -> PASSED to heal the action’s fault code (if enabled).
  • fault_code is <PREFIX>_<ACTION>_ABORTED (or _CANCELED), e.g. ACTION_NAVIGATE_TO_POSE_ABORTED. source_id is the action server’s node FQN (resolved from the status topic’s publisher, e.g. /bt_navigator), so the fault associates with that node’s SOVD entity; it falls back to the action name only if no publisher is visible.

Per-action state, not per-goal

The fault is a property of the action, not of an individual goal. On every status message the whole GoalStatusArray is scanned for the net state:

  • if any goal is ABORTED (or CANCELED when canceled_is_fault), the action is failed (order of goals in the array does not matter);
  • otherwise, if the array has terminal goals and none are failing, the action is healthy.

A fault is raised only on the healthy -> failed transition and healed only on failed -> healthy. This means one failed goal cannot heal an action while another goal is still failed, and a dropped terminal message cannot leave a fault stuck. Per-goal dedup (keyed on goal_id:status) only suppresses duplicate log lines; it never gates the transition.

The bridge tracks the latest observed state (the desired state) separately from what the FaultManager was actually told. The transition is committed only once the report is delivered: if the FaultManager service is not discovered yet

  • e.g. an action’s status topic is transient_local (latched) and its terminal status reaches the bridge during the startup discovery window, before the report client connects - the transition stays pending and is retried, rather than the high-severity fault being silently dropped. (The latched status is delivered to a subscription only once, so without this retry the dropped report would never recover.) See Delivery latency and freeze-frame.

Scope: the terminal verdict, not the reason

This bridge delivers the generic “it aborted” event. The action-specific reason (e.g. MoveItErrorCode.val = -26 START_STATE_IN_COLLISION, Nav2 error_code on Iron/Jazzy) lives in the action result message and is a separate enrichment concern (a future action-result reader using runtime message introspection / dynmsg). Per-project plugins are only needed for human-readable labels, not to surface the fault.

Delivery latency and freeze-frame

The FaultManager captures the freeze-frame (topic snapshot, rosbag) when it receives the report, so the value of that context decays with delivery latency: a late fault is reported against state that has already moved on. The bridge therefore optimises for delivering a fault to the FaultManager as soon as physically possible.

  • Happy path (service discovered): the fault is reported synchronously in the status callback, the instant the terminal status arrives. This is the normal runtime case and is already ASAP - nothing is deferred.
  • Degraded path (service not yet discovered): a fault cannot physically be delivered before the FaultManager report_fault service is discovered (startup before the FaultManager is up, or a FaultManager restart). The transition is kept pending and retried on a dedicated fast retry timer (retry_period_sec, default 50 ms), decoupled from the slow discovery rescan. So the fault lands within one short tick of the service appearing - keeping the freeze-frame as contemporaneous as the discovery floor allows.

We deliberately do not block the (single-threaded) executor waiting for the service: blocking would stall detection of other actions’ faults during an outage to shave the last few milliseconds off one fault - a bad trade when the freeze-frame is already only ~tens of ms fresher. The retry timer is armed only while a delivery is pending, so an idle bridge does no periodic work.

Known boundaries (narrow, and shrunk further by the fast retry; both are consequences of the level-triggered, net-state model, not silent loss in the common case):

File truncated at 100 lines see the full file

CHANGELOG

Changelog for package ros2_medkit_action_status_bridge

0.6.0 (2026-06-22)

  • Initial release: generic action-status bridge. Watches every /<action>/_action/status topic and turns ABORTED goals into FaultManager faults (<PREFIX>_<ACTION>_ABORTED), heals on a non-failing terminal state. Captures the terminal action verdict that the diagnostic and log bridges cannot see (#423).
  • Fault state is per-ACTION, derived from the whole GoalStatusArray on each message: a fault is raised only on the healthy -> failed transition and healed only on failed -> healthy, so it is order-independent and resilient to dropped terminal messages. Per-goal dedup now only suppresses duplicate log lines.
  • canceled_is_fault emits a status-aware <PREFIX>_<ACTION>_CANCELED code; such a fault heals on the next non-failing terminal or when the canceled goal ages out of the retained status array.
  • Vanished actions (lifecycle deactivate, one-shot nodes) are pruned on rescan.
  • Parameters are range-checked/normalized at load; an incompatible action status QoS is now warned about instead of silently dropping faults.
  • Ships a default configuration so the bridge starts out of the box (#459)
  • Fault source is fixed at first report instead of being re-attributed later: faults raised before discovery settles register the server FQN, faults are never attributed to the discovery placeholder node name, and the subscriptions and timer are reset in the destructor (#466)
  • Contributors: \@bburda, \@mfaferek93

Launch files

No launch files found

Messages

No message files found.

Services

No service files found

Plugins

No plugins found.

Recent questions tagged ros2_medkit_action_status_bridge at Robotics Stack Exchange

No version for distro noetic showing humble. Known supported distros are highlighted in the buttons above.

Package Summary

Version 0.6.0
License Apache-2.0
Build type AMENT_CMAKE
Use RECOMMENDED

Repository Summary

Checkout URI https://github.com/selfpatch/ros2_medkit.git
VCS Type git
VCS Version main
Last Updated 2026-07-07
Dev Status DEVELOPED
Released RELEASED
Contributing Help Wanted (-)
Good First Issues (-)
Pull Requests to Review (-)

Package Description

Bridge node turning terminal ROS2 action goal states (aborted) into FaultManager faults

Maintainers

  • mfaferek93

Authors

No additional authors.

ros2_medkit_action_status_bridge

Generic drop-in bridge that turns terminal ROS 2 action goal states into structured medkit faults. It watches the /<action>/_action/status (action_msgs/msg/GoalStatusArray) topic that every action server publishes, so it works across Nav2, MoveIt 2, ros2_control’s controller actions, and any custom action with no per-project code.

Why this matters

For the core ROS 2 league the authoritative “the goal failed” verdict lives on the action-result channel, not on /diagnostics or /rosout:

  • Nav2 NavigateToPose -> GoalStatus = ABORTED
  • MoveIt MoveGroup -> GoalStatus = ABORTED (with MoveItErrorCode in the result)
  • ros2_control controller actions -> aborted goals

Neither the diagnostic bridge nor the log bridge sees this. This bridge does, generically, by observing the goal-status topic.

What it does

  • Discovers every action on the graph by scanning for */_action/status topics (re-scanned on a timer to catch actions that appear later, to drop actions that vanish, e.g. a Nav2 lifecycle deactivate or a one-shot node, and to retry any fault whose delivery to the FaultManager was deferred).
  • ABORTED (6) -> fault (SEVERITY_ERROR by default).
  • CANCELED (5) -> fault only if canceled_is_fault (off by default; cancel is usually intentional). When enabled it emits a _CANCELED code.
  • SUCCEEDED (4) -> PASSED to heal the action’s fault code (if enabled).
  • fault_code is <PREFIX>_<ACTION>_ABORTED (or _CANCELED), e.g. ACTION_NAVIGATE_TO_POSE_ABORTED. source_id is the action server’s node FQN (resolved from the status topic’s publisher, e.g. /bt_navigator), so the fault associates with that node’s SOVD entity; it falls back to the action name only if no publisher is visible.

Per-action state, not per-goal

The fault is a property of the action, not of an individual goal. On every status message the whole GoalStatusArray is scanned for the net state:

  • if any goal is ABORTED (or CANCELED when canceled_is_fault), the action is failed (order of goals in the array does not matter);
  • otherwise, if the array has terminal goals and none are failing, the action is healthy.

A fault is raised only on the healthy -> failed transition and healed only on failed -> healthy. This means one failed goal cannot heal an action while another goal is still failed, and a dropped terminal message cannot leave a fault stuck. Per-goal dedup (keyed on goal_id:status) only suppresses duplicate log lines; it never gates the transition.

The bridge tracks the latest observed state (the desired state) separately from what the FaultManager was actually told. The transition is committed only once the report is delivered: if the FaultManager service is not discovered yet

  • e.g. an action’s status topic is transient_local (latched) and its terminal status reaches the bridge during the startup discovery window, before the report client connects - the transition stays pending and is retried, rather than the high-severity fault being silently dropped. (The latched status is delivered to a subscription only once, so without this retry the dropped report would never recover.) See Delivery latency and freeze-frame.

Scope: the terminal verdict, not the reason

This bridge delivers the generic “it aborted” event. The action-specific reason (e.g. MoveItErrorCode.val = -26 START_STATE_IN_COLLISION, Nav2 error_code on Iron/Jazzy) lives in the action result message and is a separate enrichment concern (a future action-result reader using runtime message introspection / dynmsg). Per-project plugins are only needed for human-readable labels, not to surface the fault.

Delivery latency and freeze-frame

The FaultManager captures the freeze-frame (topic snapshot, rosbag) when it receives the report, so the value of that context decays with delivery latency: a late fault is reported against state that has already moved on. The bridge therefore optimises for delivering a fault to the FaultManager as soon as physically possible.

  • Happy path (service discovered): the fault is reported synchronously in the status callback, the instant the terminal status arrives. This is the normal runtime case and is already ASAP - nothing is deferred.
  • Degraded path (service not yet discovered): a fault cannot physically be delivered before the FaultManager report_fault service is discovered (startup before the FaultManager is up, or a FaultManager restart). The transition is kept pending and retried on a dedicated fast retry timer (retry_period_sec, default 50 ms), decoupled from the slow discovery rescan. So the fault lands within one short tick of the service appearing - keeping the freeze-frame as contemporaneous as the discovery floor allows.

We deliberately do not block the (single-threaded) executor waiting for the service: blocking would stall detection of other actions’ faults during an outage to shave the last few milliseconds off one fault - a bad trade when the freeze-frame is already only ~tens of ms fresher. The retry timer is armed only while a delivery is pending, so an idle bridge does no periodic work.

Known boundaries (narrow, and shrunk further by the fast retry; both are consequences of the level-triggered, net-state model, not silent loss in the common case):

File truncated at 100 lines see the full file

CHANGELOG

Changelog for package ros2_medkit_action_status_bridge

0.6.0 (2026-06-22)

  • Initial release: generic action-status bridge. Watches every /<action>/_action/status topic and turns ABORTED goals into FaultManager faults (<PREFIX>_<ACTION>_ABORTED), heals on a non-failing terminal state. Captures the terminal action verdict that the diagnostic and log bridges cannot see (#423).
  • Fault state is per-ACTION, derived from the whole GoalStatusArray on each message: a fault is raised only on the healthy -> failed transition and healed only on failed -> healthy, so it is order-independent and resilient to dropped terminal messages. Per-goal dedup now only suppresses duplicate log lines.
  • canceled_is_fault emits a status-aware <PREFIX>_<ACTION>_CANCELED code; such a fault heals on the next non-failing terminal or when the canceled goal ages out of the retained status array.
  • Vanished actions (lifecycle deactivate, one-shot nodes) are pruned on rescan.
  • Parameters are range-checked/normalized at load; an incompatible action status QoS is now warned about instead of silently dropping faults.
  • Ships a default configuration so the bridge starts out of the box (#459)
  • Fault source is fixed at first report instead of being re-attributed later: faults raised before discovery settles register the server FQN, faults are never attributed to the discovery placeholder node name, and the subscriptions and timer are reset in the destructor (#466)
  • Contributors: \@bburda, \@mfaferek93

Launch files

No launch files found

Messages

No message files found.

Services

No service files found

Plugins

No plugins found.

Recent questions tagged ros2_medkit_action_status_bridge at Robotics Stack Exchange