1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
use super::{CancellingGoal, FeedbackPublisher, LiveActionServerGoal, TerminatedGoal};
use rosidl_runtime_rs::Action;
use std::{future::Future, sync::Arc};
/// This represents a goal that is in the Executing state. This struct is held
/// by an action server implementation and is used to provide feedback to the
/// client, to listen for cancellation requests, and to transition the goal into
/// its next state.
///
/// If you drop this struct without explicitly transitioning it to its next state,
/// the goal will report itself as aborted.
pub struct ExecutingGoal<A: Action> {
live: Arc<LiveActionServerGoal<A>>,
}
impl<A: Action> ExecutingGoal<A> {
/// Get the goal of this action.
pub fn goal(&self) -> &Arc<A::Goal> {
self.live.goal()
}
/// Transition the goal into the succeeded state.
///
/// "Succeeded" is a terminal state, so the state of the goal can no longer
/// be changed after this. Publish all relevant feedback before calling this.
pub fn succeeded_with(self, result: A::Result) -> TerminatedGoal {
self.live.transition_to_succeed(result);
TerminatedGoal {
uuid: *self.live.goal_id(),
}
}
/// Process a [`Future`] until it is finished or until a cancellation request
/// is received.
///
/// If the [`Future`] finishes, its output will be provided in [`Ok`].
///
/// If a cancellation request is received before the [`Future`] is finished,
/// you will receive an [`Err`] with the current state of the [`Future`],
/// which you can continue processing later if you choose. If you would rather
/// discard the [`Future`] when a cancellation request is received, you can
/// call [`Self::unless_cancel_requested`] instead.
///
/// After the cancellation request is received, you will still need to trigger
/// [`Self::begin_cancelling`] or [`Self::reject_cancellation`] to respond to
/// the request. Otherwise the cancellation request will not receive a response
/// until the goal reaches a terminal state.
//
// TODO(@mxgrey): Add a doctest and example for this.
pub async fn until_cancel_requested<F: Future + Unpin>(&self, f: F) -> Result<F::Output, F> {
self.live.cancellation().until_cancel_requested(f).await
}
/// Process a [`Future`] until it is finished unless a cancellation request
/// is received.
///
/// If the [`Future`] finishes, its output will be provided in [`Ok`].
///
/// If a cancellation request is received before the [`Future`] is finished,
/// the [`Future`] will be discarded. This allows non-[`Unpin`] futures to
/// be passed to this method. If your future implements [`Unpin`] and you want
/// the option to keep processing it after the cancellation request is received,
/// then you can call [`Self::until_cancel_requested`] instead.
///
/// After the cancellation request is received, you will still need to trigger
/// [`Self::begin_cancelling`] or [`Self::reject_cancellation`] to respond to
/// the request. Otherwise the cancellation request will not receive a response
/// until the goal reaches a terminal state.
//
// TODO(@mxgrey): Add a doctest and example for this.
pub async fn unless_cancel_requested<F: Future>(&self, f: F) -> Result<F::Output, ()> {
self.live.cancellation().unless_cancel_requested(f).await
}
/// Transition the goal into the cancelling state.
///
/// This does not require an action client to request a cancellation. If no
/// cancellation was requested, then using this function will have your action
/// server act as an action client that is commanding that the goal transition
/// to cancel.
///
/// If there are any open cancellation requests for this goal from any action
/// clients, they will all be notified that the cancellation is accepted. Any
/// new cancellation requests that arrive for the goal after this will
/// automatically be accepted.
///
/// For the goal to reach the cancelled state, you must follow this up with
/// [`CancellingGoal::cancelled_with`].
#[must_use]
pub fn begin_cancelling(self) -> CancellingGoal<A> {
CancellingGoal::new(self.live)
}
/// If there are any open cancellation requests for this goal, reject them.
/// This does not transition the goal in any way.
pub fn reject_cancellation(&self) {
self.live.reject_cancellation();
}
/// Transition the goal into the aborted state.
///
/// "Aborted" is a terminal state, so the state of the goal can no longer
/// be changed after this. Publish all relevant feedback before calling this.
pub fn aborted_with(self, result: A::Result) -> TerminatedGoal {
self.live.transition_to_aborted(result);
TerminatedGoal {
uuid: *self.live.goal_id(),
}
}
/// Publish feedback for action clients to read.
///
/// If you need to publish feedback from a separate thread or async task
/// which does not have direct access to the goal's state machine, you can
/// use [`Self::feedback_publisher`] to get a handle that you can pass along.
pub fn publish_feedback(&self, feedback: A::Feedback) {
self.live.publish_feedback(feedback);
}
/// Get a handle specifically for publishing feedback for this goal. This
/// publisher can be used separately from the overall state machine of the
/// goal, but it will stop working once the goal reaches a terminal state.
///
/// If you just need to publish a one-off feedback message, you can use
/// [`Self::publish_feedback`].
pub fn feedback_publisher(&self) -> FeedbackPublisher<A> {
FeedbackPublisher::new(Arc::clone(&self.live))
}
pub(super) fn new(live: Arc<LiveActionServerGoal<A>>) -> Self {
live.transition_to_executing();
Self { live }
}
}