@@ -488,7 +488,11 @@ ResetRobotStateAction::ResetRobotStateAction(
488488 goal_sent_(false ),
489489 result_received_(false ),
490490 unload_traj_success_(false ),
491- load_traj_success_(false )
491+ load_traj_success_(false ),
492+ unload_goal_sent_(false ),
493+ reset_goal_sent_(false ),
494+ load_goal_sent_(false ),
495+ fault_reported_(false )
492496{
493497 if (!config.blackboard ) {
494498 throw BT::RuntimeError (" ResetRobotStateAction: no blackboard provided." );
@@ -553,6 +557,7 @@ BT::NodeStatus ResetRobotStateAction::onStart()
553557 unload_goal_sent_ = false ;
554558 reset_goal_sent_ = false ;
555559 load_goal_sent_ = false ;
560+ fault_reported_ = false ;
556561
557562 // Step 1: Call UnloadTrajController action
558563 UnloadTrajController::Goal unload_traj_goal;
@@ -657,10 +662,13 @@ void ResetRobotStateAction::goalResponseCallback(
657662 name ().c_str ());
658663 action_result_.success = false ;
659664 result_received_ = true ;
660- reportFault (
661- fault_codes::kRobotResetFailed , kSeverityError ,
662- std::string (" ResetRobotStateAction [" ) + name () +
663- " ]: reset_robot_state goal rejected by server" );
665+ if (!fault_reported_) {
666+ fault_reported_ = true ;
667+ reportFault (
668+ fault_codes::kRobotResetFailed , kSeverityError ,
669+ std::string (" ResetRobotStateAction [" ) + name () +
670+ " ]: reset_robot_state goal rejected by server" );
671+ }
664672 } else {
665673 RCLCPP_INFO (
666674 node_->get_logger (), " ResetRobotStateAction [%s]: ResetRobotState Goal ACCEPTED by server." ,
@@ -676,10 +684,13 @@ void ResetRobotStateAction::resultCallback(
676684 } else {
677685 action_result_.success = false ;
678686 action_result_.message = " ResetRobotState aborted or failed" ;
679- reportFault (
680- fault_codes::kRobotResetFailed , kSeverityError ,
681- std::string (" ResetRobotStateAction [" ) + name () +
682- " ]: reset_robot_state action aborted or failed" );
687+ if (!fault_reported_) {
688+ fault_reported_ = true ;
689+ reportFault (
690+ fault_codes::kRobotResetFailed , kSeverityError ,
691+ std::string (" ResetRobotStateAction [" ) + name () +
692+ " ]: reset_robot_state action aborted or failed" );
693+ }
683694 }
684695 result_received_ = true ;
685696}
@@ -692,10 +703,13 @@ void ResetRobotStateAction::goalResponseCallbackUnloadTraj(
692703 node_->get_logger (),
693704 " ResetRobotStateAction [%s]: UnloadTrajController Goal REJECTED by server." , name ().c_str ());
694705 unload_traj_success_ = false ;
695- reportFault (
696- fault_codes::kRobotResetFailed , kSeverityError ,
697- std::string (" ResetRobotStateAction [" ) + name () +
698- " ]: unload_trajectory_controller goal rejected by server" );
706+ if (!fault_reported_) {
707+ fault_reported_ = true ;
708+ reportFault (
709+ fault_codes::kRobotResetFailed , kSeverityError ,
710+ std::string (" ResetRobotStateAction [" ) + name () +
711+ " ]: unload_trajectory_controller goal rejected by server" );
712+ }
699713 } else {
700714 RCLCPP_INFO (
701715 node_->get_logger (),
@@ -708,18 +722,22 @@ void ResetRobotStateAction::resultCallbackUnloadTraj(
708722{
709723 if (wrapped_result.code == rclcpp_action::ResultCode::SUCCEEDED) {
710724 unload_traj_success_ = wrapped_result.result ->success ;
711- if (!unload_traj_success_) {
725+ if (!unload_traj_success_ && !fault_reported_) {
726+ fault_reported_ = true ;
712727 reportFault (
713728 fault_codes::kRobotResetFailed , kSeverityError ,
714729 std::string (" ResetRobotStateAction [" ) + name () +
715730 " ]: unload_trajectory_controller reported success=false" );
716731 }
717732 } else {
718733 unload_traj_success_ = false ;
719- reportFault (
720- fault_codes::kRobotResetFailed , kSeverityError ,
721- std::string (" ResetRobotStateAction [" ) + name () +
722- " ]: unload_trajectory_controller action aborted or failed" );
734+ if (!fault_reported_) {
735+ fault_reported_ = true ;
736+ reportFault (
737+ fault_codes::kRobotResetFailed , kSeverityError ,
738+ std::string (" ResetRobotStateAction [" ) + name () +
739+ " ]: unload_trajectory_controller action aborted or failed" );
740+ }
723741 }
724742}
725743
@@ -731,10 +749,13 @@ void ResetRobotStateAction::goalResponseCallbackLoadTraj(
731749 node_->get_logger (),
732750 " ResetRobotStateAction [%s]: LoadTrajController Goal REJECTED by server." , name ().c_str ());
733751 load_traj_success_ = false ;
734- reportFault (
735- fault_codes::kRobotResetFailed , kSeverityError ,
736- std::string (" ResetRobotStateAction [" ) + name () +
737- " ]: load_trajectory_controller goal rejected by server" );
752+ if (!fault_reported_) {
753+ fault_reported_ = true ;
754+ reportFault (
755+ fault_codes::kRobotResetFailed , kSeverityError ,
756+ std::string (" ResetRobotStateAction [" ) + name () +
757+ " ]: load_trajectory_controller goal rejected by server" );
758+ }
738759 } else {
739760 RCLCPP_INFO (
740761 node_->get_logger (),
@@ -747,18 +768,22 @@ void ResetRobotStateAction::resultCallbackLoadTraj(
747768{
748769 if (wrapped_result.code == rclcpp_action::ResultCode::SUCCEEDED) {
749770 load_traj_success_ = wrapped_result.result ->success ;
750- if (!load_traj_success_) {
771+ if (!load_traj_success_ && !fault_reported_) {
772+ fault_reported_ = true ;
751773 reportFault (
752774 fault_codes::kRobotResetFailed , kSeverityError ,
753775 std::string (" ResetRobotStateAction [" ) + name () +
754776 " ]: load_trajectory_controller reported success=false" );
755777 }
756778 } else {
757779 load_traj_success_ = false ;
758- reportFault (
759- fault_codes::kRobotResetFailed , kSeverityError ,
760- std::string (" ResetRobotStateAction [" ) + name () +
761- " ]: load_trajectory_controller action aborted or failed" );
780+ if (!fault_reported_) {
781+ fault_reported_ = true ;
782+ reportFault (
783+ fault_codes::kRobotResetFailed , kSeverityError ,
784+ std::string (" ResetRobotStateAction [" ) + name () +
785+ " ]: load_trajectory_controller action aborted or failed" );
786+ }
762787 }
763788}
764789
@@ -768,7 +793,8 @@ WaitForInputAction::WaitForInputAction(
768793 goal_sent_(false ),
769794 result_received_(false ),
770795 last_success_(false ),
771- last_value_(0 )
796+ last_value_(0 ),
797+ timeout_reported_(false )
772798{
773799 // Obtain the ROS2 node from the blackboard
774800 if (!config.blackboard ) {
@@ -786,6 +812,7 @@ BT::NodeStatus WaitForInputAction::onStart()
786812 result_received_ = false ;
787813 last_success_ = false ;
788814 last_value_ = 0 ;
815+ timeout_reported_ = false ;
789816
790817 // Read ports
791818 if (!getInput<std::string>(" io_type" , io_type_)) {
@@ -863,8 +890,13 @@ BT::NodeStatus WaitForInputAction::onRunning()
863890 std::to_string (desired_value_),
864891 " green" );
865892
866- // Pair: clear timeout filter once the desired value is observed.
867- reportFaultPassed (fault_codes::kSignalWaitInputTimeout );
893+ // Pair: only clear if we actually emitted the timeout earlier. A
894+ // first-pass success has no FAILED to heal and a stray PASSED biases
895+ // LocalFilter::should_forward_passed.
896+ if (timeout_reported_) {
897+ reportFaultPassed (fault_codes::kSignalWaitInputTimeout );
898+ timeout_reported_ = false ;
899+ }
868900 return BT::NodeStatus::SUCCESS;
869901 }
870902
@@ -884,6 +916,7 @@ BT::NodeStatus WaitForInputAction::onRunning()
884916 fault_codes::kSignalWaitInputTimeout , kSeverityWarn ,
885917 " WaitForInput " + io_type_ + " [" + std::to_string (ionum_) + " ] timed out after " +
886918 std::to_string (timeout_) + " s, last_value=" + std::to_string (last_value_));
919+ timeout_reported_ = true ;
887920 return BT::NodeStatus::FAILURE;
888921 }
889922 }
@@ -914,10 +947,14 @@ void WaitForInputAction::onHalted()
914947 action_client_->async_cancel_all_goals ();
915948 }
916949 // Heal a previously raised wait-timeout: when the subtree is halted
917- // mid-wait, the timeout condition no longer holds. Without this,
918- // any earlier `kSignalWaitInputTimeout` would linger in FaultManager
919- // until the next successful wait.
920- reportFaultPassed (fault_codes::kSignalWaitInputTimeout );
950+ // mid-wait, the timeout condition no longer holds. Only emit if we
951+ // actually reported the FAILED earlier — otherwise the PASSED would
952+ // accumulate in LocalFilter::should_forward_passed and bias subsequent
953+ // healing decisions.
954+ if (timeout_reported_) {
955+ reportFaultPassed (fault_codes::kSignalWaitInputTimeout );
956+ timeout_reported_ = false ;
957+ }
921958 goal_sent_ = false ;
922959 result_received_ = false ;
923960}
0 commit comments