-
Notifications
You must be signed in to change notification settings - Fork 84
Support feedback subscription content filter for action client #1457
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: develop
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -51,6 +51,12 @@ class ActionClient extends Entity { | |||||||||||||||||||||
| * @param {QoS} options.qos.feedbackSubQosProfile - Quality of service option for the feedback subscription, | ||||||||||||||||||||||
| * default: new QoS(QoS.HistoryPolicy.RMW_QOS_POLICY_HISTORY_SYSTEM_DEFAULT, 10). | ||||||||||||||||||||||
| * @param {QoS} options.qos.statusSubQosProfile - Quality of service option for the status subscription, default: QoS.profileActionStatusDefault. | ||||||||||||||||||||||
| * @param {boolean} options.enableFeedbackMsgOptimization - Enable feedback subscription content filter to | ||||||||||||||||||||||
| * optimize the handling of feedback messages. When enabled, the content filter is used to configure | ||||||||||||||||||||||
| * the goal ID for the subscription, which helps avoid the reception of irrelevant feedback messages. | ||||||||||||||||||||||
| * An action client can handle up to 6 goals simultaneously with this optimization. If the number | ||||||||||||||||||||||
| * of goals exceeds the limit or the RMW doesn't support content filter, optimization is automatically | ||||||||||||||||||||||
| * disabled. Default: false. | ||||||||||||||||||||||
| */ | ||||||||||||||||||||||
| constructor(node, typeClass, actionName, options) { | ||||||||||||||||||||||
| super(null, null, options); | ||||||||||||||||||||||
|
|
@@ -87,6 +93,15 @@ class ActionClient extends Entity { | |||||||||||||||||||||
| checkTypes: true, | ||||||||||||||||||||||
| }; | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| // Enable feedback subscription content filter optimization. | ||||||||||||||||||||||
| // Only supported on ROS2 Rolling and only effective when the native | ||||||||||||||||||||||
| // binding provides the required functions AND the RMW implementation | ||||||||||||||||||||||
| // actually supports content filtering on the feedback subscription. | ||||||||||||||||||||||
| this._enableFeedbackMsgOptimization = | ||||||||||||||||||||||
| this._options.enableFeedbackMsgOptimization === true && | ||||||||||||||||||||||
| DistroUtils.getDistroId() >= DistroUtils.DistroId.ROLLING && | ||||||||||||||||||||||
| typeof rclnodejs.actionConfigureFeedbackSubFilterAddGoalId === 'function'; | ||||||||||||||||||||||
minggangw marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||||||||||||||||||
|
|
||||||||||||||||||||||
| let type = this.typeClass.type(); | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| this._handle = rclnodejs.actionCreateClient( | ||||||||||||||||||||||
|
|
@@ -126,6 +141,7 @@ class ActionClient extends Entity { | |||||||||||||||||||||
| } | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| this._goalHandles.set(uuid, goalHandle); | ||||||||||||||||||||||
| this._feedbackSubFilterAddGoalId(goalHandle.goalId); | ||||||||||||||||||||||
minggangw marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||||||||||||||||||
| } else { | ||||||||||||||||||||||
| // Clean up feedback callback for rejected goals | ||||||||||||||||||||||
| let uuid = ActionUuid.fromMessage( | ||||||||||||||||||||||
|
|
@@ -205,6 +221,9 @@ class ActionClient extends Entity { | |||||||||||||||||||||
| status === ActionInterfaces.GoalStatus.STATUS_ABORTED | ||||||||||||||||||||||
| ) { | ||||||||||||||||||||||
| this._goalHandles.delete(uuid); | ||||||||||||||||||||||
| this._feedbackSubFilterRemoveGoalId( | ||||||||||||||||||||||
| statusMessage.goal_info.goal_id | ||||||||||||||||||||||
| ); | ||||||||||||||||||||||
|
Comment on lines
+224
to
+226
|
||||||||||||||||||||||
| this._feedbackSubFilterRemoveGoalId( | |
| statusMessage.goal_info.goal_id | |
| ); |
Copilot
AI
Mar 30, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
_cancelGoal() removes the goal ID from the feedback filter immediately after sending the cancel request. If the cancel request is rejected (or takes time to be accepted), this can prematurely stop feedback for an active goal. Consider removing the goal ID only after the goal transitions to a terminal state (e.g., in processStatusMessage), or after a cancel response confirms the goal is canceling/canceled.
| this._feedbackSubFilterRemoveGoalId(goalHandle.goalId); |
Copilot
AI
Mar 30, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Goal IDs are removed from the feedback subscription filter in multiple places (here in _getResult completion, also in processStatusMessage terminal-state handling, and in _cancelGoal). If the native remove operation is not idempotent, a second removal can throw and will disable optimization unnecessarily. Consider tracking which goal IDs have been added/removed (e.g., a Set) and only removing once, or centralize removal in a single lifecycle point.
| this._feedbackSubFilterRemoveGoalId(goalHandle.goalId); | |
| // Ensure feedback subscription filter removal is idempotent for this goal ID. | |
| if (!this._removedFeedbackGoalIds) { | |
| this._removedFeedbackGoalIds = new Set(); | |
| } | |
| if (!this._removedFeedbackGoalIds.has(goalHandle.goalId)) { | |
| this._feedbackSubFilterRemoveGoalId(goalHandle.goalId); | |
| this._removedFeedbackGoalIds.add(goalHandle.goalId); | |
| } |
Copilot
AI
Mar 30, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
When an add/remove call throws, the code disables _enableFeedbackMsgOptimization, but it does not attempt to clear/undo any goal IDs that were already added to the underlying DDS content filter. After this point, new goals will no longer be added to the filter, so feedback for future goals may be filtered out indefinitely. If you auto-disable, also revert the subscription back to an unfiltered state (e.g., best-effort remove all previously-added goal IDs / clear the filter) and track which IDs were successfully added so cleanup is possible.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
_enableFeedbackMsgOptimizationis enabled based on the presence ofactionConfigureFeedbackSubFilterAddGoalId, but later code also callsactionConfigureFeedbackSubFilterRemoveGoalId. For safety, gate on both functions existing (or gate per-call) to avoid a runtime exception if a build/export ever provides only one of the two functions.