@@ -5,6 +5,7 @@ use crate::{
5
5
wait:: WaitableNumEntities ,
6
6
Clock , Node , RclrsError , ENTITY_LIFECYCLE_MUTEX ,
7
7
} ;
8
+ use rosidl_runtime_rs:: { Action , Message , Service } ;
8
9
use std:: {
9
10
ffi:: CString ,
10
11
sync:: { atomic:: AtomicBool , Arc , Mutex , MutexGuard } ,
@@ -159,36 +160,115 @@ where
159
160
} )
160
161
}
161
162
162
- fn execute_goal_request ( & self ) -> Result < ( ) , RclrsError > {
163
- // Take pending goal request
164
- let ( request_id, request) = {
165
- let mut request_id = rmw_request_id_t {
166
- writer_guid : [ 0 ; 16 ] ,
167
- sequence_number : 0 ,
163
+ fn take_goal_request ( & self ) -> Result < ( <<T :: SendGoalService as Service >:: Request as Message >:: RmwMsg , rmw_request_id_t ) , RclrsError > {
164
+ let mut request_id = rmw_request_id_t {
165
+ writer_guid : [ 0 ; 16 ] ,
166
+ sequence_number : 0 ,
167
+ } ;
168
+ type RmwRequest < T > = <<<T as rosidl_runtime_rs:: ActionImpl >:: SendGoalService as Service >:: Request as Message >:: RmwMsg ;
169
+ let mut request_rmw = RmwRequest :: < T > :: default ( ) ;
170
+ let handle = & * self . handle . lock ( ) ;
171
+ unsafe {
172
+ // SAFETY: The three pointers are valid/initialized
173
+ rcl_action_take_goal_request (
174
+ handle,
175
+ & mut request_id,
176
+ & mut request_rmw as * mut RmwRequest < T > as * mut _ ,
177
+ )
178
+ } . ok ( ) ?;
179
+
180
+ Ok ( ( request_rmw, request_id) )
181
+ }
182
+
183
+ fn send_goal_response ( & self , response : GoalResponse , request : & <<T :: SendGoalService as Service >:: Request as Message >:: RmwMsg , mut request_id : rmw_request_id_t ) -> Result < ( ) , RclrsError > {
184
+ let accepted = response != GoalResponse :: Reject ;
185
+
186
+ // SAFETY: No preconditions
187
+ let mut goal_info = unsafe { rcl_action_get_zero_initialized_goal_info ( ) } ;
188
+ // Populate the goal UUID; the other fields will be populated by rcl_action later on.
189
+ // TODO(nwn): Check this claim.
190
+ rosidl_runtime_rs:: ExtractUuid :: extract_uuid ( request, & mut goal_info. goal_id . uuid ) ;
191
+
192
+ let goal_handle = if accepted {
193
+ let server_handle = & mut * self . handle . lock ( ) ;
194
+ let goal_handle_ptr = unsafe {
195
+ // SAFETY: The action server handle is locked and so synchronized with other
196
+ // functions. The request_id and response message are uniquely owned, and so will
197
+ // not mutate during this function call. The returned goal handle pointer should be
198
+ // valid unless it is null.
199
+ rcl_action_accept_new_goal (
200
+ server_handle,
201
+ & goal_info,
202
+ )
168
203
} ;
169
- type RmwMsg < T > =
170
- <<T as rosidl_runtime_rs:: Action >:: Goal as rosidl_runtime_rs:: Message >:: RmwMsg ;
171
- let mut request_rmw = RmwMsg :: < T > :: default ( ) ;
204
+ if goal_handle_ptr. is_null ( ) {
205
+ // Other than rcl_get_error_string(), there's no indication what happened.
206
+ panic ! ( "Failed to accept goal" ) ;
207
+ } else {
208
+ Some ( ServerGoalHandle :: < T > :: new ( goal_handle_ptr, todo ! ( "" ) , GoalUuid ( goal_info. goal_id . uuid ) ) )
209
+ }
210
+ } else {
211
+ None
212
+ } ;
213
+
214
+ {
215
+ type RmwResponse < T > = <<<T as rosidl_runtime_rs:: ActionImpl >:: SendGoalService as Service >:: Response as Message >:: RmwMsg ;
216
+ let mut response_rmw = RmwResponse :: < T > :: default ( ) ;
217
+ // TODO(nwn): Set the `accepted` field through a trait, similarly to how we extracted the UUID.
218
+ // response_rmw.accepted = accepted;
172
219
let handle = & * self . handle . lock ( ) ;
173
- let take_result = unsafe {
174
- // SAFETY: The three pointers are valid/initialized
175
- rcl_action_take_goal_request (
220
+ unsafe {
221
+ // SAFETY: The action server handle is locked and so synchronized with other
222
+ // functions. The request_id and response message are uniquely owned, and so will
223
+ // not mutate during this function call.
224
+ // Also, `rcl_action_accept_new_goal()` has been called beforehand, as specified in
225
+ // the `rcl_action` docs.
226
+ rcl_action_send_goal_response (
176
227
handle,
177
228
& mut request_id,
178
- & mut request_rmw as * mut RmwMsg < T > as * mut _ ,
229
+ & mut response_rmw as * mut RmwResponse < T > as * mut _ ,
179
230
)
180
- } ;
181
- match take_result. try_into ( ) . unwrap ( ) {
182
- RclReturnCode :: Ok => ( ) ,
231
+ } . ok ( ) ?; // TODO(nwn): Suppress RclReturnCode::Timeout?
232
+ }
233
+
234
+ if let Some ( goal_handle) = goal_handle {
235
+ // Goal was accepted
236
+
237
+ // TODO: Add a UUID->goal_handle entry to a server goal map.
238
+
239
+ // TODO: If accept_and_execute, update goal state
240
+
241
+ // TODO: Call publish_status()
242
+
243
+ // TODO: Call the goal_accepted callback
244
+ }
245
+
246
+ Ok ( ( ) )
247
+ }
248
+
249
+ fn execute_goal_request ( & self ) -> Result < ( ) , RclrsError > {
250
+ let ( request, request_id) = match self . take_goal_request ( ) {
251
+ Ok ( res) => res,
252
+ Err ( RclrsError :: RclError { code : RclReturnCode :: ServiceTakeFailed , .. } ) => {
183
253
// Spurious wakeup – this may happen even when a waitset indicated that this
184
- // service was ready, so it shouldn't be an error.
185
- RclReturnCode :: ServiceTakeFailed => return Ok ( ( ) ) ,
186
- _ => return take_result. ok ( ) ,
187
- }
188
- let request = todo ! ( "Convert request_rmw to expected type" ) ;
189
- ( request_id, request)
254
+ // action was ready, so it shouldn't be an error.
255
+ return Ok ( ( ) ) ;
256
+ } ,
257
+ Err ( err) => return Err ( err) ,
190
258
} ;
191
- todo ! ( )
259
+
260
+ let response: GoalResponse =
261
+ {
262
+ let mut uuid = GoalUuid :: default ( ) ;
263
+ rosidl_runtime_rs:: ExtractUuid :: extract_uuid ( & request, & mut uuid. 0 ) ;
264
+
265
+ todo ! ( "Optionally convert request to an idiomatic type for the user's callback." ) ;
266
+ todo ! ( "Call self.goal_callback(uuid, request)" ) ;
267
+ } ;
268
+
269
+ self . send_goal_response ( response, & request, request_id) ?;
270
+
271
+ Ok ( ( ) )
192
272
}
193
273
194
274
fn execute_cancel_request ( & self ) -> Result < ( ) , RclrsError > {
0 commit comments