1use nros_core::{CdrReader, CdrWriter, GoalId, GoalInfo, GoalStatus, GoalStatusStamped, Serialize};
11use nros_rmw::{Publisher, ServiceClientTrait, ServiceServerTrait, Subscriber, TransportError};
12
13use super::types::NodeError;
14use crate::session;
15
16pub(crate) const CANCEL_BUF: usize = 256;
21
22pub(crate) fn action_service_base_type<'a>(
38 request_type_name: &'a str,
39 fallback_action_type: &'a str,
40) -> &'a str {
41 request_type_name
42 .strip_suffix("Request_")
43 .unwrap_or(fallback_action_type)
44}
45
46const STATUS_ARRAY_BUF: usize = 512;
50
51#[derive(Clone, Copy)]
57pub struct RawActiveGoal {
58 pub goal_id: GoalId,
60 pub status: GoalStatus,
62}
63
64#[derive(Clone, Copy)]
70pub struct PendingGetResult {
71 pub goal_id: GoalId,
73 pub sequence_number: i64,
75}
76
77#[derive(Clone, Copy)]
79pub struct CompletedResultEntry {
80 pub goal_id: GoalId,
82 pub status: GoalStatus,
84 pub offset: usize,
86 pub len: usize,
88}
89
90pub struct PendingCancelRequest {
94 pub goal_id: GoalId,
96 pub sequence_number: i64,
99 pub current_status: GoalStatus,
102}
103
104pub struct RawGoalRequest {
106 pub goal_id: GoalId,
108 pub sequence_number: i64,
110 pub data_offset: usize,
114 pub data_len: usize,
116}
117
118fn read_goal_id(reader: &mut CdrReader<'_>) -> Result<GoalId, NodeError> {
131 let mut goal_id = GoalId::default();
132 for byte in &mut goal_id.uuid {
133 *byte = reader
134 .read_u8()
135 .map_err(|_| NodeError::Transport(TransportError::DeserializationError))?;
136 }
137 Ok(goal_id)
138}
139
140fn write_goal_id(writer: &mut CdrWriter<'_>, goal_id: &GoalId) -> Result<(), NodeError> {
143 for b in &goal_id.uuid {
144 writer.write_u8(*b).map_err(|_| NodeError::Serialization)?;
145 }
146 Ok(())
147}
148
149pub struct ActionServerCore<
161 const GOAL_BUF: usize = { crate::config::DEFAULT_RX_BUF_SIZE },
162 const RESULT_BUF: usize = { crate::config::DEFAULT_RX_BUF_SIZE },
163 const FEEDBACK_BUF: usize = { crate::config::DEFAULT_RX_BUF_SIZE },
164 const MAX_GOALS: usize = 4,
165> {
166 pub(crate) send_goal_server: session::RmwServiceServer,
167 pub(crate) cancel_goal_server: session::RmwServiceServer,
168 pub(crate) get_result_server: session::RmwServiceServer,
169 pub(crate) feedback_publisher: session::RmwPublisher,
170 pub(crate) status_publisher: session::RmwPublisher,
171 pub(crate) active_goals: heapless::Vec<RawActiveGoal, MAX_GOALS>,
172 pub(crate) completed_results: heapless::Vec<CompletedResultEntry, MAX_GOALS>,
173 pub(crate) pending_get_results: heapless::Vec<PendingGetResult, MAX_GOALS>,
181 pub(crate) result_slab: [u8; RESULT_BUF],
183 pub(crate) result_slab_used: usize,
184 pub(crate) goal_buffer: [u8; GOAL_BUF],
185 pub(crate) feedback_buffer: [u8; FEEDBACK_BUF],
186 pub(crate) cancel_buffer: [u8; CANCEL_BUF],
187}
188
189impl<
190 const GOAL_BUF: usize,
191 const RESULT_BUF: usize,
192 const FEEDBACK_BUF: usize,
193 const MAX_GOALS: usize,
194> ActionServerCore<GOAL_BUF, RESULT_BUF, FEEDBACK_BUF, MAX_GOALS>
195{
196 pub fn from_channels(
201 send_goal_server: session::RmwServiceServer,
202 cancel_goal_server: session::RmwServiceServer,
203 get_result_server: session::RmwServiceServer,
204 feedback_publisher: session::RmwPublisher,
205 status_publisher: session::RmwPublisher,
206 ) -> Self {
207 Self {
208 send_goal_server,
209 cancel_goal_server,
210 get_result_server,
211 feedback_publisher,
212 status_publisher,
213 active_goals: heapless::Vec::new(),
214 completed_results: heapless::Vec::new(),
215 pending_get_results: heapless::Vec::new(),
216 result_slab: [0u8; RESULT_BUF],
217 result_slab_used: 0,
218 goal_buffer: [0u8; GOAL_BUF],
219 feedback_buffer: [0u8; FEEDBACK_BUF],
220 cancel_buffer: [0u8; CANCEL_BUF],
221 }
222 }
223
224 pub fn try_recv_goal_request(&mut self) -> Result<Option<RawGoalRequest>, NodeError> {
229 let buf_start = self.goal_buffer.as_ptr() as usize;
235 let request = match self
240 .send_goal_server
241 .try_recv_request(&mut self.goal_buffer)
242 {
243 Ok(opt) => opt,
244 Err(TransportError::NoData) => return Ok(None),
245 Err(_) => return Err(NodeError::Transport(TransportError::ServiceRequestFailed)),
246 };
247
248 let request = match request {
249 Some(r) => r,
250 None => return Ok(None),
251 };
252
253 let data_offset = (request.data.as_ptr() as usize).saturating_sub(buf_start);
254 let data_len = request.data.len();
255 let sequence_number = request.sequence_number;
256 #[allow(clippy::drop_non_drop)]
257 drop(request);
258
259 let mut reader =
260 CdrReader::new_with_header(&self.goal_buffer[data_offset..data_offset + data_len])
261 .map_err(|_| NodeError::Transport(TransportError::DeserializationError))?;
262
263 let goal_id = read_goal_id(&mut reader)?;
264
265 Ok(Some(RawGoalRequest {
266 goal_id,
267 sequence_number,
268 data_offset,
269 data_len,
270 }))
271 }
272
273 pub fn goal_buffer(&self) -> &[u8] {
275 &self.goal_buffer
276 }
277
278 pub fn accept_goal(&mut self, goal_id: GoalId, seq: i64) -> Result<(), NodeError> {
281 let mut writer = CdrWriter::new_with_header(&mut self.cancel_buffer)
283 .map_err(|_| NodeError::BufferTooSmall)?;
284 writer.write_u8(1).map_err(|_| NodeError::Serialization)?;
285 writer.write_i32(0).map_err(|_| NodeError::Serialization)?;
286 writer.write_u32(0).map_err(|_| NodeError::Serialization)?;
287 let reply_len = writer.position();
288
289 self.send_goal_server
290 .send_reply(seq, &self.cancel_buffer[..reply_len])
291 .map_err(|_| NodeError::ServiceReplyFailed)?;
292
293 let _ = self.active_goals.push(RawActiveGoal {
294 goal_id,
295 status: GoalStatus::Accepted,
296 });
297 let _ = self.publish_status_array();
298 Ok(())
299 }
300
301 pub fn reject_goal(&mut self, seq: i64) -> Result<(), NodeError> {
303 let mut writer = CdrWriter::new_with_header(&mut self.cancel_buffer)
305 .map_err(|_| NodeError::BufferTooSmall)?;
306 writer.write_u8(0).map_err(|_| NodeError::Serialization)?;
307 writer.write_i32(0).map_err(|_| NodeError::Serialization)?;
308 writer.write_u32(0).map_err(|_| NodeError::Serialization)?;
309 let reply_len = writer.position();
310
311 self.send_goal_server
312 .send_reply(seq, &self.cancel_buffer[..reply_len])
313 .map_err(|_| NodeError::ServiceReplyFailed)
314 }
315
316 pub fn publish_feedback_raw(
321 &mut self,
322 goal_id: &GoalId,
323 feedback_cdr: &[u8],
324 ) -> Result<(), NodeError> {
325 let needed = 4 + 20 + feedback_cdr.len(); if needed > FEEDBACK_BUF {
328 return Err(NodeError::BufferTooSmall);
329 }
330
331 let mut writer = CdrWriter::new_with_header(&mut self.feedback_buffer)
332 .map_err(|_| NodeError::BufferTooSmall)?;
333
334 write_goal_id(&mut writer, goal_id)?;
335
336 let pos = writer.position();
338 if pos + feedback_cdr.len() > FEEDBACK_BUF {
339 return Err(NodeError::BufferTooSmall);
340 }
341 self.feedback_buffer[pos..pos + feedback_cdr.len()].copy_from_slice(feedback_cdr);
342 let len = pos + feedback_cdr.len();
343
344 self.feedback_publisher
345 .publish_raw(&self.feedback_buffer[..len])
346 .map_err(|_| NodeError::Transport(TransportError::PublishFailed))
347 }
348
349 pub fn set_goal_status(&mut self, goal_id: &GoalId, status: GoalStatus) {
351 for goal in &mut self.active_goals {
352 if goal.goal_id.uuid == goal_id.uuid {
353 goal.status = status;
354 break;
355 }
356 }
357 let _ = self.publish_status_array();
358 }
359
360 pub fn complete_goal_raw(&mut self, goal_id: &GoalId, status: GoalStatus, result_cdr: &[u8]) {
363 if let Some(pos) = self
365 .active_goals
366 .iter()
367 .position(|g| g.goal_id.uuid == goal_id.uuid)
368 {
369 self.active_goals.swap_remove(pos);
370 }
371
372 let offset = self.result_slab_used;
374 let end = offset + result_cdr.len();
375 let stored = if end <= RESULT_BUF {
376 self.result_slab[offset..end].copy_from_slice(result_cdr);
377 self.result_slab_used = end;
378 let _ = self.completed_results.push(CompletedResultEntry {
379 goal_id: *goal_id,
380 status,
381 offset,
382 len: result_cdr.len(),
383 });
384 true
385 } else {
386 false
387 };
388
389 if stored {
393 let len = result_cdr.len();
394 let mut i = 0;
395 while i < self.pending_get_results.len() {
396 if self.pending_get_results[i].goal_id.uuid == goal_id.uuid {
397 let seq = self.pending_get_results[i].sequence_number;
398 let _ = self.pending_get_results.swap_remove(i);
400 let _ = self.reply_get_result_from_slab(seq, status, offset, len);
401 } else {
402 i += 1;
403 }
404 }
405 }
406
407 let _ = self.publish_status_array();
408 }
409
410 pub fn register_goal_waker(&self, waker: &core::task::Waker) {
415 use nros_rmw::ServiceServerTrait;
416 self.send_goal_server.register_waker(waker);
417 }
418
419 pub fn register_cancel_waker(&self, waker: &core::task::Waker) {
422 use nros_rmw::ServiceServerTrait;
423 self.cancel_goal_server.register_waker(waker);
424 }
425
426 pub fn register_get_result_waker(&self, waker: &core::task::Waker) {
429 use nros_rmw::ServiceServerTrait;
430 self.get_result_server.register_waker(waker);
431 }
432
433 pub fn try_recv_cancel_request(&mut self) -> Result<Option<PendingCancelRequest>, NodeError> {
449 let buf_start = self.cancel_buffer.as_ptr() as usize;
450 let request = match self
451 .cancel_goal_server
452 .try_recv_request(&mut self.cancel_buffer)
453 {
454 Ok(Some(r)) => r,
455 Ok(None) | Err(TransportError::NoData) => return Ok(None),
456 Err(_) => return Err(NodeError::Transport(TransportError::ServiceRequestFailed)),
457 };
458
459 let data_offset = (request.data.as_ptr() as usize).saturating_sub(buf_start);
460 let data_len = request.data.len();
461 let sequence_number = request.sequence_number;
462 #[allow(clippy::drop_non_drop)]
463 drop(request);
464
465 let mut reader =
466 CdrReader::new_with_header(&self.cancel_buffer[data_offset..data_offset + data_len])
467 .map_err(|_| NodeError::Transport(TransportError::DeserializationError))?;
468
469 let goal_id = read_goal_id(&mut reader)?;
470 let current_status = self.find_goal_status(&goal_id);
471
472 Ok(Some(PendingCancelRequest {
473 goal_id,
474 sequence_number,
475 current_status,
476 }))
477 }
478
479 pub fn send_cancel_reply(
489 &mut self,
490 sequence_number: i64,
491 return_code: nros_core::CancelResponse,
492 accepted: &[GoalId],
493 ) -> Result<(), NodeError> {
494 for id in accepted {
495 self.set_goal_status(id, GoalStatus::Canceling);
496 }
497
498 let mut writer = CdrWriter::new_with_header(&mut self.goal_buffer)
499 .map_err(|_| NodeError::BufferTooSmall)?;
500 writer
501 .write_i8(return_code as i8)
502 .map_err(|_| NodeError::Serialization)?;
503 let count = u32::try_from(accepted.len()).unwrap_or(u32::MAX);
504 writer
505 .write_u32(count)
506 .map_err(|_| NodeError::Serialization)?;
507 for id in accepted {
508 write_goal_id(&mut writer, id)?;
509 writer.write_i32(0).map_err(|_| NodeError::Serialization)?;
511 writer.write_u32(0).map_err(|_| NodeError::Serialization)?;
512 }
513 let reply_len = writer.position();
514
515 self.cancel_goal_server
516 .send_reply(sequence_number, &self.goal_buffer[..reply_len])
517 .map_err(|_| NodeError::ServiceReplyFailed)?;
518
519 if !accepted.is_empty() {
520 let _ = self.publish_status_array();
521 }
522
523 Ok(())
524 }
525
526 pub fn try_handle_cancel(
528 &mut self,
529 cancel_handler: impl FnOnce(&GoalId, GoalStatus) -> nros_core::CancelResponse,
530 ) -> Result<Option<(GoalId, nros_core::CancelResponse)>, NodeError> {
531 let buf_start = self.cancel_buffer.as_ptr() as usize;
532 let request = match self
534 .cancel_goal_server
535 .try_recv_request(&mut self.cancel_buffer)
536 {
537 Ok(Some(r)) => r,
538 Ok(None) | Err(TransportError::NoData) => return Ok(None),
539 Err(_) => return Err(NodeError::Transport(TransportError::ServiceRequestFailed)),
540 };
541
542 let data_offset = (request.data.as_ptr() as usize).saturating_sub(buf_start);
543 let data_len = request.data.len();
544 let sequence_number = request.sequence_number;
545 #[allow(clippy::drop_non_drop)]
546 drop(request);
547
548 let mut reader =
549 CdrReader::new_with_header(&self.cancel_buffer[data_offset..data_offset + data_len])
550 .map_err(|_| NodeError::Transport(TransportError::DeserializationError))?;
551
552 let goal_id = read_goal_id(&mut reader)?;
553
554 let current_status = self.find_goal_status(&goal_id);
555 let response = cancel_handler(&goal_id, current_status);
556
557 if response == nros_core::CancelResponse::Ok {
558 self.set_goal_status(&goal_id, GoalStatus::Canceling);
559 }
560
561 let mut writer = CdrWriter::new_with_header(&mut self.goal_buffer)
563 .map_err(|_| NodeError::BufferTooSmall)?;
564 writer
565 .write_i8(response as i8)
566 .map_err(|_| NodeError::Serialization)?;
567
568 let num_canceling = if response == nros_core::CancelResponse::Ok {
569 1u32
570 } else {
571 0u32
572 };
573 writer
574 .write_u32(num_canceling)
575 .map_err(|_| NodeError::Serialization)?;
576 if response == nros_core::CancelResponse::Ok {
577 write_goal_id(&mut writer, &goal_id)?;
578 writer.write_i32(0).map_err(|_| NodeError::Serialization)?;
579 writer.write_u32(0).map_err(|_| NodeError::Serialization)?;
580 }
581 let reply_len = writer.position();
582
583 self.cancel_goal_server
584 .send_reply(sequence_number, &self.goal_buffer[..reply_len])
585 .map_err(|_| NodeError::ServiceReplyFailed)?;
586
587 Ok(Some((goal_id, response)))
588 }
589
590 pub fn try_handle_get_result_raw(
598 &mut self,
599 default_result_cdr: &[u8],
600 ) -> Result<Option<GoalId>, NodeError> {
601 let buf_start = self.goal_buffer.as_ptr() as usize;
602 let request = match self
604 .get_result_server
605 .try_recv_request(&mut self.goal_buffer)
606 {
607 Ok(Some(r)) => r,
608 Ok(None) | Err(TransportError::NoData) => return Ok(None),
609 Err(_) => return Err(NodeError::Transport(TransportError::ServiceRequestFailed)),
610 };
611
612 let data_offset = (request.data.as_ptr() as usize).saturating_sub(buf_start);
613 let data_len = request.data.len();
614 let sequence_number = request.sequence_number;
615 #[allow(clippy::drop_non_drop)]
616 drop(request);
617
618 let mut reader =
619 CdrReader::new_with_header(&self.goal_buffer[data_offset..data_offset + data_len])
620 .map_err(|_| NodeError::Transport(TransportError::DeserializationError))?;
621
622 let goal_id = read_goal_id(&mut reader)?;
623
624 let completed = self
626 .completed_results
627 .iter()
628 .find(|c| c.goal_id.uuid == goal_id.uuid);
629
630 if let Some(entry) = completed {
631 let (off, len, status) = (entry.offset, entry.len, entry.status);
633 self.reply_get_result_from_slab(sequence_number, status, off, len)?;
634 } else if self
635 .active_goals
636 .iter()
637 .any(|g| g.goal_id.uuid == goal_id.uuid)
638 {
639 if self
646 .pending_get_results
647 .push(PendingGetResult {
648 goal_id,
649 sequence_number,
650 })
651 .is_err()
652 {
653 return Err(NodeError::BufferTooSmall);
656 }
657 } else {
658 let mut writer = CdrWriter::new_with_header(&mut self.goal_buffer)
660 .map_err(|_| NodeError::BufferTooSmall)?;
661 writer
662 .write_i8(GoalStatus::Unknown as i8)
663 .map_err(|_| NodeError::Serialization)?;
664 writer.align(4).map_err(|_| NodeError::Serialization)?;
665 let pos = writer.position();
666 if pos + default_result_cdr.len() > GOAL_BUF {
667 return Err(NodeError::BufferTooSmall);
668 }
669 self.goal_buffer[pos..pos + default_result_cdr.len()]
670 .copy_from_slice(default_result_cdr);
671 let reply_len = pos + default_result_cdr.len();
672
673 self.get_result_server
674 .send_reply(sequence_number, &self.goal_buffer[..reply_len])
675 .map_err(|_| NodeError::ServiceReplyFailed)?;
676 }
677
678 Ok(Some(goal_id))
679 }
680
681 fn reply_get_result_from_slab(
687 &mut self,
688 sequence_number: i64,
689 status: GoalStatus,
690 slab_offset: usize,
691 slab_len: usize,
692 ) -> Result<(), NodeError> {
693 let mut writer = CdrWriter::new_with_header(&mut self.goal_buffer)
694 .map_err(|_| NodeError::BufferTooSmall)?;
695 writer
696 .write_i8(status as i8)
697 .map_err(|_| NodeError::Serialization)?;
698 writer.align(4).map_err(|_| NodeError::Serialization)?;
701 let pos = writer.position();
702 if pos + slab_len > GOAL_BUF {
703 return Err(NodeError::BufferTooSmall);
704 }
705 self.goal_buffer[pos..pos + slab_len]
706 .copy_from_slice(&self.result_slab[slab_offset..slab_offset + slab_len]);
707 let reply_len = pos + slab_len;
708 self.get_result_server
709 .send_reply(sequence_number, &self.goal_buffer[..reply_len])
710 .map_err(|_| NodeError::ServiceReplyFailed)
711 }
712
713 pub fn active_goal_count(&self) -> usize {
715 self.active_goals.len()
716 }
717
718 pub fn active_goals(&self) -> &[RawActiveGoal] {
720 &self.active_goals
721 }
722
723 pub fn find_goal_status(&self, goal_id: &GoalId) -> GoalStatus {
725 self.active_goals
726 .iter()
727 .find(|g| g.goal_id.uuid == goal_id.uuid)
728 .map(|g| g.status)
729 .unwrap_or(GoalStatus::Unknown)
730 }
731
732 pub fn publish_status_array(&self) -> Result<(), NodeError> {
734 let mut buf = [0u8; STATUS_ARRAY_BUF];
735 let mut writer =
736 CdrWriter::new_with_header(&mut buf).map_err(|_| NodeError::BufferTooSmall)?;
737
738 writer
739 .write_u32(self.active_goals.len() as u32)
740 .map_err(|_| NodeError::Serialization)?;
741
742 for goal in &self.active_goals {
743 let stamped = GoalStatusStamped::new(GoalInfo::with_id(goal.goal_id), goal.status);
744 stamped
745 .serialize(&mut writer)
746 .map_err(|_| NodeError::Serialization)?;
747 }
748
749 let len = writer.position();
750 self.status_publisher
751 .publish_raw(&buf[..len])
752 .map_err(|_| NodeError::Transport(TransportError::PublishFailed))
753 }
754}
755
756pub struct ActionClientCore<
765 const GOAL_BUF: usize = { crate::config::DEFAULT_RX_BUF_SIZE },
766 const RESULT_BUF: usize = { crate::config::DEFAULT_RX_BUF_SIZE },
767 const FEEDBACK_BUF: usize = { crate::config::DEFAULT_RX_BUF_SIZE },
768> {
769 pub(crate) send_goal_client: session::RmwServiceClient,
770 pub(crate) cancel_goal_client: session::RmwServiceClient,
771 pub(crate) get_result_client: session::RmwServiceClient,
772 pub(crate) feedback_subscriber: session::RmwSubscriber,
773 pub(crate) goal_buffer: [u8; GOAL_BUF],
774 pub(crate) result_buffer: [u8; RESULT_BUF],
775 pub(crate) feedback_buffer: [u8; FEEDBACK_BUF],
776 pub(crate) goal_counter: u64,
777 pub(crate) in_flight_send_goal: bool,
782 pub(crate) in_flight_cancel: bool,
783 pub(crate) in_flight_get_result: bool,
784}
785
786impl<const GOAL_BUF: usize, const RESULT_BUF: usize, const FEEDBACK_BUF: usize>
787 ActionClientCore<GOAL_BUF, RESULT_BUF, FEEDBACK_BUF>
788{
789 pub fn start_server_discovery(
795 &mut self,
796 timeout_ms: u32,
797 ) -> Result<(), nros_rmw::TransportError> {
798 use nros_rmw::ServiceClientTrait;
799 self.send_goal_client.start_server_discovery(timeout_ms)
800 }
801
802 pub fn poll_server_discovery(&mut self) -> Result<Option<bool>, nros_rmw::TransportError> {
805 use nros_rmw::ServiceClientTrait;
806 self.send_goal_client.poll_server_discovery()
807 }
808
809 pub fn is_server_ready(&self) -> bool {
812 use nros_rmw::ServiceClientTrait;
813 self.send_goal_client.is_server_ready()
814 }
815
816 pub fn new(
818 send_goal_client: session::RmwServiceClient,
819 cancel_goal_client: session::RmwServiceClient,
820 get_result_client: session::RmwServiceClient,
821 feedback_subscriber: session::RmwSubscriber,
822 ) -> Self {
823 Self {
824 send_goal_client,
825 cancel_goal_client,
826 get_result_client,
827 feedback_subscriber,
828 goal_buffer: [0u8; GOAL_BUF],
829 result_buffer: [0u8; RESULT_BUF],
830 feedback_buffer: [0u8; FEEDBACK_BUF],
831 goal_counter: 0,
832 in_flight_send_goal: false,
833 in_flight_cancel: false,
834 in_flight_get_result: false,
835 }
836 }
837
838 pub fn send_goal_raw(&mut self, goal_cdr: &[u8]) -> Result<GoalId, NodeError> {
846 self.goal_counter += 1;
847 let mut goal_id = GoalId::default();
848 let counter_bytes = self.goal_counter.to_le_bytes();
849 goal_id.uuid[..8].copy_from_slice(&counter_bytes);
850
851 let mut writer = CdrWriter::new_with_header(&mut self.goal_buffer)
852 .map_err(|_| NodeError::BufferTooSmall)?;
853
854 write_goal_id(&mut writer, &goal_id)?;
855
856 let pos = writer.position();
858 if pos + goal_cdr.len() > GOAL_BUF {
859 return Err(NodeError::BufferTooSmall);
860 }
861 self.goal_buffer[pos..pos + goal_cdr.len()].copy_from_slice(goal_cdr);
862 let req_len = pos + goal_cdr.len();
863
864 self.send_goal_client
865 .send_request_raw(&self.goal_buffer[..req_len])
866 .map_err(|_| NodeError::ServiceRequestFailed)?;
867
868 Ok(goal_id)
869 }
870
871 pub fn send_goal_blocking(&mut self, goal_cdr: &[u8]) -> Result<(GoalId, bool), NodeError> {
877 self.goal_counter += 1;
878 let mut goal_id = GoalId::default();
879 let counter_bytes = self.goal_counter.to_le_bytes();
880 goal_id.uuid[..8].copy_from_slice(&counter_bytes);
881
882 let mut writer = CdrWriter::new_with_header(&mut self.goal_buffer)
883 .map_err(|_| NodeError::BufferTooSmall)?;
884
885 write_goal_id(&mut writer, &goal_id)?;
886
887 let pos = writer.position();
888 if pos + goal_cdr.len() > GOAL_BUF {
889 return Err(NodeError::BufferTooSmall);
890 }
891 self.goal_buffer[pos..pos + goal_cdr.len()].copy_from_slice(goal_cdr);
892 let req_len = pos + goal_cdr.len();
893
894 #[allow(deprecated)]
895 let len = self
896 .send_goal_client
897 .call_raw(&self.goal_buffer[..req_len], &mut self.result_buffer)
898 .map_err(|_| NodeError::ServiceRequestFailed)?;
899
900 let accepted = len >= 5 && self.result_buffer[4] != 0;
902
903 Ok((goal_id, accepted))
904 }
905
906 pub fn try_recv_feedback_raw(&mut self) -> Result<Option<(GoalId, usize)>, NodeError> {
911 let data = self
912 .feedback_subscriber
913 .try_recv_raw(&mut self.feedback_buffer)
914 .map_err(|_| NodeError::Transport(TransportError::DeserializationError))?;
915
916 let len = match data {
917 Some(len) => len,
918 None => return Ok(None),
919 };
920
921 let mut reader = CdrReader::new_with_header(&self.feedback_buffer[..len])
922 .map_err(|_| NodeError::Transport(TransportError::DeserializationError))?;
923
924 let goal_id = read_goal_id(&mut reader)?;
925
926 Ok(Some((goal_id, len)))
927 }
928
929 pub fn send_cancel_request(&mut self, goal_id: &GoalId) -> Result<(), NodeError> {
934 let mut writer = CdrWriter::new_with_header(&mut self.goal_buffer)
935 .map_err(|_| NodeError::BufferTooSmall)?;
936
937 write_goal_id(&mut writer, goal_id)?;
938 writer.write_i32(0).map_err(|_| NodeError::Serialization)?;
939 writer.write_u32(0).map_err(|_| NodeError::Serialization)?;
940
941 let req_len = writer.position();
942
943 self.cancel_goal_client
944 .send_request_raw(&self.goal_buffer[..req_len])
945 .map_err(|_| NodeError::ServiceRequestFailed)
946 }
947
948 pub fn send_get_result_request(&mut self, goal_id: &GoalId) -> Result<(), NodeError> {
953 let mut writer = CdrWriter::new_with_header(&mut self.goal_buffer)
954 .map_err(|_| NodeError::BufferTooSmall)?;
955
956 write_goal_id(&mut writer, goal_id)?;
957
958 let req_len = writer.position();
959
960 self.get_result_client
961 .send_request_raw(&self.goal_buffer[..req_len])
962 .map_err(|_| NodeError::ServiceRequestFailed)
963 }
964
965 pub fn register_goal_response_waker(&self, waker: &core::task::Waker) {
968 use nros_rmw::ServiceClientTrait;
969 self.send_goal_client.register_waker(waker);
970 }
971
972 pub fn register_cancel_response_waker(&self, waker: &core::task::Waker) {
974 use nros_rmw::ServiceClientTrait;
975 self.cancel_goal_client.register_waker(waker);
976 }
977
978 pub fn register_result_waker(&self, waker: &core::task::Waker) {
980 use nros_rmw::ServiceClientTrait;
981 self.get_result_client.register_waker(waker);
982 }
983
984 pub fn register_feedback_waker(&self, waker: &core::task::Waker) {
986 use nros_rmw::Subscriber;
987 self.feedback_subscriber.register_waker(waker);
988 }
989
990 pub fn try_recv_cancel_reply(&mut self) -> Result<Option<usize>, NodeError> {
995 match self
996 .cancel_goal_client
997 .try_recv_reply_raw(&mut self.result_buffer)
998 {
999 Ok(opt) => Ok(opt),
1000 Err(TransportError::NoData) => Ok(None),
1001 Err(_) => Err(NodeError::Transport(TransportError::DeserializationError)),
1002 }
1003 }
1004
1005 pub fn try_recv_get_result_reply(&mut self) -> Result<Option<usize>, NodeError> {
1014 match self
1016 .get_result_client
1017 .try_recv_reply_raw(&mut self.result_buffer)
1018 {
1019 Ok(opt) => Ok(opt),
1020 Err(TransportError::NoData) => Ok(None),
1021 Err(_) => Err(NodeError::Transport(TransportError::DeserializationError)),
1022 }
1023 }
1024
1025 pub fn try_recv_send_goal_reply(&mut self) -> Result<Option<usize>, NodeError> {
1032 match self
1034 .send_goal_client
1035 .try_recv_reply_raw(&mut self.result_buffer)
1036 {
1037 Ok(opt) => Ok(opt),
1038 Err(TransportError::NoData) => Ok(None),
1039 Err(_) => Err(NodeError::Transport(TransportError::DeserializationError)),
1040 }
1041 }
1042
1043 pub fn get_result_blocking(&mut self, goal_id: &GoalId) -> Result<usize, NodeError> {
1050 let mut writer = CdrWriter::new_with_header(&mut self.goal_buffer)
1051 .map_err(|_| NodeError::BufferTooSmall)?;
1052
1053 write_goal_id(&mut writer, goal_id)?;
1054
1055 let req_len = writer.position();
1056
1057 #[allow(deprecated)]
1058 let len = self
1059 .get_result_client
1060 .call_raw(&self.goal_buffer[..req_len], &mut self.result_buffer)
1061 .map_err(|_| NodeError::ServiceRequestFailed)?;
1062
1063 Ok(len)
1064 }
1065
1066 pub fn result_buffer_ref(&self) -> &[u8] {
1068 &self.result_buffer
1069 }
1070
1071 pub fn feedback_buffer_ref(&self) -> &[u8] {
1073 &self.feedback_buffer
1074 }
1075
1076 pub fn goal_counter(&self) -> u64 {
1078 self.goal_counter
1079 }
1080}