Skip to content

Commit 104f778

Browse files
authored
Merge pull request #19 from vortexntnu/feat/state-utils
Feat/state utils
2 parents f053c27 + 166226a commit 104f778

6 files changed

Lines changed: 124 additions & 9 deletions

File tree

‎mission/vortex_yasmin_utils/CMakeLists.txt‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ add_library(vortex_yasmin_utils
2121
src/landmark_polling_state.cpp
2222
src/service_trigger_wait_state.cpp
2323
src/waypoint_goal_state.cpp
24+
src/persistent_waypoint_manager_state.cpp
2425
)
2526

2627
target_include_directories(vortex_yasmin_utils PUBLIC

‎mission/vortex_yasmin_utils/include/vortex_yasmin_utils/first_wins_concurrence.hpp‎

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
#include <mutex>
77
#include <string>
88
#include <unordered_map>
9+
#include <unordered_set>
910

1011
#include <yasmin/state.hpp>
1112
#include <yasmin/types.hpp>
@@ -48,14 +49,21 @@ using FirstWinsOutcomeMap =
4849
* @param states Map of logical name → state to run concurrently.
4950
* @param default_outcome Returned when the winner's outcome has no mapping.
5051
* @param outcome_map Per-state outcome translation table (see above).
52+
* @param winner_states Optional set of state names that are eligible to win.
53+
* If empty (default), every state can win. When
54+
* non-empty, only a state whose name appears in this set
55+
* can trigger "first done"; other states run alongside
56+
* but their completion is silently ignored.
5157
*/
5258
class FirstWinsConcurrence : public yasmin::State {
5359
public:
5460
YASMIN_PTR_ALIASES(FirstWinsConcurrence)
5561

56-
FirstWinsConcurrence(const yasmin::StateMap& states,
57-
const std::string& default_outcome,
58-
const FirstWinsOutcomeMap& outcome_map);
62+
FirstWinsConcurrence(
63+
const yasmin::StateMap& states,
64+
const std::string& default_outcome,
65+
const FirstWinsOutcomeMap& outcome_map,
66+
const std::unordered_set<std::string>& winner_states = {});
5967

6068
std::string execute(yasmin::Blackboard::SharedPtr blackboard) override;
6169

@@ -65,6 +73,7 @@ class FirstWinsConcurrence : public yasmin::State {
6573
const yasmin::StateMap states_;
6674
const std::string default_outcome_;
6775
const FirstWinsOutcomeMap outcome_map_;
76+
const std::unordered_set<std::string> winner_states_;
6877

6978
std::atomic<bool> first_done_{false};
7079
std::string winning_state_name_;
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
#ifndef VORTEX_YASMIN_UTILS__PERSISTENT_WAYPOINT_MANAGER_STATE_HPP_
2+
#define VORTEX_YASMIN_UTILS__PERSISTENT_WAYPOINT_MANAGER_STATE_HPP_
3+
4+
#include <string>
5+
6+
#include <vortex_msgs/action/waypoint_manager.hpp>
7+
8+
#include <yasmin_ros/action_state.hpp>
9+
10+
namespace vortex_yasmin_utils {
11+
12+
using WaypointManagerAction = vortex_msgs::action::WaypointManager;
13+
14+
/**
15+
* @brief Starts the WaypointManager action server in persistent mode.
16+
*
17+
* Sends an empty waypoint list with persistent = true, so the action server
18+
* stays alive and accepts waypoints from other sources. Does not send any
19+
* waypoints itself.
20+
*
21+
* All configuration is stored as member variables — no blackboard reads or
22+
* writes — so instances are safe to use inside a yasmin concurrent container.
23+
*
24+
* Outcomes: SUCCEED, ABORT.
25+
*
26+
* @param action_server_name Name of the WaypointManager action server.
27+
* @param convergence_threshold Convergence threshold for the action server.
28+
*/
29+
class PersistentWaypointManagerState
30+
: public yasmin_ros::ActionState<WaypointManagerAction> {
31+
public:
32+
PersistentWaypointManagerState(const std::string& action_server_name,
33+
double convergence_threshold = 0.1);
34+
35+
WaypointManagerAction::Goal create_goal(
36+
yasmin::Blackboard::SharedPtr blackboard);
37+
38+
private:
39+
double convergence_threshold_;
40+
};
41+
42+
} // namespace vortex_yasmin_utils
43+
44+
#endif // VORTEX_YASMIN_UTILS__PERSISTENT_WAYPOINT_MANAGER_STATE_HPP_

‎mission/vortex_yasmin_utils/src/first_wins_concurrence.cpp‎

Lines changed: 25 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
#include <stdexcept>
44
#include <thread>
5+
#include <unordered_set>
56
#include <vector>
67

78
#include <yasmin/logs.hpp>
@@ -27,18 +28,28 @@ yasmin::Outcomes FirstWinsConcurrence::collect_outcomes(
2728
FirstWinsConcurrence::FirstWinsConcurrence(
2829
const yasmin::StateMap& states,
2930
const std::string& default_outcome,
30-
const FirstWinsOutcomeMap& outcome_map)
31+
const FirstWinsOutcomeMap& outcome_map,
32+
const std::unordered_set<std::string>& winner_states)
3133
: State(collect_outcomes(outcome_map, default_outcome)),
3234
states_(states),
3335
default_outcome_(default_outcome),
34-
outcome_map_(outcome_map) {
36+
outcome_map_(outcome_map),
37+
winner_states_(winner_states) {
3538
for (const auto& [state_name, _] : outcome_map) {
3639
if (states.find(state_name) == states.end()) {
3740
throw std::invalid_argument(
3841
"FirstWinsConcurrence: outcome_map references unknown state '" +
3942
state_name + "'");
4043
}
4144
}
45+
for (const auto& name : winner_states_) {
46+
if (states.find(name) == states.end()) {
47+
throw std::invalid_argument(
48+
"FirstWinsConcurrence: winner_states references unknown "
49+
"state '" +
50+
name + "'");
51+
}
52+
}
4253
}
4354

4455
std::string FirstWinsConcurrence::execute(
@@ -54,7 +65,18 @@ std::string FirstWinsConcurrence::execute(
5465
threads.emplace_back([this, blackboard, name = name, state = state]() {
5566
const std::string outcome = (*state)(blackboard);
5667

57-
// Only the first completion wins; subsequent ones are ignored.
68+
// If winner_states is set, only those states can win the race.
69+
if (!winner_states_.empty() &&
70+
winner_states_.find(name) == winner_states_.end()) {
71+
YASMIN_LOG_INFO(
72+
"FirstWinsConcurrence: '%s' finished with '%s' but is not "
73+
"an eligible winner, ignoring",
74+
name.c_str(), outcome.c_str());
75+
return;
76+
}
77+
78+
// Only the first eligible completion wins; subsequent ones are
79+
// ignored.
5880
bool expected = false;
5981
if (first_done_.compare_exchange_strong(expected, true)) {
6082
{

‎mission/vortex_yasmin_utils/src/landmark_converge_state.cpp‎

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
#include <vortex/utils/ros/ros_conversions.hpp>
44
#include <vortex/utils/waypoint_utils.hpp>
5+
#include <vortex_msgs/msg/landmark.hpp>
56

67
namespace vortex_yasmin_utils {
78

@@ -19,10 +20,24 @@ LandmarkConvergeState::LandmarkConvergeState(
1920
subtype_(subtype) {}
2021

2122
LandmarkConvergenceAction::Goal LandmarkConvergeState::create_goal(
22-
yasmin::Blackboard::SharedPtr /*blackboard*/) {
23+
yasmin::Blackboard::SharedPtr blackboard) {
2324
LandmarkConvergenceAction::Goal goal;
24-
goal.type = type_;
25-
goal.subtype = subtype_;
25+
26+
if (blackboard->contains("landmarks")) {
27+
auto landmarks =
28+
blackboard->get<std::vector<vortex_msgs::msg::Landmark>>(
29+
"landmarks");
30+
if (!landmarks.empty()) {
31+
goal.type = landmarks.front().type;
32+
goal.subtype = landmarks.front().subtype;
33+
} else {
34+
goal.type = type_;
35+
goal.subtype = subtype_;
36+
}
37+
} else {
38+
goal.type = type_;
39+
goal.subtype = subtype_;
40+
}
2641
goal.convergence_offset = vortex::utils::ros_conversions::to_pose_msg(
2742
convergence_goal_.convergence_offset);
2843
goal.convergence_threshold = convergence_goal_.convergence_threshold;
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
#include "vortex_yasmin_utils/persistent_waypoint_manager_state.hpp"
2+
3+
namespace vortex_yasmin_utils {
4+
5+
PersistentWaypointManagerState::PersistentWaypointManagerState(
6+
const std::string& action_server_name,
7+
double convergence_threshold)
8+
: ActionState(action_server_name,
9+
std::bind(&PersistentWaypointManagerState::create_goal,
10+
this,
11+
std::placeholders::_1)),
12+
convergence_threshold_(convergence_threshold) {}
13+
14+
WaypointManagerAction::Goal PersistentWaypointManagerState::create_goal(
15+
yasmin::Blackboard::SharedPtr /*blackboard*/) {
16+
WaypointManagerAction::Goal goal;
17+
goal.waypoints = {};
18+
goal.persistent = true;
19+
goal.convergence_threshold = convergence_threshold_;
20+
21+
return goal;
22+
}
23+
24+
} // namespace vortex_yasmin_utils

0 commit comments

Comments
 (0)