1use core::marker::PhantomData;
4
5use nros_core::{RosAction, RosMessage, RosService};
6use nros_rmw::{ActionInfo, QoSProfile, ServiceInfo, Session as _, TopicInfo, TransportError};
7
8use crate::{
9 rmw_type_registry::{MessageForRmw, register_type},
10 session,
11};
12
13use super::{
14 handles::{
15 ActionClient, ActionClientCallback, ActionServer, EmbeddedPublisher, EmbeddedServiceClient,
16 EmbeddedServiceServer, ServiceClientCallback, Subscription,
17 },
18 types::NodeError,
19};
20
21pub(crate) fn validate_qos_or_report(
42 qos: &QoSProfile,
43 supported: nros_rmw::QoSPolicyMask,
44 kind: &str,
45 name: &str,
46) -> Result<(), NodeError> {
47 match qos.validate_against(supported) {
48 Ok(()) => Ok(()),
49 Err(err) => {
50 nros_log::log_error!(
51 nros_log::get_logger("nros_node"),
52 "qos: {} '{}' refused — the backend does not honour {}",
53 kind,
54 name,
55 err.qos_policy_name()
60 .unwrap_or("a QoS policy it did not name")
61 );
62 Err(NodeError::Transport(err))
63 }
64 }
65}
66
67pub struct NodeHandle<'a> {
73 name: heapless::String<64>,
74 namespace: heapless::String<64>,
75 session: &'a mut session::ConcreteSession,
76 remaps: super::spin::RemapView<'a>,
80 domain_id: u32,
81 qos_overrides: &'static [nros_rmw::QoSOverride],
88 monitors: &'static [crate::executor::monitor::MonitorSpec],
92 age_monitors: &'static [crate::executor::monitor::AgeMonitorSpec],
95 epoch_us_fn: Option<fn() -> u64>,
96}
97
98impl<'a> NodeHandle<'a> {
99 pub(crate) fn new(
101 name: heapless::String<64>,
102 namespace: heapless::String<64>,
103 session: &'a mut session::ConcreteSession,
104 remaps: super::spin::RemapView<'a>,
105 domain_id: u32,
106 ) -> Self {
107 Self {
108 name,
109 namespace,
110 session,
111 remaps,
112 domain_id,
113 qos_overrides: &[],
114 monitors: &[],
115 age_monitors: &[],
116 epoch_us_fn: None,
117 }
118 }
119
120 pub fn set_qos_overrides(&mut self, overrides: &'static [nros_rmw::QoSOverride]) {
128 self.qos_overrides = overrides;
129 }
130
131 pub fn set_monitors(&mut self, monitors: &'static [crate::executor::monitor::MonitorSpec]) {
134 self.monitors = monitors;
135 }
136
137 pub fn set_age_monitors(
140 &mut self,
141 table: &'static [crate::executor::monitor::AgeMonitorSpec],
142 epoch_us: Option<fn() -> u64>,
143 ) {
144 self.age_monitors = table;
145 self.epoch_us_fn = epoch_us;
146 }
147
148 #[must_use]
150 pub fn qos_overrides(&self) -> &'static [nros_rmw::QoSOverride] {
151 self.qos_overrides
152 }
153
154 pub fn name(&self) -> &str {
156 &self.name
157 }
158
159 #[must_use]
176 pub fn serialization_format(&self) -> Option<&'static str> {
177 nros_rmw::Session::serialization_format(&*self.session)
178 }
179
180 #[must_use]
192 pub fn rmw_implementation_identifier(&self) -> Option<&'static str> {
193 nros_rmw::Session::implementation_identifier(&*self.session)
194 }
195
196 #[must_use]
227 pub fn logger(&self) -> &'static nros_log::Logger {
228 nros_log::resolve_node_logger(self.name())
229 }
230
231 pub fn domain_id(&self) -> u32 {
233 self.domain_id
234 }
235
236 pub fn set_domain_id(&mut self, domain_id: u32) {
238 self.domain_id = domain_id;
239 }
240
241 pub fn session_mut(&mut self) -> &mut session::ConcreteSession {
243 self.session
244 }
245
246 fn topic_info<'b>(
266 domain_id: u32,
267 node_name: &'b str,
268 namespace: &'b str,
269 topic_name: &'b str,
270 type_name: &'b str,
271 type_hash: &'b str,
272 ) -> TopicInfo<'b> {
273 TopicInfo::new(topic_name, type_name, type_hash)
274 .with_domain(domain_id)
275 .with_node_name(node_name)
276 .with_namespace(namespace)
277 }
278
279 fn service_info<'b>(
280 domain_id: u32,
281 node_name: &'b str,
282 namespace: &'b str,
283 service_name: &'b str,
284 type_name: &'b str,
285 type_hash: &'b str,
286 ) -> ServiceInfo<'b> {
287 ServiceInfo::new(service_name, type_name, type_hash)
288 .with_domain(domain_id)
289 .with_node_name(node_name)
290 .with_namespace(namespace)
291 }
292
293 fn action_info<'b>(
294 domain_id: u32,
295 action_name: &'b str,
296 type_name: &'b str,
297 type_hash: &'b str,
298 ) -> ActionInfo<'b> {
299 ActionInfo::new(action_name, type_name, type_hash).with_domain(domain_id)
303 }
304
305 fn remapped(&self, source: &str) -> Option<crate::names::ResolvedName> {
325 crate::names::remap_layered(
326 source,
327 &self.name,
328 &self.namespace,
329 self.remaps.launch_rules(&self.name, &self.namespace),
330 self.remaps.argv_rules(&self.name, &self.namespace),
331 )
332 }
333
334 pub fn create_publisher<M: MessageForRmw>(
338 &mut self,
339 topic_name: &str,
340 ) -> Result<EmbeddedPublisher<M>, NodeError> {
341 self.create_publisher_with_qos::<M>(topic_name, QoSProfile::default())
342 }
343
344 pub fn create_publisher_with_qos<M: MessageForRmw>(
346 &mut self,
347 topic_name: &str,
348 qos: QoSProfile,
349 ) -> Result<EmbeddedPublisher<M>, NodeError> {
350 crate::format_check::assert_message_format::<M>();
354 register_type::<M>()?;
358 let qos = qos.apply_overrides(
362 topic_name,
363 nros_rmw::QoSOverrideRole::Publisher,
364 self.qos_overrides,
365 );
366 let remapped = self.remapped(topic_name);
369 let topic = Self::topic_info(
370 self.domain_id,
371 &self.name,
372 &self.namespace,
373 remapped.as_deref().unwrap_or(topic_name),
374 <M as RosMessage>::TYPE_NAME,
375 <M as RosMessage>::TYPE_HASH,
376 );
377 let qos = crate::declared_qos::honour_publisher(topic.type_name, topic.name, qos)?;
381 validate_qos_or_report(
382 &qos,
383 nros_rmw::Session::supported_qos_policies(self.session),
384 "publisher",
385 topic_name,
386 )?;
387 let handle = self
388 .session
389 .create_publisher(&topic, qos)
390 .map_err(|_| NodeError::Transport(TransportError::PublisherCreationFailed))?;
391 let monitor = self
394 .monitors
395 .iter()
396 .find(|m| m.topic == topic_name)
397 .map(|m| m.cell);
398 Ok(EmbeddedPublisher {
399 handle,
400 event_regs: crate::executor::handles::empty_event_regs(),
401 monitor,
402 epoch: self.epoch_us_fn,
403 _phantom: PhantomData,
404 })
405 }
406
407 pub fn create_publisher_raw(
413 &mut self,
414 topic_name: &str,
415 type_name: &str,
416 type_hash: &str,
417 ) -> Result<crate::executor::handles::EmbeddedRawPublisher, NodeError> {
418 self.create_publisher_raw_with_qos(topic_name, type_name, type_hash, QoSProfile::default())
419 }
420
421 pub fn create_publisher_raw_with_qos(
423 &mut self,
424 topic_name: &str,
425 type_name: &str,
426 type_hash: &str,
427 qos: QoSProfile,
428 ) -> Result<crate::executor::handles::EmbeddedRawPublisher, NodeError> {
429 let qos = qos.apply_overrides(
431 topic_name,
432 nros_rmw::QoSOverrideRole::Publisher,
433 self.qos_overrides,
434 );
435 let remapped = self.remapped(topic_name);
436 let topic = Self::topic_info(
437 self.domain_id,
438 &self.name,
439 &self.namespace,
440 remapped.as_deref().unwrap_or(topic_name),
441 type_name,
442 type_hash,
443 );
444 let qos = crate::declared_qos::honour_publisher(topic.type_name, topic.name, qos)?;
446 validate_qos_or_report(
447 &qos,
448 nros_rmw::Session::supported_qos_policies(self.session),
449 "publisher",
450 topic_name,
451 )?;
452 let handle = self
453 .session
454 .create_publisher(&topic, qos)
455 .map_err(|_| NodeError::Transport(TransportError::PublisherCreationFailed))?;
456 Ok(crate::executor::handles::EmbeddedRawPublisher {
457 handle,
458 arena: crate::executor::handles::TxArena::new(),
459 event_regs: crate::executor::handles::empty_event_regs(),
460 monitor: self
462 .monitors
463 .iter()
464 .find(|m| m.topic == topic_name)
465 .map(|m| m.cell),
466 })
467 }
468
469 pub fn publisher<'t>(&mut self, topic: &'t str) -> PublisherBuilder<'_, 'a, 't> {
475 PublisherBuilder {
476 node: self,
477 topic,
478 qos: QoSProfile::default(),
479 }
480 }
481
482 pub fn create_subscription<M: MessageForRmw>(
486 &mut self,
487 topic_name: &str,
488 ) -> Result<Subscription<M>, NodeError> {
489 self.create_subscription_sized::<M, { crate::config::DEFAULT_RX_BUF_SIZE }>(topic_name)
490 }
491
492 pub fn create_subscription_sized<M: MessageForRmw, const RX_BUF: usize>(
494 &mut self,
495 topic_name: &str,
496 ) -> Result<Subscription<M, RX_BUF>, NodeError> {
497 self.create_subscription_with_qos::<M, RX_BUF>(topic_name, QoSProfile::default())
498 }
499
500 pub fn create_subscription_with_qos<M: MessageForRmw, const RX_BUF: usize>(
502 &mut self,
503 topic_name: &str,
504 qos: QoSProfile,
505 ) -> Result<Subscription<M, RX_BUF>, NodeError> {
506 crate::format_check::assert_message_format::<M>();
510 register_type::<M>()?;
512 let qos = qos.apply_overrides(
514 topic_name,
515 nros_rmw::QoSOverrideRole::Subscription,
516 self.qos_overrides,
517 );
518 validate_qos_or_report(
519 &qos,
520 nros_rmw::Session::supported_qos_policies(self.session),
521 "subscription",
522 topic_name,
523 )?;
524 let remapped = self.remapped(topic_name);
525 let topic = Self::topic_info(
526 self.domain_id,
527 &self.name,
528 &self.namespace,
529 remapped.as_deref().unwrap_or(topic_name),
530 <M as RosMessage>::TYPE_NAME,
531 <M as RosMessage>::TYPE_HASH,
532 );
533 let qos = crate::declared_qos::honour(topic.type_name, topic.name, qos)?;
535 let topic = crate::declared_qos::with_declared_arrival_rate(topic);
537 let handle = self
538 .session
539 .create_subscription(&topic, qos)
540 .map_err(NodeError::Transport)?;
541 let age_mon = match <M as RosMessage>::STAMP_OFFSET {
545 Some(_) => self
546 .age_monitors
547 .iter()
548 .find(|a| a.topic == topic_name)
549 .map(|a| (a.cell, self.epoch_us_fn)),
550 None => None,
551 };
552 Ok(Subscription {
553 handle,
554 buffer: [0u8; RX_BUF],
555 event_regs: crate::executor::handles::empty_event_regs(),
556 age_mon,
557 _phantom: PhantomData,
558 })
559 }
560
561 pub fn create_subscription_raw(
563 &mut self,
564 topic_name: &str,
565 type_name: &str,
566 type_hash: &str,
567 ) -> Result<crate::executor::handles::RawSubscription, NodeError> {
568 self.create_subscription_raw_sized::<{ crate::config::DEFAULT_RX_BUF_SIZE }>(
569 topic_name, type_name, type_hash,
570 )
571 }
572
573 pub fn create_subscription_raw_sized<const RX_BUF: usize>(
575 &mut self,
576 topic_name: &str,
577 type_name: &str,
578 type_hash: &str,
579 ) -> Result<crate::executor::handles::RawSubscription<RX_BUF>, NodeError> {
580 let qos = QoSProfile::default().apply_overrides(
585 topic_name,
586 nros_rmw::QoSOverrideRole::Subscription,
587 self.qos_overrides,
588 );
589 validate_qos_or_report(
590 &qos,
591 nros_rmw::Session::supported_qos_policies(self.session),
592 "subscription",
593 topic_name,
594 )?;
595 let remapped = self.remapped(topic_name);
596 let topic = Self::topic_info(
597 self.domain_id,
598 &self.name,
599 &self.namespace,
600 remapped.as_deref().unwrap_or(topic_name),
601 type_name,
602 type_hash,
603 );
604 let qos = crate::declared_qos::honour(topic.type_name, topic.name, qos)?;
609 let topic = crate::declared_qos::with_declared_arrival_rate(topic);
611 let handle = self
612 .session
613 .create_subscription(&topic, qos)
614 .map_err(NodeError::Transport)?;
615 Ok(crate::executor::handles::RawSubscription {
616 handle,
617 buffer: [0u8; RX_BUF],
618 event_regs: crate::executor::handles::empty_event_regs(),
619 })
620 }
621
622 pub fn create_service<Svc: RosService>(
626 &mut self,
627 service_name: &str,
628 ) -> Result<EmbeddedServiceServer<Svc>, NodeError>
629 where
630 Svc::Request: MessageForRmw,
631 Svc::Reply: MessageForRmw,
632 {
633 self.create_service_sized::<Svc, { crate::config::DEFAULT_RX_BUF_SIZE }, { crate::config::DEFAULT_RX_BUF_SIZE }>(service_name, QoSProfile::services_default())
634 }
635
636 pub fn create_service_with_qos<Svc: RosService>(
639 &mut self,
640 service_name: &str,
641 qos: QoSProfile,
642 ) -> Result<EmbeddedServiceServer<Svc>, NodeError>
643 where
644 Svc::Request: MessageForRmw,
645 Svc::Reply: MessageForRmw,
646 {
647 self.create_service_sized::<Svc, { crate::config::DEFAULT_RX_BUF_SIZE }, { crate::config::DEFAULT_RX_BUF_SIZE }>(service_name, qos)
648 }
649
650 pub fn create_service_sized<Svc: RosService, const REQ_BUF: usize, const REPLY_BUF: usize>(
652 &mut self,
653 service_name: &str,
654 qos: QoSProfile,
655 ) -> Result<EmbeddedServiceServer<Svc, REQ_BUF, REPLY_BUF>, NodeError>
656 where
657 Svc::Request: MessageForRmw,
658 Svc::Reply: MessageForRmw,
659 {
660 register_type::<Svc::Request>()?;
663 register_type::<Svc::Reply>()?;
664 validate_qos_or_report(
669 &qos,
670 nros_rmw::Session::supported_qos_policies(self.session),
671 "service",
672 service_name,
673 )?;
674 let remapped = self.remapped(service_name);
675 let info = Self::service_info(
676 self.domain_id,
677 &self.name,
678 &self.namespace,
679 remapped.as_deref().unwrap_or(service_name),
680 Svc::SERVICE_NAME,
681 Svc::SERVICE_HASH,
682 );
683 let handle = self
684 .session
685 .create_service(&info, qos)
686 .map_err(NodeError::Transport)?;
687 Ok(EmbeddedServiceServer {
688 handle,
689 req_buffer: [0u8; REQ_BUF],
690 reply_buffer: [0u8; REPLY_BUF],
691 _phantom: PhantomData,
692 })
693 }
694
695 pub fn create_client<Svc: RosService>(
697 &mut self,
698 service_name: &str,
699 ) -> Result<EmbeddedServiceClient<Svc>, NodeError>
700 where
701 Svc::Request: MessageForRmw,
702 Svc::Reply: MessageForRmw,
703 {
704 self.create_client_sized::<Svc, { crate::config::DEFAULT_RX_BUF_SIZE }, { crate::config::DEFAULT_RX_BUF_SIZE }>(service_name, QoSProfile::services_default())
705 }
706
707 pub fn create_client_with_qos<Svc: RosService>(
709 &mut self,
710 service_name: &str,
711 qos: QoSProfile,
712 ) -> Result<EmbeddedServiceClient<Svc>, NodeError>
713 where
714 Svc::Request: MessageForRmw,
715 Svc::Reply: MessageForRmw,
716 {
717 self.create_client_sized::<Svc, { crate::config::DEFAULT_RX_BUF_SIZE }, { crate::config::DEFAULT_RX_BUF_SIZE }>(service_name, qos)
718 }
719
720 pub fn create_client_sized<Svc: RosService, const REQ_BUF: usize, const REPLY_BUF: usize>(
722 &mut self,
723 service_name: &str,
724 qos: QoSProfile,
725 ) -> Result<EmbeddedServiceClient<Svc, REQ_BUF, REPLY_BUF>, NodeError>
726 where
727 Svc::Request: MessageForRmw,
728 Svc::Reply: MessageForRmw,
729 {
730 register_type::<Svc::Request>()?;
732 register_type::<Svc::Reply>()?;
733 validate_qos_or_report(
736 &qos,
737 nros_rmw::Session::supported_qos_policies(self.session),
738 "client",
739 service_name,
740 )?;
741 let remapped = self.remapped(service_name);
742 let info = Self::service_info(
743 self.domain_id,
744 &self.name,
745 &self.namespace,
746 remapped.as_deref().unwrap_or(service_name),
747 Svc::SERVICE_NAME,
748 Svc::SERVICE_HASH,
749 );
750 let handle = self
751 .session
752 .create_client(&info, qos)
753 .map_err(|_| NodeError::Transport(TransportError::ServiceClientCreationFailed))?;
754 Ok(EmbeddedServiceClient {
755 handle,
756 req_buffer: [0u8; REQ_BUF],
757 reply_buffer: [0u8; REPLY_BUF],
758 in_flight: false,
759 pending: nros_rmw::RequestCorrelator::new(),
760 _phantom: PhantomData,
761 })
762 }
763
764 pub fn create_service_raw(
769 &mut self,
770 service_name: &str,
771 type_name: &str,
772 type_hash: &str,
773 ) -> Result<crate::executor::handles::RawServiceServer, NodeError> {
774 self.create_service_raw_sized::<
775 { crate::config::DEFAULT_RX_BUF_SIZE },
776 { crate::config::DEFAULT_RX_BUF_SIZE },
777 >(service_name, type_name, type_hash)
778 }
779
780 pub fn create_service_raw_sized<const REQ_BUF: usize, const RESP_BUF: usize>(
782 &mut self,
783 service_name: &str,
784 type_name: &str,
785 type_hash: &str,
786 ) -> Result<crate::executor::handles::RawServiceServer<REQ_BUF, RESP_BUF>, NodeError> {
787 let remapped = self.remapped(service_name);
788 let info = Self::service_info(
789 self.domain_id,
790 &self.name,
791 &self.namespace,
792 remapped.as_deref().unwrap_or(service_name),
793 type_name,
794 type_hash,
795 );
796 let handle = self
797 .session
798 .create_service(&info, QoSProfile::services_default())
799 .map_err(NodeError::Transport)?;
800 Ok(crate::executor::handles::RawServiceServer::new(handle))
801 }
802
803 pub fn create_client_raw(
805 &mut self,
806 service_name: &str,
807 type_name: &str,
808 type_hash: &str,
809 ) -> Result<crate::executor::handles::RawServiceClient, NodeError> {
810 self.create_client_raw_sized::<
811 { crate::config::DEFAULT_RX_BUF_SIZE },
812 { crate::config::DEFAULT_RX_BUF_SIZE },
813 >(service_name, type_name, type_hash)
814 }
815
816 pub fn create_client_raw_sized<const REQ_BUF: usize, const REPLY_BUF: usize>(
818 &mut self,
819 service_name: &str,
820 type_name: &str,
821 type_hash: &str,
822 ) -> Result<crate::executor::handles::RawServiceClient<REQ_BUF, REPLY_BUF>, NodeError> {
823 let remapped = self.remapped(service_name);
824 let info = Self::service_info(
825 self.domain_id,
826 &self.name,
827 &self.namespace,
828 remapped.as_deref().unwrap_or(service_name),
829 type_name,
830 type_hash,
831 );
832 let handle = self
833 .session
834 .create_client(&info, QoSProfile::services_default())
835 .map_err(|_| NodeError::Transport(TransportError::ServiceClientCreationFailed))?;
836 Ok(crate::executor::handles::RawServiceClient::new(handle))
837 }
838
839 pub fn create_action_server_raw(
849 &mut self,
850 action_name: &str,
851 type_name: &str,
852 type_hash: &str,
853 ) -> Result<
854 super::action_core::ActionServerCore<
855 { crate::config::DEFAULT_RX_BUF_SIZE },
856 { crate::config::DEFAULT_RX_BUF_SIZE },
857 { crate::config::DEFAULT_RX_BUF_SIZE },
858 4,
859 >,
860 NodeError,
861 > {
862 self.create_action_server_raw_sized::<
863 { crate::config::DEFAULT_RX_BUF_SIZE },
864 { crate::config::DEFAULT_RX_BUF_SIZE },
865 { crate::config::DEFAULT_RX_BUF_SIZE },
866 4,
867 >(action_name, type_name, type_hash)
868 }
869
870 pub fn create_action_server_raw_sized<
872 const GOAL_BUF: usize,
873 const RESULT_BUF: usize,
874 const FEEDBACK_BUF: usize,
875 const MAX_GOALS: usize,
876 >(
877 &mut self,
878 action_name: &str,
879 type_name: &str,
880 type_hash: &str,
881 ) -> Result<
882 super::action_core::ActionServerCore<GOAL_BUF, RESULT_BUF, FEEDBACK_BUF, MAX_GOALS>,
883 NodeError,
884 > {
885 let remapped = self.remapped(action_name);
886 let action_name = remapped.as_deref().unwrap_or(action_name);
887 let action_info = Self::action_info(self.domain_id, action_name, type_name, type_hash);
888
889 let send_goal_type: heapless::String<256> =
895 super::action_core::action_channel_type(type_name, "SendGoal");
896 let send_goal_keyexpr: heapless::String<256> = action_info.send_goal_key();
897 let send_goal_info = Self::service_info(
898 self.domain_id,
899 &self.name,
900 &self.namespace,
901 &send_goal_keyexpr,
902 &send_goal_type,
903 type_hash,
904 );
905 let send_goal_server = self
906 .session
907 .create_service(&send_goal_info, QoSProfile::services_default())
908 .map_err(|_| NodeError::ActionCreationFailed)?;
909
910 let cancel_goal_keyexpr: heapless::String<256> = action_info.cancel_goal_key();
911 let cancel_goal_info = Self::service_info(
912 self.domain_id,
913 &self.name,
914 &self.namespace,
915 &cancel_goal_keyexpr,
916 "action_msgs::srv::dds_::CancelGoal_",
917 type_hash,
918 );
919 let cancel_goal_server = self
920 .session
921 .create_service(&cancel_goal_info, QoSProfile::services_default())
922 .map_err(|_| NodeError::ActionCreationFailed)?;
923
924 let get_result_type: heapless::String<256> =
925 super::action_core::action_channel_type(type_name, "GetResult");
926 let get_result_keyexpr: heapless::String<256> = action_info.get_result_key();
927 let get_result_info = Self::service_info(
928 self.domain_id,
929 &self.name,
930 &self.namespace,
931 &get_result_keyexpr,
932 &get_result_type,
933 type_hash,
934 );
935 let get_result_server = self
936 .session
937 .create_service(&get_result_info, QoSProfile::services_default())
938 .map_err(|_| NodeError::ActionCreationFailed)?;
939
940 let feedback_type: heapless::String<256> =
941 super::action_core::action_channel_type(type_name, "FeedbackMessage");
942 let feedback_keyexpr: heapless::String<256> = action_info.feedback_key();
943 let feedback_topic = Self::topic_info(
944 self.domain_id,
945 &self.name,
946 &self.namespace,
947 &feedback_keyexpr,
948 &feedback_type,
949 type_hash,
950 );
951 let feedback_publisher = self
952 .session
953 .create_publisher(&feedback_topic, QoSProfile::QOS_PROFILE_DEFAULT)
954 .map_err(|_| NodeError::ActionCreationFailed)?;
955
956 let status_keyexpr: heapless::String<256> = action_info.status_key();
957 let status_topic = Self::topic_info(
958 self.domain_id,
959 &self.name,
960 &self.namespace,
961 &status_keyexpr,
962 "action_msgs::msg::dds_::GoalStatusArray_",
963 type_hash,
964 );
965 let status_publisher = self
966 .session
967 .create_publisher(&status_topic, QoSProfile::QOS_PROFILE_ACTION_STATUS_DEFAULT)
968 .map_err(|_| NodeError::ActionCreationFailed)?;
969
970 Ok(super::action_core::ActionServerCore {
971 send_goal_server,
972 cancel_goal_server,
973 get_result_server,
974 feedback_publisher,
975 status_publisher,
976 active_goals: heapless::Vec::new(),
977 completed_results: heapless::Vec::new(),
978 pending_get_results: heapless::Vec::new(),
979 result_slab: [0u8; RESULT_BUF],
980 result_slab_used: 0,
981 goal_buffer: [0u8; GOAL_BUF],
982 feedback_buffer: [0u8; FEEDBACK_BUF],
983 cancel_buffer: [0u8; 256],
984 })
985 }
986
987 pub fn create_action_client_raw(
991 &mut self,
992 action_name: &str,
993 type_name: &str,
994 type_hash: &str,
995 ) -> Result<
996 super::action_core::ActionClientCore<
997 { crate::config::DEFAULT_RX_BUF_SIZE },
998 { crate::config::DEFAULT_RX_BUF_SIZE },
999 { crate::config::DEFAULT_RX_BUF_SIZE },
1000 >,
1001 NodeError,
1002 > {
1003 self.create_action_client_raw_sized::<
1004 { crate::config::DEFAULT_RX_BUF_SIZE },
1005 { crate::config::DEFAULT_RX_BUF_SIZE },
1006 { crate::config::DEFAULT_RX_BUF_SIZE },
1007 >(action_name, type_name, type_hash)
1008 }
1009
1010 pub fn create_action_client_raw_sized<
1012 const GOAL_BUF: usize,
1013 const RESULT_BUF: usize,
1014 const FEEDBACK_BUF: usize,
1015 >(
1016 &mut self,
1017 action_name: &str,
1018 type_name: &str,
1019 type_hash: &str,
1020 ) -> Result<super::action_core::ActionClientCore<GOAL_BUF, RESULT_BUF, FEEDBACK_BUF>, NodeError>
1021 {
1022 let remapped = self.remapped(action_name);
1023 let action_name = remapped.as_deref().unwrap_or(action_name);
1024 let action_info = Self::action_info(self.domain_id, action_name, type_name, type_hash);
1025
1026 let send_goal_type: heapless::String<256> =
1032 super::action_core::action_channel_type(type_name, "SendGoal");
1033 let send_goal_keyexpr: heapless::String<256> = action_info.send_goal_key();
1034 let send_goal_info = Self::service_info(
1035 self.domain_id,
1036 &self.name,
1037 &self.namespace,
1038 &send_goal_keyexpr,
1039 &send_goal_type,
1040 type_hash,
1041 );
1042 let send_goal_client = self
1043 .session
1044 .create_client(&send_goal_info, QoSProfile::services_default())
1045 .map_err(|_| NodeError::ActionCreationFailed)?;
1046
1047 let cancel_goal_keyexpr: heapless::String<256> = action_info.cancel_goal_key();
1048 let cancel_goal_info = Self::service_info(
1049 self.domain_id,
1050 &self.name,
1051 &self.namespace,
1052 &cancel_goal_keyexpr,
1053 "action_msgs::srv::dds_::CancelGoal_",
1054 type_hash,
1055 );
1056 let cancel_goal_client = self
1057 .session
1058 .create_client(&cancel_goal_info, QoSProfile::services_default())
1059 .map_err(|_| NodeError::ActionCreationFailed)?;
1060
1061 let get_result_type: heapless::String<256> =
1062 super::action_core::action_channel_type(type_name, "GetResult");
1063 let get_result_keyexpr: heapless::String<256> = action_info.get_result_key();
1064 let get_result_info = Self::service_info(
1065 self.domain_id,
1066 &self.name,
1067 &self.namespace,
1068 &get_result_keyexpr,
1069 &get_result_type,
1070 type_hash,
1071 );
1072 let get_result_client = self
1073 .session
1074 .create_client(&get_result_info, QoSProfile::services_default())
1075 .map_err(|_| NodeError::ActionCreationFailed)?;
1076
1077 let feedback_type: heapless::String<256> =
1078 super::action_core::action_channel_type(type_name, "FeedbackMessage");
1079 let feedback_keyexpr: heapless::String<256> = action_info.feedback_key();
1080 let feedback_topic = Self::topic_info(
1081 self.domain_id,
1082 &self.name,
1083 &self.namespace,
1084 &feedback_keyexpr,
1085 &feedback_type,
1086 type_hash,
1087 );
1088 let feedback_subscriber = self
1089 .session
1090 .create_subscription(&feedback_topic, QoSProfile::BEST_EFFORT)
1091 .map_err(|_| NodeError::ActionCreationFailed)?;
1092
1093 Ok(super::action_core::ActionClientCore::new(
1094 send_goal_client,
1095 cancel_goal_client,
1096 get_result_client,
1097 feedback_subscriber,
1098 ))
1099 }
1100
1101 pub fn create_action_server<A: RosAction>(
1103 &mut self,
1104 action_name: &str,
1105 ) -> Result<ActionServer<A>, NodeError>
1106 where
1107 A::Goal: MessageForRmw,
1108 A::Result: MessageForRmw,
1109 A::Feedback: MessageForRmw,
1110 A::SendGoalRequest: MessageForRmw,
1111 A::SendGoalResponse: MessageForRmw,
1112 A::GetResultRequest: MessageForRmw,
1113 A::GetResultResponse: MessageForRmw,
1114 A::FeedbackMessage: MessageForRmw,
1115 {
1116 self.create_action_server_sized::<A, { crate::config::DEFAULT_RX_BUF_SIZE }, { crate::config::DEFAULT_RX_BUF_SIZE }, { crate::config::DEFAULT_RX_BUF_SIZE }, 4>(action_name)
1117 }
1118
1119 pub fn create_action_server_sized<
1121 A: RosAction,
1122 const GOAL_BUF: usize,
1123 const RESULT_BUF: usize,
1124 const FEEDBACK_BUF: usize,
1125 const MAX_GOALS: usize,
1126 >(
1127 &mut self,
1128 action_name: &str,
1129 ) -> Result<ActionServer<A, GOAL_BUF, RESULT_BUF, FEEDBACK_BUF, MAX_GOALS>, NodeError>
1130 where
1131 A::Goal: MessageForRmw,
1132 A::Result: MessageForRmw,
1133 A::Feedback: MessageForRmw,
1134 A::SendGoalRequest: MessageForRmw,
1135 A::SendGoalResponse: MessageForRmw,
1136 A::GetResultRequest: MessageForRmw,
1137 A::GetResultResponse: MessageForRmw,
1138 A::FeedbackMessage: MessageForRmw,
1139 {
1140 register_type::<A::Goal>()?;
1148 register_type::<A::Result>()?;
1149 register_type::<A::Feedback>()?;
1150 register_type::<A::SendGoalRequest>()?;
1151 register_type::<A::SendGoalResponse>()?;
1152 register_type::<A::GetResultRequest>()?;
1153 register_type::<A::GetResultResponse>()?;
1154 register_type::<A::FeedbackMessage>()?;
1155 A::register_protocol_types().map_err(|()| NodeError::ActionCreationFailed)?;
1166 let remapped = self.remapped(action_name);
1167 let action_name = remapped.as_deref().unwrap_or(action_name);
1168 let action_info =
1169 Self::action_info(self.domain_id, action_name, A::ACTION_NAME, A::ACTION_HASH);
1170
1171 let send_goal_type = super::action_core::action_service_base_type(
1180 <A::SendGoalRequest as RosMessage>::TYPE_NAME,
1181 A::ACTION_NAME,
1182 );
1183 let get_result_type = super::action_core::action_service_base_type(
1184 <A::GetResultRequest as RosMessage>::TYPE_NAME,
1185 A::ACTION_NAME,
1186 );
1187 let feedback_type = <A::FeedbackMessage as RosMessage>::TYPE_NAME;
1188
1189 let send_goal_keyexpr: heapless::String<256> = action_info.send_goal_key();
1190 let send_goal_info = Self::service_info(
1191 self.domain_id,
1192 &self.name,
1193 &self.namespace,
1194 &send_goal_keyexpr,
1195 send_goal_type,
1196 A::SEND_GOAL_SERVICE_HASH,
1197 );
1198 let send_goal_server = self
1199 .session
1200 .create_service(&send_goal_info, QoSProfile::services_default())
1201 .map_err(|_| NodeError::ActionCreationFailed)?;
1202
1203 let cancel_goal_keyexpr: heapless::String<256> = action_info.cancel_goal_key();
1204 let cancel_goal_info = Self::service_info(
1205 self.domain_id,
1206 &self.name,
1207 &self.namespace,
1208 &cancel_goal_keyexpr,
1209 "action_msgs::srv::dds_::CancelGoal_",
1210 A::CANCEL_GOAL_SERVICE_HASH,
1211 );
1212 let cancel_goal_server = self
1213 .session
1214 .create_service(&cancel_goal_info, QoSProfile::services_default())
1215 .map_err(|_| NodeError::ActionCreationFailed)?;
1216
1217 let get_result_keyexpr: heapless::String<256> = action_info.get_result_key();
1218 let get_result_info = Self::service_info(
1219 self.domain_id,
1220 &self.name,
1221 &self.namespace,
1222 &get_result_keyexpr,
1223 get_result_type,
1224 A::GET_RESULT_SERVICE_HASH,
1225 );
1226 let get_result_server = self
1227 .session
1228 .create_service(&get_result_info, QoSProfile::services_default())
1229 .map_err(|_| NodeError::ActionCreationFailed)?;
1230
1231 let feedback_keyexpr: heapless::String<256> = action_info.feedback_key();
1232 let feedback_topic = Self::topic_info(
1233 self.domain_id,
1234 &self.name,
1235 &self.namespace,
1236 &feedback_keyexpr,
1237 feedback_type,
1238 <A::FeedbackMessage as RosMessage>::TYPE_HASH,
1239 );
1240 let feedback_publisher = self
1241 .session
1242 .create_publisher(&feedback_topic, QoSProfile::QOS_PROFILE_DEFAULT)
1243 .map_err(|_| NodeError::ActionCreationFailed)?;
1244
1245 let status_keyexpr: heapless::String<256> = action_info.status_key();
1246 let status_topic = Self::topic_info(
1247 self.domain_id,
1248 &self.name,
1249 &self.namespace,
1250 &status_keyexpr,
1251 "action_msgs::msg::dds_::GoalStatusArray_",
1252 A::STATUS_MESSAGE_HASH,
1253 );
1254 let status_publisher = self
1255 .session
1256 .create_publisher(&status_topic, QoSProfile::QOS_PROFILE_ACTION_STATUS_DEFAULT)
1257 .map_err(|_| NodeError::ActionCreationFailed)?;
1258
1259 Ok(ActionServer {
1260 core: super::action_core::ActionServerCore {
1261 send_goal_server,
1262 cancel_goal_server,
1263 get_result_server,
1264 feedback_publisher,
1265 status_publisher,
1266 active_goals: heapless::Vec::new(),
1267 completed_results: heapless::Vec::new(),
1268 pending_get_results: heapless::Vec::new(),
1269 result_slab: [0u8; RESULT_BUF],
1270 result_slab_used: 0,
1271 goal_buffer: [0u8; GOAL_BUF],
1272 feedback_buffer: [0u8; FEEDBACK_BUF],
1273 cancel_buffer: [0u8; 256],
1274 },
1275 typed_goals: heapless::Vec::new(),
1276 completed_goals: heapless::Vec::new(),
1277 })
1278 }
1279
1280 pub fn create_action_client<A: RosAction>(
1282 &mut self,
1283 action_name: &str,
1284 ) -> Result<ActionClient<A>, NodeError>
1285 where
1286 A::Goal: MessageForRmw,
1287 A::Result: MessageForRmw,
1288 A::Feedback: MessageForRmw,
1289 A::SendGoalRequest: MessageForRmw,
1290 A::SendGoalResponse: MessageForRmw,
1291 A::GetResultRequest: MessageForRmw,
1292 A::GetResultResponse: MessageForRmw,
1293 A::FeedbackMessage: MessageForRmw,
1294 {
1295 self.create_action_client_sized::<A, { crate::config::DEFAULT_RX_BUF_SIZE }, { crate::config::DEFAULT_RX_BUF_SIZE }, { crate::config::DEFAULT_RX_BUF_SIZE }>(action_name)
1296 }
1297
1298 pub fn create_action_client_sized<
1300 A: RosAction,
1301 const GOAL_BUF: usize,
1302 const RESULT_BUF: usize,
1303 const FEEDBACK_BUF: usize,
1304 >(
1305 &mut self,
1306 action_name: &str,
1307 ) -> Result<ActionClient<A, GOAL_BUF, RESULT_BUF, FEEDBACK_BUF>, NodeError>
1308 where
1309 A::Goal: MessageForRmw,
1310 A::Result: MessageForRmw,
1311 A::Feedback: MessageForRmw,
1312 A::SendGoalRequest: MessageForRmw,
1313 A::SendGoalResponse: MessageForRmw,
1314 A::GetResultRequest: MessageForRmw,
1315 A::GetResultResponse: MessageForRmw,
1316 A::FeedbackMessage: MessageForRmw,
1317 {
1318 register_type::<A::Goal>()?;
1320 register_type::<A::Result>()?;
1321 register_type::<A::Feedback>()?;
1322 register_type::<A::SendGoalRequest>()?;
1323 register_type::<A::SendGoalResponse>()?;
1324 register_type::<A::GetResultRequest>()?;
1325 register_type::<A::GetResultResponse>()?;
1326 register_type::<A::FeedbackMessage>()?;
1327 A::register_protocol_types().map_err(|()| NodeError::ActionCreationFailed)?;
1333 let remapped = self.remapped(action_name);
1334 let action_name = remapped.as_deref().unwrap_or(action_name);
1335 let action_info =
1336 Self::action_info(self.domain_id, action_name, A::ACTION_NAME, A::ACTION_HASH);
1337
1338 let send_goal_type = super::action_core::action_service_base_type(
1346 <A::SendGoalRequest as RosMessage>::TYPE_NAME,
1347 A::ACTION_NAME,
1348 );
1349 let get_result_type = super::action_core::action_service_base_type(
1350 <A::GetResultRequest as RosMessage>::TYPE_NAME,
1351 A::ACTION_NAME,
1352 );
1353 let feedback_type = <A::FeedbackMessage as RosMessage>::TYPE_NAME;
1354
1355 let send_goal_keyexpr: heapless::String<256> = action_info.send_goal_key();
1356 let send_goal_info = Self::service_info(
1357 self.domain_id,
1358 &self.name,
1359 &self.namespace,
1360 &send_goal_keyexpr,
1361 send_goal_type,
1362 A::SEND_GOAL_SERVICE_HASH,
1363 );
1364 let send_goal_client = self
1365 .session
1366 .create_client(&send_goal_info, QoSProfile::services_default())
1367 .map_err(|_| NodeError::ActionCreationFailed)?;
1368
1369 let cancel_goal_keyexpr: heapless::String<256> = action_info.cancel_goal_key();
1370 let cancel_goal_info = Self::service_info(
1371 self.domain_id,
1372 &self.name,
1373 &self.namespace,
1374 &cancel_goal_keyexpr,
1375 "action_msgs::srv::dds_::CancelGoal_",
1376 A::CANCEL_GOAL_SERVICE_HASH,
1377 );
1378 let cancel_goal_client = self
1379 .session
1380 .create_client(&cancel_goal_info, QoSProfile::services_default())
1381 .map_err(|_| NodeError::ActionCreationFailed)?;
1382
1383 let get_result_keyexpr: heapless::String<256> = action_info.get_result_key();
1384 let get_result_info = Self::service_info(
1385 self.domain_id,
1386 &self.name,
1387 &self.namespace,
1388 &get_result_keyexpr,
1389 get_result_type,
1390 A::GET_RESULT_SERVICE_HASH,
1391 );
1392 let get_result_client = self
1393 .session
1394 .create_client(&get_result_info, QoSProfile::services_default())
1395 .map_err(|_| NodeError::ActionCreationFailed)?;
1396
1397 let feedback_keyexpr: heapless::String<256> = action_info.feedback_key();
1398 let feedback_topic = Self::topic_info(
1399 self.domain_id,
1400 &self.name,
1401 &self.namespace,
1402 &feedback_keyexpr,
1403 feedback_type,
1404 <A::FeedbackMessage as RosMessage>::TYPE_HASH,
1405 );
1406 let feedback_subscriber = self
1407 .session
1408 .create_subscription(&feedback_topic, QoSProfile::BEST_EFFORT)
1409 .map_err(|_| NodeError::ActionCreationFailed)?;
1410
1411 Ok(ActionClient {
1412 core: super::action_core::ActionClientCore {
1413 send_goal_client,
1414 cancel_goal_client,
1415 get_result_client,
1416 feedback_subscriber,
1417 goal_buffer: [0u8; GOAL_BUF],
1418 result_buffer: [0u8; RESULT_BUF],
1419 feedback_buffer: [0u8; FEEDBACK_BUF],
1420 goal_counter: 0,
1421 in_flight_send_goal: false,
1422 in_flight_cancel: false,
1423 in_flight_get_result: false,
1424 pending_send_goal: super::action_core::ActionRequests::new(),
1425 pending_cancel: super::action_core::ActionRequests::new(),
1426 pending_get_result: super::action_core::ActionRequests::new(),
1427 },
1428 _phantom: PhantomData,
1429 })
1430 }
1431}
1432
1433pub struct PublisherBuilder<'n, 'a, 't> {
1440 node: &'n mut NodeHandle<'a>,
1441 topic: &'t str,
1442 qos: QoSProfile,
1443}
1444
1445impl<'n, 'a, 't> PublisherBuilder<'n, 'a, 't> {
1446 pub fn qos(mut self, qos: QoSProfile) -> Self {
1448 self.qos = qos;
1449 self
1450 }
1451
1452 pub fn tx_express(mut self, express: bool) -> Self {
1456 self.qos.tx_express = express;
1457 self
1458 }
1459
1460 pub fn typed<M: MessageForRmw>(self) -> TypedPublisherBuilder<'n, 'a, 't, M> {
1462 TypedPublisherBuilder {
1463 node: self.node,
1464 topic: self.topic,
1465 qos: self.qos,
1466 _phantom: PhantomData,
1467 }
1468 }
1469
1470 pub fn generic(
1473 self,
1474 type_name: &'t str,
1475 type_hash: &'t str,
1476 ) -> GenericPublisherBuilder<'n, 'a, 't> {
1477 GenericPublisherBuilder {
1478 node: self.node,
1479 topic: self.topic,
1480 type_name,
1481 type_hash,
1482 qos: self.qos,
1483 }
1484 }
1485}
1486
1487pub struct TypedPublisherBuilder<'n, 'a, 't, M> {
1489 node: &'n mut NodeHandle<'a>,
1490 topic: &'t str,
1491 qos: QoSProfile,
1492 _phantom: PhantomData<M>,
1493}
1494
1495impl<'n, 'a, 't, M: MessageForRmw> TypedPublisherBuilder<'n, 'a, 't, M> {
1496 pub fn qos(mut self, qos: QoSProfile) -> Self {
1497 self.qos = qos;
1498 self
1499 }
1500
1501 pub fn tx_express(mut self, express: bool) -> Self {
1503 self.qos.tx_express = express;
1504 self
1505 }
1506
1507 pub fn build(self) -> Result<EmbeddedPublisher<M>, NodeError> {
1508 self.node
1509 .create_publisher_with_qos::<M>(self.topic, self.qos)
1510 }
1511}
1512
1513pub struct GenericPublisherBuilder<'n, 'a, 't> {
1515 node: &'n mut NodeHandle<'a>,
1516 topic: &'t str,
1517 type_name: &'t str,
1518 type_hash: &'t str,
1519 qos: QoSProfile,
1520}
1521
1522impl<'n, 'a, 't> GenericPublisherBuilder<'n, 'a, 't> {
1523 pub fn qos(mut self, qos: QoSProfile) -> Self {
1524 self.qos = qos;
1525 self
1526 }
1527
1528 pub fn tx_express(mut self, express: bool) -> Self {
1530 self.qos.tx_express = express;
1531 self
1532 }
1533
1534 pub fn build(self) -> Result<crate::executor::handles::EmbeddedRawPublisher, NodeError> {
1535 self.node.create_publisher_raw_with_qos(
1536 self.topic,
1537 self.type_name,
1538 self.type_hash,
1539 self.qos,
1540 )
1541 }
1542}
1543
1544pub struct CallbackGroup {
1560 name: heapless::String<32>,
1561}
1562
1563impl CallbackGroup {
1564 pub fn name(&self) -> &str {
1566 &self.name
1567 }
1568}
1569
1570pub struct Node<'e, 's> {
1585 executor: &'e mut super::spin::Executor<'s>,
1586 node_id: super::node_record::NodeId,
1587}
1588
1589#[cfg(feature = "param-store")]
1601impl<'e, 's> Node<'e, 's> {
1602 pub fn declare_parameter<'a, T: nros_params::ParameterVariant>(
1611 &'a mut self,
1612 name: &'a str,
1613 ) -> Result<nros_params::ParameterBuilder<'a, 's, T>, NodeError> {
1614 self.executor.parameter_on(self.node_id, name)
1615 }
1616
1617 pub fn use_undeclared_parameters(
1620 &mut self,
1621 ) -> Result<nros_params::Parameters<'_, 's>, NodeError> {
1622 self.executor.use_undeclared_parameters_on(self.node_id)
1623 }
1624
1625 pub fn declare_parameter_with_descriptor(
1629 &mut self,
1630 name: &str,
1631 value: nros_params::ParameterValue,
1632 descriptor: nros_params::ParameterDescriptor,
1633 ) -> bool {
1634 self.executor
1635 .declare_parameter_with_descriptor_on(self.node_id, name, value, descriptor)
1636 }
1637
1638 pub fn get_parameter(&self, name: &str) -> Option<&nros_params::ParameterValue> {
1640 self.executor.get_parameter_on(self.node_id, name)
1641 }
1642
1643 pub fn set_parameter(
1645 &mut self,
1646 name: &str,
1647 value: nros_params::ParameterValue,
1648 ) -> Result<(), nros_params::ParameterError> {
1649 self.executor.set_parameter_on(self.node_id, name, value)
1650 }
1651
1652 pub fn set_parameters_atomically<'x, I>(
1655 &mut self,
1656 items: I,
1657 ) -> Result<(), nros_params::ParameterError>
1658 where
1659 I: Iterator<Item = Option<(&'x str, nros_params::ParameterValue)>> + Clone,
1660 {
1661 self.executor
1662 .set_parameters_atomically_on(self.node_id, items)
1663 }
1664
1665 pub fn undeclare_parameter(&mut self, name: &str) -> bool {
1667 self.executor.undeclare_parameter_on(self.node_id, name)
1668 }
1669
1670 pub fn allow_undeclared_parameters(&mut self, allow: bool) {
1672 self.executor
1673 .allow_undeclared_parameters_on(self.node_id, allow)
1674 }
1675
1676 pub fn describe_parameter(&self, name: &str) -> Option<&nros_params::ParameterDescriptor> {
1678 self.executor.describe_parameter_on(self.node_id, name)
1679 }
1680
1681 pub fn get_parameter_type(&self, name: &str) -> Option<nros_params::ParameterType> {
1683 self.executor.get_parameter_type_on(self.node_id, name)
1684 }
1685
1686 pub fn list_parameters<'p>(&'p self, prefix: &'p str) -> impl Iterator<Item = &'p str> {
1689 self.executor.list_parameters_on(self.node_id, prefix)
1690 }
1691
1692 pub fn set_parameter_description(
1694 &mut self,
1695 name: &str,
1696 description: &str,
1697 additional_constraints: &str,
1698 ) -> bool {
1699 self.executor.set_parameter_description_on(
1700 self.node_id,
1701 name,
1702 description,
1703 additional_constraints,
1704 )
1705 }
1706
1707 pub fn set_parameter_read_only(&mut self, name: &str, read_only: bool) -> bool {
1709 self.executor
1710 .set_parameter_read_only_on(self.node_id, name, read_only)
1711 }
1712
1713 pub fn set_parameter_integer_range(
1715 &mut self,
1716 name: &str,
1717 min: i64,
1718 max: i64,
1719 step: i64,
1720 ) -> bool {
1721 self.executor
1722 .set_parameter_integer_range_on(self.node_id, name, min, max, step)
1723 }
1724
1725 pub fn set_parameter_float_range(&mut self, name: &str, min: f64, max: f64, step: f64) -> bool {
1727 self.executor
1728 .set_parameter_float_range_on(self.node_id, name, min, max, step)
1729 }
1730
1731 pub fn add_on_set_parameters_callback(
1734 &mut self,
1735 callback: nros_params::OnSetParameterFn,
1736 ctx: nros_params::OnSetContext,
1737 ) -> Option<nros_params::OnSetParameterHandle> {
1738 self.executor
1739 .add_on_set_parameters_callback_on(self.node_id, callback, ctx)
1740 }
1741
1742 pub fn remove_on_set_parameters_callback(
1744 &mut self,
1745 handle: nros_params::OnSetParameterHandle,
1746 ) -> bool {
1747 self.executor.remove_on_set_parameters_callback(handle)
1748 }
1749
1750 #[cfg(feature = "alloc")]
1758 pub fn argv_param_overrides(
1759 &self,
1760 ) -> impl Iterator<Item = (&str, nros_params::ParameterValue)> + '_ {
1761 self.executor.argv_param_overrides_on(self.node_id)
1762 }
1763
1764 #[cfg(feature = "alloc")]
1767 pub fn for_each_argv_param_override(
1768 &self,
1769 f: impl FnMut(&str, &nros_params::ParameterValue),
1770 ) -> usize {
1771 self.executor
1772 .for_each_argv_param_override_on(self.node_id, f)
1773 }
1774
1775 pub fn set_parameter_local(
1778 &mut self,
1779 name: &str,
1780 value: nros_params::ParameterValue,
1781 ) -> Result<(), nros_params::ParameterError> {
1782 self.executor
1783 .set_parameter_local_on(self.node_id, name, value)
1784 }
1785
1786 #[cfg(feature = "param-services")]
1789 pub fn disable_parameter_events(&mut self) {
1790 self.executor.disable_parameter_events_on(self.node_id)
1791 }
1792
1793 #[cfg(feature = "param-services")]
1795 pub fn enable_parameter_events(&mut self) -> Result<(), NodeError> {
1796 self.executor.enable_parameter_events_on(self.node_id)
1797 }
1798
1799 pub fn delete_parameters_on_not_set(&mut self, on: bool) {
1802 self.executor
1803 .delete_parameters_on_not_set_on(self.node_id, on)
1804 }
1805
1806 pub fn set_parameters_atomically_with<'x>(
1809 &mut self,
1810 count: usize,
1811 fill: impl FnMut(usize, &mut nros_params::ParameterValue) -> Option<&'x str>,
1812 ) -> Result<(), nros_params::ParameterError> {
1813 self.executor
1814 .set_parameters_atomically_with_on(self.node_id, count, fill)
1815 }
1816}
1817
1818impl<'e, 's> Node<'e, 's> {
1819 pub(crate) fn new(
1820 executor: &'e mut super::spin::Executor<'s>,
1821 node_id: super::node_record::NodeId,
1822 ) -> Self {
1823 Self { executor, node_id }
1824 }
1825
1826 #[must_use]
1831 pub fn name(&self) -> &str {
1832 self.executor
1833 .node(self.node_id)
1834 .map(|r| r.name.as_str())
1835 .unwrap_or("")
1836 }
1837
1838 #[must_use]
1840 pub fn namespace(&self) -> &str {
1841 self.executor
1842 .node(self.node_id)
1843 .map(|r| r.namespace.as_str())
1844 .unwrap_or("")
1845 }
1846
1847 pub fn fully_qualified_name(&self) -> Result<crate::names::ResolvedName, NodeError> {
1852 crate::names::fully_qualified_name(self.name(), self.namespace())
1853 .map_err(|()| NodeError::NameTooLong)
1854 }
1855
1856 #[must_use]
1859 pub fn domain_id(&self) -> u32 {
1860 self.executor.domain_id
1861 }
1862
1863 #[must_use]
1866 pub fn logger(&self) -> &'static nros_log::Logger {
1867 nros_log::resolve_node_logger(self.name())
1868 }
1869
1870 #[doc(hidden)]
1873 pub fn __executor(&mut self) -> &mut super::spin::Executor<'s> {
1874 self.executor
1875 }
1876
1877 #[must_use]
1881 pub fn id(&self) -> super::node_record::NodeId {
1882 self.node_id
1883 }
1884
1885 pub fn get_clock(&self) -> nros_core::clock::Clock {
1910 nros_core::clock::Clock::ros_time()
1911 }
1912
1913 pub fn subscription<'t>(&mut self, topic: &'t str) -> SubscriptionBuilder<'_, 'e, 't, 's> {
1916 SubscriptionBuilder {
1917 ctx: self,
1918 topic,
1919 qos: QoSProfile::default(),
1920 }
1921 }
1922
1923 pub fn publisher<'t>(&mut self, topic: &'t str) -> CtxPublisherBuilder<'_, 'e, 't, 's> {
1930 CtxPublisherBuilder {
1931 ctx: self,
1932 topic,
1933 qos: QoSProfile::default(),
1934 }
1935 }
1936
1937 pub fn create_publisher<'o, M: MessageForRmw>(
1943 &mut self,
1944 options: impl crate::IntoPrimitiveOptions<'o>,
1945 ) -> Result<EmbeddedPublisher<M>, NodeError> {
1946 let options = options.into_primitive_options();
1947 self.create_publisher_with_qos::<M>(options.name, options.resolve(QoSProfile::default()))
1948 }
1949
1950 pub fn create_generic_publisher(
1952 &mut self,
1953 topic: &str,
1954 type_name: &str,
1955 type_hash: &str,
1956 ) -> Result<crate::executor::handles::EmbeddedRawPublisher, NodeError> {
1957 self.create_generic_publisher_with_qos(topic, type_name, type_hash, QoSProfile::default())
1958 }
1959
1960 pub fn create_generic_publisher_with_qos(
1966 &mut self,
1967 topic: &str,
1968 type_name: &str,
1969 type_hash: &str,
1970 qos: QoSProfile,
1971 ) -> Result<crate::executor::handles::EmbeddedRawPublisher, NodeError> {
1972 self.executor
1973 .create_publisher_raw_on(self.node_id, topic, type_name, type_hash, qos)
1974 }
1975
1976 pub fn create_subscription<'o, M, F>(
1984 &mut self,
1985 options: impl crate::IntoPrimitiveOptions<'o>,
1986 callback: F,
1987 ) -> Result<super::types::HandleId, NodeError>
1988 where
1989 M: MessageForRmw + 'static,
1990 F: FnMut(&M) + 'static,
1991 {
1992 let options = options.into_primitive_options();
1993 self.executor
1994 .register_subscription_buffered_on::<M, F, { crate::config::DEFAULT_RX_BUF_SIZE }>(
1995 self.node_id,
1996 options.name,
1997 options.resolve(QoSProfile::default()),
1998 callback,
1999 None, None, )
2002 }
2003
2004 #[cfg(all(feature = "sim-time", any(has_rmw, test)))]
2026 pub fn install_ros_time_source(&mut self) -> Result<super::types::HandleId, NodeError> {
2027 self.install_ros_time_source_on(crate::time_source::CLOCK_TOPIC)
2028 }
2029
2030 #[cfg(all(feature = "sim-time", any(has_rmw, test)))]
2034 pub fn install_ros_time_source_on(
2035 &mut self,
2036 topic: &str,
2037 ) -> Result<super::types::HandleId, NodeError> {
2038 self.executor.install_ros_time_source(self.node_id, topic)
2042 }
2043
2044 pub fn create_callback_group(&self, name: &str) -> CallbackGroup {
2063 let mut s = heapless::String::<32>::new();
2064 for ch in name.chars() {
2067 if s.push(ch).is_err() {
2068 break;
2069 }
2070 }
2071 CallbackGroup { name: s }
2072 }
2073
2074 pub fn create_timer_in_group<F>(
2081 &mut self,
2082 group: &CallbackGroup,
2083 period: crate::timer::TimerDuration,
2084 callback: F,
2085 ) -> Result<super::types::HandleId, NodeError>
2086 where
2087 F: FnMut() + 'static,
2088 {
2089 self.executor
2090 .register_timer_on(Some(self.node_id), period, callback, Some(group.name()))
2091 }
2092
2093 pub fn create_timer_repeating<F>(
2107 &mut self,
2108 period: impl Into<crate::timer::TimerDuration>,
2109 callback: F,
2110 ) -> Result<super::types::HandleId, NodeError>
2111 where
2112 F: FnMut() + 'static,
2113 {
2114 self.executor.register_timer_entry(
2115 Some(self.node_id),
2116 period.into(),
2117 super::TimerClockSource::Steady,
2118 false,
2119 None,
2120 callback,
2121 )
2122 }
2123
2124 pub fn create_timer_oneshot<F>(
2130 &mut self,
2131 period: impl Into<crate::timer::TimerDuration>,
2132 callback: F,
2133 ) -> Result<super::types::HandleId, NodeError>
2134 where
2135 F: FnMut() + 'static,
2136 {
2137 self.executor.register_timer_entry(
2138 Some(self.node_id),
2139 period.into(),
2140 super::TimerClockSource::Steady,
2141 true,
2142 None,
2143 callback,
2144 )
2145 }
2146
2147 pub fn create_timer_inert<F>(
2157 &mut self,
2158 period: impl Into<crate::timer::TimerDuration>,
2159 callback: F,
2160 ) -> Result<super::types::HandleId, NodeError>
2161 where
2162 F: FnMut() + 'static,
2163 {
2164 let mut callback = callback;
2165 self.executor.register_timer_entry_with(
2166 Some(self.node_id),
2167 period.into(),
2168 super::TimerClockSource::Steady,
2169 false,
2170 false,
2171 None,
2172 None,
2173 core::ptr::null_mut(),
2174 move |_| move |_: &crate::timer::TimerCallInfo| callback(),
2175 )
2176 }
2177
2178 pub fn create_guard_condition<F>(
2206 &mut self,
2207 callback: F,
2208 ) -> Result<(super::types::HandleId, super::types::GuardCondition), NodeError>
2209 where
2210 F: FnMut() + 'static,
2211 {
2212 self.executor
2213 .register_guard_condition_on(Some(self.node_id), callback)
2214 }
2215
2216 pub fn create_timer_on_clock<F>(
2241 &mut self,
2242 period: crate::timer::TimerDuration,
2243 source: crate::executor::TimerClockSource,
2244 callback: F,
2245 ) -> Result<super::types::HandleId, NodeError>
2246 where
2247 F: FnMut() + 'static,
2248 {
2249 self.executor.register_timer_entry(
2250 Some(self.node_id),
2251 period,
2252 source,
2253 false,
2254 None,
2255 callback,
2256 )
2257 }
2258
2259 pub fn create_timer_on_clock_in_group<F>(
2265 &mut self,
2266 group: &CallbackGroup,
2267 period: crate::timer::TimerDuration,
2268 source: crate::executor::TimerClockSource,
2269 callback: F,
2270 ) -> Result<super::types::HandleId, NodeError>
2271 where
2272 F: FnMut() + 'static,
2273 {
2274 self.executor.register_timer_entry(
2275 Some(self.node_id),
2276 period,
2277 source,
2278 false,
2279 Some(group.name()),
2280 callback,
2281 )
2282 }
2283
2284 pub fn create_timer_with_info<F>(
2304 &mut self,
2305 period: crate::timer::TimerDuration,
2306 source: crate::executor::TimerClockSource,
2307 autostart: bool,
2308 callback: F,
2309 ) -> Result<super::types::HandleId, NodeError>
2310 where
2311 F: FnMut(&crate::TimerCallInfo) + 'static,
2312 {
2313 self.executor.register_timer_with_info_on(
2314 Some(self.node_id),
2315 period,
2316 source,
2317 autostart,
2318 None,
2319 callback,
2320 )
2321 }
2322
2323 pub fn create_subscription_in_group<M, F>(
2329 &mut self,
2330 group: &CallbackGroup,
2331 topic: &str,
2332 callback: F,
2333 ) -> Result<super::types::HandleId, NodeError>
2334 where
2335 M: MessageForRmw + 'static,
2336 F: FnMut(&M) + 'static,
2337 {
2338 self.executor
2339 .register_subscription_buffered_on::<M, F, { crate::config::DEFAULT_RX_BUF_SIZE }>(
2340 self.node_id,
2341 topic,
2342 QoSProfile::default(),
2343 callback,
2344 Some(group.name()),
2345 None, )
2347 }
2348
2349 pub fn create_publisher_in_group<M: MessageForRmw>(
2357 &mut self,
2358 _group: &CallbackGroup,
2359 topic: &str,
2360 ) -> Result<EmbeddedPublisher<M>, NodeError> {
2361 self.create_publisher::<M>(topic)
2363 }
2364
2365 pub fn create_client_with_callback<Svc, F>(
2371 &mut self,
2372 service_name: &str,
2373 callback: F,
2374 ) -> Result<ServiceClientCallback<Svc>, NodeError>
2375 where
2376 Svc: RosService + 'static,
2377 Svc::Request: MessageForRmw,
2378 Svc::Reply: MessageForRmw,
2379 F: FnMut(&Svc::Reply) + 'static,
2380 {
2381 self.create_client_with_callback_sized::<
2382 Svc,
2383 F,
2384 { crate::config::DEFAULT_RX_BUF_SIZE },
2385 { crate::config::DEFAULT_RX_BUF_SIZE },
2386 >(service_name, callback)
2387 }
2388
2389 pub fn create_client_with_callback_sized<Svc, F, const REQ_BUF: usize, const REPLY_BUF: usize>(
2391 &mut self,
2392 service_name: &str,
2393 callback: F,
2394 ) -> Result<ServiceClientCallback<Svc, REQ_BUF, REPLY_BUF>, NodeError>
2395 where
2396 Svc: RosService + 'static,
2397 Svc::Request: MessageForRmw,
2398 Svc::Reply: MessageForRmw,
2399 F: FnMut(&Svc::Reply) + 'static,
2400 {
2401 self.register_client_callback::<Svc, _, REQ_BUF, REPLY_BUF>(
2402 service_name,
2403 super::arena::ReplyOnly(callback),
2404 )
2405 }
2406
2407 pub fn create_client_with_callback_with_info<Svc, F>(
2422 &mut self,
2423 service_name: &str,
2424 callback: F,
2425 ) -> Result<ServiceClientCallback<Svc>, NodeError>
2426 where
2427 Svc: RosService + 'static,
2428 Svc::Request: MessageForRmw,
2429 Svc::Reply: MessageForRmw,
2430 F: FnMut(&Svc::Reply, &nros_core::ServiceInfo) + 'static,
2431 {
2432 self.register_client_callback::<
2433 Svc,
2434 _,
2435 { crate::config::DEFAULT_RX_BUF_SIZE },
2436 { crate::config::DEFAULT_RX_BUF_SIZE },
2437 >(service_name, super::arena::ReplyWithInfo(callback))
2438 }
2439
2440 fn register_client_callback<Svc, C, const REQ_BUF: usize, const REPLY_BUF: usize>(
2442 &mut self,
2443 service_name: &str,
2444 callback: C,
2445 ) -> Result<ServiceClientCallback<Svc, REQ_BUF, REPLY_BUF>, NodeError>
2446 where
2447 Svc: RosService + 'static,
2448 Svc::Request: MessageForRmw,
2449 Svc::Reply: MessageForRmw,
2450 C: super::arena::ClientReplyCallback<Svc::Reply> + 'static,
2451 {
2452 register_type::<Svc::Request>()?;
2453 register_type::<Svc::Reply>()?;
2454 let (_id, hdr) = self
2455 .executor
2456 .register_service_client_callback::<Svc, C, REPLY_BUF>(
2457 Some(self.node_id),
2458 service_name,
2459 Svc::SERVICE_NAME,
2460 Svc::SERVICE_HASH,
2461 QoSProfile::services_default(),
2462 callback,
2463 )?;
2464 Ok(ServiceClientCallback::new(hdr))
2465 }
2466
2467 #[allow(clippy::type_complexity)]
2475 pub fn create_action_client_with_callbacks<A, GRespF, FbF, ResF>(
2476 &mut self,
2477 action_name: &str,
2478 on_goal_response: GRespF,
2479 on_feedback: FbF,
2480 on_result: ResF,
2481 ) -> Result<ActionClientCallback<A>, NodeError>
2482 where
2483 A: RosAction + 'static,
2484 A::Goal: MessageForRmw,
2485 A::Result: MessageForRmw,
2486 A::Feedback: MessageForRmw,
2487 GRespF: FnMut(&nros_core::GoalUuid, bool) + 'static,
2488 FbF: FnMut(&nros_core::GoalUuid, &A::Feedback) + 'static,
2489 ResF: FnMut(&nros_core::GoalUuid, nros_core::GoalStatusCode, &A::Result) + 'static,
2490 {
2491 self.create_action_client_with_callbacks_sized::<
2492 A,
2493 GRespF,
2494 FbF,
2495 ResF,
2496 { crate::config::DEFAULT_RX_BUF_SIZE },
2497 { crate::config::DEFAULT_RX_BUF_SIZE },
2498 { crate::config::DEFAULT_RX_BUF_SIZE },
2499 >(action_name, on_goal_response, on_feedback, on_result)
2500 }
2501
2502 #[allow(clippy::type_complexity)]
2504 pub fn create_action_client_with_callbacks_sized<
2505 A,
2506 GRespF,
2507 FbF,
2508 ResF,
2509 const GOAL_BUF: usize,
2510 const RESULT_BUF: usize,
2511 const FEEDBACK_BUF: usize,
2512 >(
2513 &mut self,
2514 action_name: &str,
2515 on_goal_response: GRespF,
2516 on_feedback: FbF,
2517 on_result: ResF,
2518 ) -> Result<ActionClientCallback<A, GOAL_BUF, RESULT_BUF, FEEDBACK_BUF>, NodeError>
2519 where
2520 A: RosAction + 'static,
2521 A::Goal: MessageForRmw,
2522 A::Result: MessageForRmw,
2523 A::Feedback: MessageForRmw,
2524 GRespF: FnMut(&nros_core::GoalUuid, bool) + 'static,
2525 FbF: FnMut(&nros_core::GoalUuid, &A::Feedback) + 'static,
2526 ResF: FnMut(&nros_core::GoalUuid, nros_core::GoalStatusCode, &A::Result) + 'static,
2527 {
2528 register_type::<A::Goal>()?;
2529 register_type::<A::Result>()?;
2530 register_type::<A::Feedback>()?;
2531 let (_id, core) = self
2532 .executor
2533 .register_action_client_callback::<A, GRespF, FbF, ResF, GOAL_BUF, RESULT_BUF, FEEDBACK_BUF>(
2534 Some(self.node_id),
2535 action_name,
2536 A::ACTION_NAME,
2537 A::ACTION_HASH,
2538 super::arena::DEFAULT_ACTION_FEEDBACK_DEPTH,
2541 on_goal_response,
2542 on_feedback,
2543 on_result,
2544 )?;
2545 Ok(ActionClientCallback::new(core))
2546 }
2547
2548 pub fn create_generic_subscription<F>(
2550 &mut self,
2551 topic: &str,
2552 type_name: &str,
2553 type_hash: &str,
2554 callback: F,
2555 ) -> Result<super::types::HandleId, NodeError>
2556 where
2557 F: FnMut(&[u8]) + 'static,
2558 {
2559 self.create_generic_subscription_with_qos(
2560 topic,
2561 type_name,
2562 type_hash,
2563 QoSProfile::default(),
2564 callback,
2565 )
2566 }
2567
2568 pub fn create_generic_subscription_with_qos<F>(
2571 &mut self,
2572 topic: &str,
2573 type_name: &str,
2574 type_hash: &str,
2575 qos: QoSProfile,
2576 callback: F,
2577 ) -> Result<super::types::HandleId, NodeError>
2578 where
2579 F: FnMut(&[u8]) + 'static,
2580 {
2581 self.create_generic_subscription_with_qos_and_rx_hint(
2582 topic, type_name, type_hash, qos, None, callback,
2583 )
2584 }
2585
2586 pub fn create_generic_subscription_with_qos_and_rx_hint<F>(
2593 &mut self,
2594 topic: &str,
2595 type_name: &str,
2596 type_hash: &str,
2597 qos: QoSProfile,
2598 rx_hint: Option<usize>,
2599 callback: F,
2600 ) -> Result<super::types::HandleId, NodeError>
2601 where
2602 F: FnMut(&[u8]) + 'static,
2603 {
2604 self.executor
2605 .register_subscription_buffered_raw_on::<F, { crate::config::DEFAULT_RX_BUF_SIZE }>(
2606 self.node_id,
2607 topic,
2608 type_name,
2609 type_hash,
2610 qos,
2611 rx_hint,
2612 callback,
2613 )
2614 }
2615
2616 #[cfg(feature = "safety-e2e")]
2623 pub fn create_generic_subscription_with_integrity<F>(
2624 &mut self,
2625 topic: &str,
2626 type_name: &str,
2627 type_hash: &str,
2628 callback: F,
2629 ) -> Result<super::types::HandleId, NodeError>
2630 where
2631 F: FnMut(&[u8], &nros_rmw::IntegrityStatus) + 'static,
2632 {
2633 self.create_generic_subscription_with_integrity_and_rx_hint(
2634 topic, type_name, type_hash, None, callback,
2635 )
2636 }
2637
2638 #[cfg(feature = "safety-e2e")]
2642 pub fn create_generic_subscription_with_integrity_and_rx_hint<F>(
2643 &mut self,
2644 topic: &str,
2645 type_name: &str,
2646 type_hash: &str,
2647 rx_hint: Option<usize>,
2648 callback: F,
2649 ) -> Result<super::types::HandleId, NodeError>
2650 where
2651 F: FnMut(&[u8], &nros_rmw::IntegrityStatus) + 'static,
2652 {
2653 self.executor
2654 .register_subscription_buffered_raw_safety_on::<F, { crate::config::DEFAULT_RX_BUF_SIZE }>(
2655 self.node_id,
2656 topic,
2657 type_name,
2658 type_hash,
2659 QoSProfile::default(),
2660 rx_hint,
2661 callback,
2662 )
2663 }
2664
2665 pub fn create_subscription_viewable<B, F>(
2685 &mut self,
2686 topic: &str,
2687 callback: F,
2688 ) -> Result<super::types::HandleId, NodeError>
2689 where
2690 B: nros_core::ViewableMessage + 'static,
2691 F: for<'a> FnMut(&B::View<'a>) + 'static,
2692 {
2693 self.executor
2694 .register_subscription_buffered_borrowed_on::<B, F, { crate::config::DEFAULT_RX_BUF_SIZE }>(
2695 self.node_id,
2696 topic,
2697 QoSProfile::default().keep_last(1),
2698 callback,
2699 )
2700 }
2701
2702 pub fn service<'t>(&mut self, name: &'t str) -> CtxServiceBuilder<'_, 'e, 't, 's> {
2706 CtxServiceBuilder {
2707 ctx: self,
2708 name,
2709 qos: QoSProfile::services_default(),
2710 }
2711 }
2712
2713 pub fn create_service<'o, Svc, F>(
2721 &mut self,
2722 options: impl crate::IntoPrimitiveOptions<'o>,
2723 callback: F,
2724 ) -> Result<super::types::HandleId, NodeError>
2725 where
2726 Svc: RosService + 'static,
2727 Svc::Request: crate::rmw_type_registry::MessageForRmw,
2728 Svc::Reply: crate::rmw_type_registry::MessageForRmw,
2729 F: FnMut(&Svc::Request) -> Svc::Reply + 'static,
2730 {
2731 let options = options.into_primitive_options();
2732 self.executor.register_service_sized_on::<
2733 Svc,
2734 F,
2735 { crate::config::DEFAULT_RX_BUF_SIZE },
2736 { crate::config::DEFAULT_RX_BUF_SIZE },
2737 >(
2738 self.node_id,
2739 options.name,
2740 options.resolve(QoSProfile::services_default()),
2741 callback,
2742 )
2743 }
2744
2745 pub fn create_service_with_info<Svc, F>(
2754 &mut self,
2755 name: &str,
2756 callback: F,
2757 ) -> Result<super::types::HandleId, NodeError>
2758 where
2759 Svc: RosService + 'static,
2760 Svc::Request: crate::rmw_type_registry::MessageForRmw,
2761 Svc::Reply: crate::rmw_type_registry::MessageForRmw,
2762 F: FnMut(&Svc::Request, &nros_core::ServiceInfo) -> Svc::Reply + 'static,
2763 {
2764 self.service(name).service_info().build::<Svc, F>(callback)
2765 }
2766
2767 pub fn get_node_names(
2814 &mut self,
2815 visit: &mut dyn FnMut(&str, &str) -> bool,
2816 ) -> Result<(), NodeError> {
2817 self.executor
2818 .get_node_names(&mut |name, ns, _enclave| visit(name, ns))
2819 }
2820
2821 pub fn get_node_names_with_enclaves(
2829 &mut self,
2830 visit: &mut dyn FnMut(&str, &str, Option<&str>) -> bool,
2831 ) -> Result<(), NodeError> {
2832 self.executor.get_node_names(visit)
2833 }
2834
2835 pub fn get_topic_names_and_types(
2843 &mut self,
2844 visit: &mut dyn FnMut(&str, &[&str]) -> bool,
2845 ) -> Result<(), NodeError> {
2846 self.executor.get_topic_names_and_types(visit)
2847 }
2848
2849 pub fn get_service_names_and_types(
2854 &mut self,
2855 visit: &mut dyn FnMut(&str, &[&str]) -> bool,
2856 ) -> Result<(), NodeError> {
2857 self.executor.get_service_names_and_types(visit)
2858 }
2859
2860 pub fn count_publishers(&mut self, topic_name: &str) -> Result<usize, NodeError> {
2872 self.executor.count_publishers(topic_name)
2873 }
2874
2875 pub fn count_subscriptions(&mut self, topic_name: &str) -> Result<usize, NodeError> {
2889 self.executor.count_subscribers(topic_name)
2890 }
2891
2892 pub fn count_clients(&mut self, service_name: &str) -> Result<usize, NodeError> {
2899 self.executor.count_clients(service_name)
2900 }
2901
2902 pub fn count_services(&mut self, service_name: &str) -> Result<usize, NodeError> {
2905 self.executor.count_services(service_name)
2906 }
2907
2908 pub fn get_publisher_names_and_types_by_node(
2915 &mut self,
2916 node_name: &str,
2917 node_namespace: &str,
2918 visit: &mut dyn FnMut(&str, &[&str]) -> bool,
2919 ) -> Result<(), NodeError> {
2920 self.executor
2921 .get_publisher_names_and_types_by_node(node_name, node_namespace, visit)
2922 }
2923
2924 pub fn get_subscription_names_and_types_by_node(
2928 &mut self,
2929 node_name: &str,
2930 node_namespace: &str,
2931 visit: &mut dyn FnMut(&str, &[&str]) -> bool,
2932 ) -> Result<(), NodeError> {
2933 self.executor
2934 .get_subscription_names_and_types_by_node(node_name, node_namespace, visit)
2935 }
2936
2937 pub fn get_service_names_and_types_by_node(
2940 &mut self,
2941 node_name: &str,
2942 node_namespace: &str,
2943 visit: &mut dyn FnMut(&str, &[&str]) -> bool,
2944 ) -> Result<(), NodeError> {
2945 self.executor
2946 .get_service_names_and_types_by_node(node_name, node_namespace, visit)
2947 }
2948
2949 pub fn get_client_names_and_types_by_node(
2952 &mut self,
2953 node_name: &str,
2954 node_namespace: &str,
2955 visit: &mut dyn FnMut(&str, &[&str]) -> bool,
2956 ) -> Result<(), NodeError> {
2957 self.executor
2958 .get_client_names_and_types_by_node(node_name, node_namespace, visit)
2959 }
2960
2961 pub fn get_publishers_info_by_topic(
2982 &mut self,
2983 topic_name: &str,
2984 visit: &mut dyn FnMut(&nros_rmw::GraphEndpointInfo<'_>) -> bool,
2985 ) -> Result<(), NodeError> {
2986 self.executor
2987 .get_publishers_info_by_topic(topic_name, visit)
2988 }
2989
2990 pub fn get_subscriptions_info_by_topic(
2998 &mut self,
2999 topic_name: &str,
3000 visit: &mut dyn FnMut(&nros_rmw::GraphEndpointInfo<'_>) -> bool,
3001 ) -> Result<(), NodeError> {
3002 self.executor
3003 .get_subscriptions_info_by_topic(topic_name, visit)
3004 }
3005
3006 pub fn create_publisher_with_qos<M: MessageForRmw>(
3025 &mut self,
3026 topic_name: &str,
3027 qos: QoSProfile,
3028 ) -> Result<EmbeddedPublisher<M>, NodeError> {
3029 self.executor.with_node_try(self.node_id, |h| {
3030 h.create_publisher_with_qos::<M>(topic_name, qos)
3031 })
3032 }
3033
3034 pub fn create_publisher_raw(
3036 &mut self,
3037 topic_name: &str,
3038 type_name: &str,
3039 type_hash: &str,
3040 ) -> Result<crate::executor::handles::EmbeddedRawPublisher, NodeError> {
3041 self.executor.with_node_try(self.node_id, |h| {
3042 h.create_publisher_raw(topic_name, type_name, type_hash)
3043 })
3044 }
3045
3046 pub fn create_publisher_raw_with_qos(
3048 &mut self,
3049 topic_name: &str,
3050 type_name: &str,
3051 type_hash: &str,
3052 qos: QoSProfile,
3053 ) -> Result<crate::executor::handles::EmbeddedRawPublisher, NodeError> {
3054 self.executor.with_node_try(self.node_id, |h| {
3055 h.create_publisher_raw_with_qos(topic_name, type_name, type_hash, qos)
3056 })
3057 }
3058
3059 pub fn create_polling_subscription<M: MessageForRmw>(
3062 &mut self,
3063 topic_name: &str,
3064 ) -> Result<Subscription<M>, NodeError> {
3065 self.executor
3066 .with_node_try(self.node_id, |h| h.create_subscription::<M>(topic_name))
3067 }
3068
3069 pub fn create_polling_subscription_sized<M: MessageForRmw, const RX_BUF: usize>(
3072 &mut self,
3073 topic_name: &str,
3074 ) -> Result<Subscription<M, RX_BUF>, NodeError> {
3075 self.executor.with_node_try(self.node_id, |h| {
3076 h.create_subscription_sized::<M, RX_BUF>(topic_name)
3077 })
3078 }
3079
3080 pub fn create_polling_subscription_with_qos<M: MessageForRmw, const RX_BUF: usize>(
3083 &mut self,
3084 topic_name: &str,
3085 qos: QoSProfile,
3086 ) -> Result<Subscription<M, RX_BUF>, NodeError> {
3087 self.executor.with_node_try(self.node_id, |h| {
3088 h.create_subscription_with_qos::<M, RX_BUF>(topic_name, qos)
3089 })
3090 }
3091
3092 pub fn create_polling_subscription_raw(
3095 &mut self,
3096 topic_name: &str,
3097 type_name: &str,
3098 type_hash: &str,
3099 ) -> Result<crate::executor::handles::RawSubscription, NodeError> {
3100 self.executor.with_node_try(self.node_id, |h| {
3101 h.create_subscription_raw(topic_name, type_name, type_hash)
3102 })
3103 }
3104
3105 pub fn create_polling_subscription_raw_sized<const RX_BUF: usize>(
3108 &mut self,
3109 topic_name: &str,
3110 type_name: &str,
3111 type_hash: &str,
3112 ) -> Result<crate::executor::handles::RawSubscription<RX_BUF>, NodeError> {
3113 self.executor.with_node_try(self.node_id, |h| {
3114 h.create_subscription_raw_sized::<RX_BUF>(topic_name, type_name, type_hash)
3115 })
3116 }
3117
3118 pub fn create_polling_service<Svc: RosService>(
3121 &mut self,
3122 service_name: &str,
3123 ) -> Result<EmbeddedServiceServer<Svc>, NodeError>
3124 where
3125 Svc::Request: MessageForRmw,
3126 Svc::Reply: MessageForRmw,
3127 {
3128 self.executor
3129 .with_node_try(self.node_id, |h| h.create_service::<Svc>(service_name))
3130 }
3131
3132 pub fn create_polling_service_with_qos<Svc: RosService>(
3135 &mut self,
3136 service_name: &str,
3137 qos: QoSProfile,
3138 ) -> Result<EmbeddedServiceServer<Svc>, NodeError>
3139 where
3140 Svc::Request: MessageForRmw,
3141 Svc::Reply: MessageForRmw,
3142 {
3143 self.executor.with_node_try(self.node_id, |h| {
3144 h.create_service_with_qos::<Svc>(service_name, qos)
3145 })
3146 }
3147
3148 pub fn create_polling_service_sized<
3151 Svc: RosService,
3152 const REQ_BUF: usize,
3153 const REPLY_BUF: usize,
3154 >(
3155 &mut self,
3156 service_name: &str,
3157 qos: QoSProfile,
3158 ) -> Result<EmbeddedServiceServer<Svc, REQ_BUF, REPLY_BUF>, NodeError>
3159 where
3160 Svc::Request: MessageForRmw,
3161 Svc::Reply: MessageForRmw,
3162 {
3163 self.executor.with_node_try(self.node_id, |h| {
3164 h.create_service_sized::<Svc, REQ_BUF, REPLY_BUF>(service_name, qos)
3165 })
3166 }
3167
3168 pub fn create_client<'o, Svc: RosService>(
3175 &mut self,
3176 options: impl crate::IntoPrimitiveOptions<'o>,
3177 ) -> Result<EmbeddedServiceClient<Svc>, NodeError>
3178 where
3179 Svc::Request: MessageForRmw,
3180 Svc::Reply: MessageForRmw,
3181 {
3182 let options = options.into_primitive_options();
3183 let qos = options.resolve(QoSProfile::services_default());
3184 self.executor.with_node_try(self.node_id, |h| {
3185 h.create_client_with_qos::<Svc>(options.name, qos)
3186 })
3187 }
3188
3189 pub fn create_client_with_qos<Svc: RosService>(
3191 &mut self,
3192 service_name: &str,
3193 qos: QoSProfile,
3194 ) -> Result<EmbeddedServiceClient<Svc>, NodeError>
3195 where
3196 Svc::Request: MessageForRmw,
3197 Svc::Reply: MessageForRmw,
3198 {
3199 self.executor.with_node_try(self.node_id, |h| {
3200 h.create_client_with_qos::<Svc>(service_name, qos)
3201 })
3202 }
3203
3204 pub fn create_client_sized<Svc: RosService, const REQ_BUF: usize, const REPLY_BUF: usize>(
3206 &mut self,
3207 service_name: &str,
3208 qos: QoSProfile,
3209 ) -> Result<EmbeddedServiceClient<Svc, REQ_BUF, REPLY_BUF>, NodeError>
3210 where
3211 Svc::Request: MessageForRmw,
3212 Svc::Reply: MessageForRmw,
3213 {
3214 self.executor.with_node_try(self.node_id, |h| {
3215 h.create_client_sized::<Svc, REQ_BUF, REPLY_BUF>(service_name, qos)
3216 })
3217 }
3218
3219 pub fn create_polling_service_raw(
3222 &mut self,
3223 service_name: &str,
3224 type_name: &str,
3225 type_hash: &str,
3226 ) -> Result<crate::executor::handles::RawServiceServer, NodeError> {
3227 self.executor.with_node_try(self.node_id, |h| {
3228 h.create_service_raw(service_name, type_name, type_hash)
3229 })
3230 }
3231
3232 pub fn create_polling_service_raw_sized<const REQ_BUF: usize, const RESP_BUF: usize>(
3235 &mut self,
3236 service_name: &str,
3237 type_name: &str,
3238 type_hash: &str,
3239 ) -> Result<crate::executor::handles::RawServiceServer<REQ_BUF, RESP_BUF>, NodeError> {
3240 self.executor.with_node_try(self.node_id, |h| {
3241 h.create_service_raw_sized::<REQ_BUF, RESP_BUF>(service_name, type_name, type_hash)
3242 })
3243 }
3244
3245 pub fn create_client_raw(
3247 &mut self,
3248 service_name: &str,
3249 type_name: &str,
3250 type_hash: &str,
3251 ) -> Result<crate::executor::handles::RawServiceClient, NodeError> {
3252 self.executor.with_node_try(self.node_id, |h| {
3253 h.create_client_raw(service_name, type_name, type_hash)
3254 })
3255 }
3256
3257 pub fn create_client_raw_sized<const REQ_BUF: usize, const REPLY_BUF: usize>(
3259 &mut self,
3260 service_name: &str,
3261 type_name: &str,
3262 type_hash: &str,
3263 ) -> Result<crate::executor::handles::RawServiceClient<REQ_BUF, REPLY_BUF>, NodeError> {
3264 self.executor.with_node_try(self.node_id, |h| {
3265 h.create_client_raw_sized::<REQ_BUF, REPLY_BUF>(service_name, type_name, type_hash)
3266 })
3267 }
3268
3269 pub fn create_action_server_raw(
3271 &mut self,
3272 action_name: &str,
3273 type_name: &str,
3274 type_hash: &str,
3275 ) -> Result<
3276 super::action_core::ActionServerCore<
3277 { crate::config::DEFAULT_RX_BUF_SIZE },
3278 { crate::config::DEFAULT_RX_BUF_SIZE },
3279 { crate::config::DEFAULT_RX_BUF_SIZE },
3280 4,
3281 >,
3282 NodeError,
3283 > {
3284 self.executor.with_node_try(self.node_id, |h| {
3285 h.create_action_server_raw(action_name, type_name, type_hash)
3286 })
3287 }
3288
3289 pub fn create_action_server_raw_sized<
3291 const GOAL_BUF: usize,
3292 const RESULT_BUF: usize,
3293 const FEEDBACK_BUF: usize,
3294 const MAX_GOALS: usize,
3295 >(
3296 &mut self,
3297 action_name: &str,
3298 type_name: &str,
3299 type_hash: &str,
3300 ) -> Result<
3301 super::action_core::ActionServerCore<GOAL_BUF, RESULT_BUF, FEEDBACK_BUF, MAX_GOALS>,
3302 NodeError,
3303 > {
3304 self.executor.with_node_try(self.node_id, |h| {
3305 h.create_action_server_raw_sized::<GOAL_BUF, RESULT_BUF, FEEDBACK_BUF, MAX_GOALS>(
3306 action_name,
3307 type_name,
3308 type_hash,
3309 )
3310 })
3311 }
3312
3313 pub fn create_action_client_raw(
3315 &mut self,
3316 action_name: &str,
3317 type_name: &str,
3318 type_hash: &str,
3319 ) -> Result<
3320 super::action_core::ActionClientCore<
3321 { crate::config::DEFAULT_RX_BUF_SIZE },
3322 { crate::config::DEFAULT_RX_BUF_SIZE },
3323 { crate::config::DEFAULT_RX_BUF_SIZE },
3324 >,
3325 NodeError,
3326 > {
3327 self.executor.with_node_try(self.node_id, |h| {
3328 h.create_action_client_raw(action_name, type_name, type_hash)
3329 })
3330 }
3331
3332 pub fn create_action_client_raw_sized<
3334 const GOAL_BUF: usize,
3335 const RESULT_BUF: usize,
3336 const FEEDBACK_BUF: usize,
3337 >(
3338 &mut self,
3339 action_name: &str,
3340 type_name: &str,
3341 type_hash: &str,
3342 ) -> Result<super::action_core::ActionClientCore<GOAL_BUF, RESULT_BUF, FEEDBACK_BUF>, NodeError>
3343 {
3344 self.executor.with_node_try(self.node_id, |h| {
3345 h.create_action_client_raw_sized::<GOAL_BUF, RESULT_BUF, FEEDBACK_BUF>(
3346 action_name,
3347 type_name,
3348 type_hash,
3349 )
3350 })
3351 }
3352
3353 pub fn create_action_server<A: RosAction>(
3355 &mut self,
3356 action_name: &str,
3357 ) -> Result<ActionServer<A>, NodeError>
3358 where
3359 A::Goal: MessageForRmw,
3360 A::Result: MessageForRmw,
3361 A::Feedback: MessageForRmw,
3362 A::SendGoalRequest: MessageForRmw,
3363 A::SendGoalResponse: MessageForRmw,
3364 A::GetResultRequest: MessageForRmw,
3365 A::GetResultResponse: MessageForRmw,
3366 A::FeedbackMessage: MessageForRmw,
3367 {
3368 self.executor
3369 .with_node_try(self.node_id, |h| h.create_action_server::<A>(action_name))
3370 }
3371
3372 pub fn create_action_server_sized<
3374 A: RosAction,
3375 const GOAL_BUF: usize,
3376 const RESULT_BUF: usize,
3377 const FEEDBACK_BUF: usize,
3378 const MAX_GOALS: usize,
3379 >(
3380 &mut self,
3381 action_name: &str,
3382 ) -> Result<ActionServer<A, GOAL_BUF, RESULT_BUF, FEEDBACK_BUF, MAX_GOALS>, NodeError>
3383 where
3384 A::Goal: MessageForRmw,
3385 A::Result: MessageForRmw,
3386 A::Feedback: MessageForRmw,
3387 A::SendGoalRequest: MessageForRmw,
3388 A::SendGoalResponse: MessageForRmw,
3389 A::GetResultRequest: MessageForRmw,
3390 A::GetResultResponse: MessageForRmw,
3391 A::FeedbackMessage: MessageForRmw,
3392 {
3393 self.executor.with_node_try(self.node_id, |h| {
3394 h.create_action_server_sized::<A, GOAL_BUF, RESULT_BUF, FEEDBACK_BUF, MAX_GOALS>(
3395 action_name,
3396 )
3397 })
3398 }
3399
3400 pub fn create_action_client<A: RosAction>(
3402 &mut self,
3403 action_name: &str,
3404 ) -> Result<ActionClient<A>, NodeError>
3405 where
3406 A::Goal: MessageForRmw,
3407 A::Result: MessageForRmw,
3408 A::Feedback: MessageForRmw,
3409 A::SendGoalRequest: MessageForRmw,
3410 A::SendGoalResponse: MessageForRmw,
3411 A::GetResultRequest: MessageForRmw,
3412 A::GetResultResponse: MessageForRmw,
3413 A::FeedbackMessage: MessageForRmw,
3414 {
3415 self.executor
3416 .with_node_try(self.node_id, |h| h.create_action_client::<A>(action_name))
3417 }
3418
3419 pub fn create_action_client_sized<
3421 A: RosAction,
3422 const GOAL_BUF: usize,
3423 const RESULT_BUF: usize,
3424 const FEEDBACK_BUF: usize,
3425 >(
3426 &mut self,
3427 action_name: &str,
3428 ) -> Result<ActionClient<A, GOAL_BUF, RESULT_BUF, FEEDBACK_BUF>, NodeError>
3429 where
3430 A::Goal: MessageForRmw,
3431 A::Result: MessageForRmw,
3432 A::Feedback: MessageForRmw,
3433 A::SendGoalRequest: MessageForRmw,
3434 A::SendGoalResponse: MessageForRmw,
3435 A::GetResultRequest: MessageForRmw,
3436 A::GetResultResponse: MessageForRmw,
3437 A::FeedbackMessage: MessageForRmw,
3438 {
3439 self.executor.with_node_try(self.node_id, |h| {
3440 h.create_action_client_sized::<A, GOAL_BUF, RESULT_BUF, FEEDBACK_BUF>(action_name)
3441 })
3442 }
3443}
3444
3445pub struct CtxServiceBuilder<'c, 'e, 't, 's> {
3447 ctx: &'c mut Node<'e, 's>,
3448 name: &'t str,
3449 qos: QoSProfile,
3450}
3451
3452impl<'c, 'e, 't, 's> CtxServiceBuilder<'c, 'e, 't, 's> {
3453 pub fn qos(mut self, qos: QoSProfile) -> Self {
3456 self.qos = qos;
3457 self
3458 }
3459
3460 pub fn service_info(self) -> CtxServiceInfoBuilder<'c, 'e, 't, 's> {
3464 CtxServiceInfoBuilder {
3465 ctx: self.ctx,
3466 name: self.name,
3467 qos: self.qos,
3468 }
3469 }
3470
3471 pub fn build<Svc, F>(self, callback: F) -> Result<super::types::HandleId, NodeError>
3472 where
3473 Svc: RosService + 'static,
3474 Svc::Request: crate::rmw_type_registry::MessageForRmw,
3475 Svc::Reply: crate::rmw_type_registry::MessageForRmw,
3476 F: FnMut(&Svc::Request) -> Svc::Reply + 'static,
3477 {
3478 self.ctx.executor.register_service_sized_on::<
3479 Svc,
3480 F,
3481 { crate::config::DEFAULT_RX_BUF_SIZE },
3482 { crate::config::DEFAULT_RX_BUF_SIZE },
3483 >(self.ctx.node_id, self.name, self.qos, callback)
3484 }
3485}
3486
3487pub struct CtxServiceInfoBuilder<'c, 'e, 't, 's> {
3491 ctx: &'c mut Node<'e, 's>,
3492 name: &'t str,
3493 qos: QoSProfile,
3494}
3495
3496impl<'c, 'e, 't, 's> CtxServiceInfoBuilder<'c, 'e, 't, 's> {
3497 pub fn qos(mut self, qos: QoSProfile) -> Self {
3500 self.qos = qos;
3501 self
3502 }
3503
3504 pub fn build<Svc, F>(self, callback: F) -> Result<super::types::HandleId, NodeError>
3505 where
3506 Svc: RosService + 'static,
3507 Svc::Request: crate::rmw_type_registry::MessageForRmw,
3508 Svc::Reply: crate::rmw_type_registry::MessageForRmw,
3509 F: FnMut(&Svc::Request, &nros_core::ServiceInfo) -> Svc::Reply + 'static,
3510 {
3511 self.ctx.executor.register_service_with_info_sized_on::<
3512 Svc,
3513 F,
3514 { crate::config::DEFAULT_RX_BUF_SIZE },
3515 { crate::config::DEFAULT_RX_BUF_SIZE },
3516 >(self.ctx.node_id, self.name, self.qos, callback)
3517 }
3518}
3519
3520pub struct CtxPublisherBuilder<'c, 'e, 't, 's> {
3522 ctx: &'c mut Node<'e, 's>,
3523 topic: &'t str,
3524 qos: QoSProfile,
3525}
3526
3527impl<'c, 'e, 't, 's> CtxPublisherBuilder<'c, 'e, 't, 's> {
3528 pub fn qos(mut self, qos: QoSProfile) -> Self {
3529 self.qos = qos;
3530 self
3531 }
3532
3533 pub fn typed<M: MessageForRmw>(self) -> CtxTypedPublisherBuilder<'c, 'e, 't, 's, M> {
3535 CtxTypedPublisherBuilder {
3536 ctx: self.ctx,
3537 topic: self.topic,
3538 qos: self.qos,
3539 _phantom: PhantomData,
3540 }
3541 }
3542
3543 pub fn generic(
3545 self,
3546 type_name: &'t str,
3547 type_hash: &'t str,
3548 ) -> CtxGenericPublisherBuilder<'c, 'e, 't, 's> {
3549 CtxGenericPublisherBuilder {
3550 ctx: self.ctx,
3551 topic: self.topic,
3552 type_name,
3553 type_hash,
3554 qos: self.qos,
3555 }
3556 }
3557}
3558
3559pub struct CtxTypedPublisherBuilder<'c, 'e, 't, 's, M> {
3561 ctx: &'c mut Node<'e, 's>,
3562 topic: &'t str,
3563 qos: QoSProfile,
3564 _phantom: PhantomData<M>,
3565}
3566
3567impl<'c, 'e, 't, 's, M: MessageForRmw> CtxTypedPublisherBuilder<'c, 'e, 't, 's, M> {
3568 pub fn qos(mut self, qos: QoSProfile) -> Self {
3569 self.qos = qos;
3570 self
3571 }
3572
3573 pub fn build(self) -> Result<EmbeddedPublisher<M>, NodeError> {
3574 self.ctx
3575 .executor
3576 .create_publisher_on::<M>(self.ctx.node_id, self.topic, self.qos)
3577 }
3578}
3579
3580pub struct CtxGenericPublisherBuilder<'c, 'e, 't, 's> {
3582 ctx: &'c mut Node<'e, 's>,
3583 topic: &'t str,
3584 type_name: &'t str,
3585 type_hash: &'t str,
3586 qos: QoSProfile,
3587}
3588
3589impl<'c, 'e, 't, 's> CtxGenericPublisherBuilder<'c, 'e, 't, 's> {
3590 pub fn qos(mut self, qos: QoSProfile) -> Self {
3591 self.qos = qos;
3592 self
3593 }
3594
3595 pub fn build(self) -> Result<crate::executor::handles::EmbeddedRawPublisher, NodeError> {
3596 self.ctx.executor.create_publisher_raw_on(
3597 self.ctx.node_id,
3598 self.topic,
3599 self.type_name,
3600 self.type_hash,
3601 self.qos,
3602 )
3603 }
3604}
3605
3606pub struct SubscriptionBuilder<'c, 'e, 't, 's> {
3608 ctx: &'c mut Node<'e, 's>,
3609 topic: &'t str,
3610 qos: QoSProfile,
3611}
3612
3613impl<'c, 'e, 't, 's> SubscriptionBuilder<'c, 'e, 't, 's> {
3614 pub fn qos(mut self, qos: QoSProfile) -> Self {
3615 self.qos = qos;
3616 self
3617 }
3618
3619 pub fn typed<M: MessageForRmw + 'static>(self) -> TypedSubscriptionBuilder<'c, 'e, 't, 's, M> {
3621 TypedSubscriptionBuilder {
3622 ctx: self.ctx,
3623 topic: self.topic,
3624 qos: self.qos,
3625 sched: None,
3626 rx_explicit: false,
3627 _phantom: PhantomData,
3628 }
3629 }
3630
3631 pub fn generic(
3633 self,
3634 type_name: &'t str,
3635 type_hash: &'t str,
3636 ) -> GenericSubscriptionBuilder<'c, 'e, 't, 's> {
3637 GenericSubscriptionBuilder {
3638 ctx: self.ctx,
3639 topic: self.topic,
3640 type_name,
3641 type_hash,
3642 qos: self.qos,
3643 sched: None,
3644 }
3645 }
3646}
3647
3648pub struct TypedSubscriptionBuilder<
3651 'c,
3652 'e,
3653 't,
3654 's,
3655 M,
3656 const RX: usize = { crate::config::DEFAULT_RX_BUF_SIZE },
3657> {
3658 ctx: &'c mut Node<'e, 's>,
3659 topic: &'t str,
3660 qos: QoSProfile,
3661 sched: Option<super::sched_context::SchedContextId>,
3662 rx_explicit: bool,
3669 _phantom: PhantomData<M>,
3670}
3671
3672impl<'c, 'e, 't, 's, M: MessageForRmw + 'static, const RX: usize>
3673 TypedSubscriptionBuilder<'c, 'e, 't, 's, M, RX>
3674{
3675 pub fn qos(mut self, qos: QoSProfile) -> Self {
3676 self.qos = qos;
3677 self
3678 }
3679
3680 pub fn sched_context(mut self, sc: super::sched_context::SchedContextId) -> Self {
3682 self.sched = Some(sc);
3683 self
3684 }
3685
3686 pub fn rx_buffer<const N: usize>(self) -> TypedSubscriptionBuilder<'c, 'e, 't, 's, M, N> {
3699 TypedSubscriptionBuilder {
3700 ctx: self.ctx,
3701 topic: self.topic,
3702 qos: self.qos,
3703 sched: self.sched,
3704 rx_explicit: true,
3705 _phantom: PhantomData,
3706 }
3707 }
3708
3709 pub fn rx_buffer_from_type(self) -> TypedSubBoundBuilder<'c, 'e, 't, 's, M, RX>
3768 where
3769 M: nros_serdes::schema::Message,
3770 {
3771 const {
3775 assert!(
3776 crate::rmw_type_registry::subscription_rx_bytes::<M>(RX).is_some(),
3777 "this message type has NO maximum serialized size, so its receive \
3778 buffer cannot be sized from it. Every message type must carry a \
3779 bound: give the unbounded member one in the `.msg` \
3780 (`string<=64`, `int32[<=8]`) or a `cap` in `nros-codegen.toml`. \
3781 The generated C header for this type names the member that costs \
3782 it the bound (`NROS_UNBOUNDED__<type>__field_<member>`)."
3783 )
3784 }
3785 TypedSubBoundBuilder {
3786 ctx: self.ctx,
3787 topic: self.topic,
3788 qos: self.qos,
3789 sched: self.sched,
3790 _phantom: PhantomData,
3791 }
3792 }
3793
3794 pub fn message_info(self) -> TypedSubInfoBuilder<'c, 'e, 't, 's, M, RX> {
3799 TypedSubInfoBuilder {
3800 ctx: self.ctx,
3801 topic: self.topic,
3802 qos: self.qos,
3803 sched: self.sched,
3804 rx_explicit: self.rx_explicit,
3805 _phantom: PhantomData,
3806 }
3807 }
3808
3809 #[cfg(feature = "safety-e2e")]
3812 pub fn safety(self) -> TypedSubSafetyBuilder<'c, 'e, 't, 's, M, RX> {
3813 TypedSubSafetyBuilder {
3814 ctx: self.ctx,
3815 topic: self.topic,
3816 qos: self.qos,
3817 sched: self.sched,
3818 rx_explicit: self.rx_explicit,
3819 _phantom: PhantomData,
3820 }
3821 }
3822
3823 pub fn build<F: FnMut(&M) + 'static>(
3824 self,
3825 callback: F,
3826 ) -> Result<super::types::HandleId, NodeError> {
3827 let handle = self
3828 .ctx
3829 .executor
3830 .register_subscription_buffered_on::<M, F, RX>(
3831 self.ctx.node_id,
3832 self.topic,
3833 self.qos,
3834 callback,
3835 None, if self.rx_explicit { Some(RX) } else { None },
3840 )?;
3841 if let Some(sc) = self.sched {
3842 self.ctx.executor.bind_handle_to_sched_context(handle, sc)?;
3843 }
3844 Ok(handle)
3845 }
3846}
3847
3848pub struct TypedSubBoundBuilder<
3856 'c,
3857 'e,
3858 't,
3859 's,
3860 M,
3861 const RX: usize = { crate::config::DEFAULT_RX_BUF_SIZE },
3862> {
3863 ctx: &'c mut Node<'e, 's>,
3864 topic: &'t str,
3865 qos: QoSProfile,
3866 sched: Option<super::sched_context::SchedContextId>,
3867 _phantom: PhantomData<M>,
3868}
3869
3870impl<'c, 'e, 't, 's, M: MessageForRmw + nros_serdes::schema::Message + 'static, const RX: usize>
3871 TypedSubBoundBuilder<'c, 'e, 't, 's, M, RX>
3872{
3873 pub fn qos(mut self, qos: QoSProfile) -> Self {
3874 self.qos = qos;
3875 self
3876 }
3877
3878 pub fn sched_context(mut self, sc: super::sched_context::SchedContextId) -> Self {
3880 self.sched = Some(sc);
3881 self
3882 }
3883
3884 pub fn build<F: FnMut(&M) + 'static>(
3885 self,
3886 callback: F,
3887 ) -> Result<super::types::HandleId, NodeError> {
3888 let rx_bytes = crate::rmw_type_registry::subscription_rx_bytes::<M>(RX)
3891 .expect("rx_buffer_from_type asserts the bound exists at build time");
3892 let handle = self
3893 .ctx
3894 .executor
3895 .register_subscription_buffered_on::<M, F, RX>(
3896 self.ctx.node_id,
3897 self.topic,
3898 self.qos,
3899 callback,
3900 None,
3901 Some(rx_bytes),
3902 )?;
3903 if let Some(sc) = self.sched {
3904 self.ctx.executor.bind_handle_to_sched_context(handle, sc)?;
3905 }
3906 Ok(handle)
3907 }
3908}
3909
3910pub struct TypedSubInfoBuilder<
3913 'c,
3914 'e,
3915 't,
3916 's,
3917 M,
3918 const RX: usize = { crate::config::DEFAULT_RX_BUF_SIZE },
3919> {
3920 ctx: &'c mut Node<'e, 's>,
3921 topic: &'t str,
3922 qos: QoSProfile,
3923 sched: Option<super::sched_context::SchedContextId>,
3924 rx_explicit: bool,
3927 _phantom: PhantomData<M>,
3928}
3929
3930impl<'c, 'e, 't, 's, M: MessageForRmw + 'static, const RX: usize>
3931 TypedSubInfoBuilder<'c, 'e, 't, 's, M, RX>
3932{
3933 pub fn qos(mut self, qos: QoSProfile) -> Self {
3934 self.qos = qos;
3935 self
3936 }
3937
3938 pub fn sched_context(mut self, sc: super::sched_context::SchedContextId) -> Self {
3939 self.sched = Some(sc);
3940 self
3941 }
3942
3943 pub fn rx_buffer<const N: usize>(self) -> TypedSubInfoBuilder<'c, 'e, 't, 's, M, N> {
3944 TypedSubInfoBuilder {
3945 ctx: self.ctx,
3946 topic: self.topic,
3947 qos: self.qos,
3948 sched: self.sched,
3949 rx_explicit: true,
3950 _phantom: PhantomData,
3951 }
3952 }
3953
3954 pub fn build<F: FnMut(&M, Option<&nros_core::MessageInfo>) + 'static>(
3955 self,
3956 callback: F,
3957 ) -> Result<super::types::HandleId, NodeError> {
3958 let handle = self
3959 .ctx
3960 .executor
3961 .register_subscription_with_info_sized_inner::<M, F, RX>(
3962 Some(self.ctx.node_id),
3963 self.topic,
3964 self.qos,
3965 callback,
3966 if self.rx_explicit { Some(RX) } else { None },
3967 )?;
3968 if let Some(sc) = self.sched {
3969 self.ctx.executor.bind_handle_to_sched_context(handle, sc)?;
3970 }
3971 Ok(handle)
3972 }
3973}
3974
3975#[cfg(feature = "safety-e2e")]
3978pub struct TypedSubSafetyBuilder<
3979 'c,
3980 'e,
3981 't,
3982 's,
3983 M,
3984 const RX: usize = { crate::config::DEFAULT_RX_BUF_SIZE },
3985> {
3986 ctx: &'c mut Node<'e, 's>,
3987 topic: &'t str,
3988 qos: QoSProfile,
3989 sched: Option<super::sched_context::SchedContextId>,
3990 rx_explicit: bool,
3993 _phantom: PhantomData<M>,
3994}
3995
3996#[cfg(feature = "safety-e2e")]
3997impl<'c, 'e, 't, 's, M: MessageForRmw + 'static, const RX: usize>
3998 TypedSubSafetyBuilder<'c, 'e, 't, 's, M, RX>
3999{
4000 pub fn qos(mut self, qos: QoSProfile) -> Self {
4001 self.qos = qos;
4002 self
4003 }
4004
4005 pub fn sched_context(mut self, sc: super::sched_context::SchedContextId) -> Self {
4006 self.sched = Some(sc);
4007 self
4008 }
4009
4010 pub fn rx_buffer<const N: usize>(self) -> TypedSubSafetyBuilder<'c, 'e, 't, 's, M, N> {
4011 TypedSubSafetyBuilder {
4012 ctx: self.ctx,
4013 topic: self.topic,
4014 qos: self.qos,
4015 sched: self.sched,
4016 rx_explicit: true,
4017 _phantom: PhantomData,
4018 }
4019 }
4020
4021 pub fn build<F: FnMut(&M, &nros_rmw::IntegrityStatus) + 'static>(
4022 self,
4023 callback: F,
4024 ) -> Result<super::types::HandleId, NodeError> {
4025 let handle = self
4026 .ctx
4027 .executor
4028 .register_subscription_with_safety_sized_inner::<M, F, RX>(
4029 Some(self.ctx.node_id),
4030 self.topic,
4031 self.qos,
4032 callback,
4033 if self.rx_explicit { Some(RX) } else { None },
4034 )?;
4035 if let Some(sc) = self.sched {
4036 self.ctx.executor.bind_handle_to_sched_context(handle, sc)?;
4037 }
4038 Ok(handle)
4039 }
4040}
4041
4042pub struct GenericSubscriptionBuilder<
4044 'c,
4045 'e,
4046 't,
4047 's,
4048 const RX: usize = { crate::config::DEFAULT_RX_BUF_SIZE },
4049> {
4050 ctx: &'c mut Node<'e, 's>,
4051 topic: &'t str,
4052 type_name: &'t str,
4053 type_hash: &'t str,
4054 qos: QoSProfile,
4055 sched: Option<super::sched_context::SchedContextId>,
4056}
4057
4058impl<'c, 'e, 't, 's, const RX: usize> GenericSubscriptionBuilder<'c, 'e, 't, 's, RX> {
4059 pub fn qos(mut self, qos: QoSProfile) -> Self {
4060 self.qos = qos;
4061 self
4062 }
4063
4064 pub fn sched_context(mut self, sc: super::sched_context::SchedContextId) -> Self {
4065 self.sched = Some(sc);
4066 self
4067 }
4068
4069 pub fn rx_buffer<const N: usize>(self) -> GenericSubscriptionBuilder<'c, 'e, 't, 's, N> {
4070 GenericSubscriptionBuilder {
4071 ctx: self.ctx,
4072 topic: self.topic,
4073 type_name: self.type_name,
4074 type_hash: self.type_hash,
4075 qos: self.qos,
4076 sched: self.sched,
4077 }
4078 }
4079
4080 pub fn message_info(self) -> GenericSubInfoBuilder<'c, 'e, 't, 's, RX> {
4084 GenericSubInfoBuilder {
4085 ctx: self.ctx,
4086 topic: self.topic,
4087 type_name: self.type_name,
4088 type_hash: self.type_hash,
4089 qos: self.qos,
4090 sched: self.sched,
4091 }
4092 }
4093
4094 pub fn build<F: FnMut(&[u8]) + 'static>(
4095 self,
4096 callback: F,
4097 ) -> Result<super::types::HandleId, NodeError> {
4098 let handle = self
4099 .ctx
4100 .executor
4101 .register_subscription_buffered_raw_on::<F, RX>(
4102 self.ctx.node_id,
4103 self.topic,
4104 self.type_name,
4105 self.type_hash,
4106 self.qos,
4107 None,
4108 callback,
4109 )?;
4110 if let Some(sc) = self.sched {
4111 self.ctx.executor.bind_handle_to_sched_context(handle, sc)?;
4112 }
4113 Ok(handle)
4114 }
4115}
4116
4117pub struct GenericSubInfoBuilder<
4120 'c,
4121 'e,
4122 't,
4123 's,
4124 const RX: usize = { crate::config::DEFAULT_RX_BUF_SIZE },
4125> {
4126 ctx: &'c mut Node<'e, 's>,
4127 topic: &'t str,
4128 type_name: &'t str,
4129 type_hash: &'t str,
4130 qos: QoSProfile,
4131 sched: Option<super::sched_context::SchedContextId>,
4132}
4133
4134impl<'c, 'e, 't, 's, const RX: usize> GenericSubInfoBuilder<'c, 'e, 't, 's, RX> {
4135 pub fn qos(mut self, qos: QoSProfile) -> Self {
4136 self.qos = qos;
4137 self
4138 }
4139
4140 pub fn sched_context(mut self, sc: super::sched_context::SchedContextId) -> Self {
4141 self.sched = Some(sc);
4142 self
4143 }
4144
4145 pub fn rx_buffer<const N: usize>(self) -> GenericSubInfoBuilder<'c, 'e, 't, 's, N> {
4146 GenericSubInfoBuilder {
4147 ctx: self.ctx,
4148 topic: self.topic,
4149 type_name: self.type_name,
4150 type_hash: self.type_hash,
4151 qos: self.qos,
4152 sched: self.sched,
4153 }
4154 }
4155
4156 pub fn build<F: FnMut(&[u8], &nros_core::RawMessageInfo) + 'static>(
4157 self,
4158 callback: F,
4159 ) -> Result<super::types::HandleId, NodeError> {
4160 let handle = self
4161 .ctx
4162 .executor
4163 .register_subscription_buffered_raw_info_on::<F, RX>(
4164 self.ctx.node_id,
4165 self.topic,
4166 self.type_name,
4167 self.type_hash,
4168 self.qos,
4169 callback,
4170 )?;
4171 if let Some(sc) = self.sched {
4172 self.ctx.executor.bind_handle_to_sched_context(handle, sc)?;
4173 }
4174 Ok(handle)
4175 }
4176}
4177
4178#[cfg(all(test, feature = "alloc", not(feature = "rmw-cffi")))]
4184mod builder_tests {
4185 use super::*;
4186 use crate::{executor::Executor, mock::MockSession};
4187 use nros_core::{CdrReader, CdrWriter, DeserError, Deserialize, SerError, Serialize};
4188
4189 struct TestMsg;
4190 impl RosMessage for TestMsg {
4191 const TYPE_NAME: &'static str = "test/msg/TestMsg";
4192 const TYPE_HASH: &'static str = "test_hash";
4193 }
4194 impl Serialize for TestMsg {
4195 fn serialize(&self, _w: &mut CdrWriter) -> Result<(), SerError> {
4196 Ok(())
4197 }
4198 }
4199 impl Deserialize for TestMsg {
4200 fn deserialize(_r: &mut CdrReader) -> Result<Self, DeserError> {
4201 Ok(Self)
4202 }
4203 }
4204 impl nros_serdes::schema::Message for TestMsg {
4213 const TYPE_NAME: &'static str = "test/msg/TestMsg";
4214 const FIELDS: &'static [nros_serdes::schema::Field] = &[nros_serdes::schema::Field {
4215 name: "data",
4216 ty: nros_serdes::schema::FieldType::Uint8,
4217 offset: 0,
4218 }];
4219 }
4220
4221 fn s(v: &str) -> heapless::String<64> {
4222 heapless::String::try_from(v).unwrap()
4223 }
4224
4225 #[test]
4226 fn publisher_builder_typed_and_generic() {
4227 let mut session = MockSession::new();
4228 let mut node = NodeHandle::new(
4229 s("n"),
4230 s("/"),
4231 &mut session,
4232 crate::executor::spin::RemapView {
4233 launch: &[],
4234 runtime: &[],
4235 },
4236 0,
4237 );
4238
4239 let _typed = node
4241 .publisher("/chatter")
4242 .typed::<TestMsg>()
4243 .qos(QoSProfile::default().keep_last(5))
4244 .build()
4245 .expect("typed publisher builds");
4246
4247 let _generic = node
4249 .publisher("/chatter")
4250 .qos(QoSProfile::default())
4251 .generic("std_msgs/msg/Int32", "hash")
4252 .build()
4253 .expect("generic publisher builds");
4254 }
4255
4256 fn argv_rule(from: &'static str, to: &'static str) -> crate::ros_args::RemapArg<'static> {
4261 crate::ros_args::RemapArg {
4262 node: None,
4263 from,
4264 to,
4265 }
4266 }
4267
4268 #[test]
4269 fn create_node_handle_applies_an_argv_remap() {
4270 let mut exec: Executor = Executor::from_session(MockSession::new());
4271 crate::ros_args::install_argv_remaps(&mut exec, [argv_rule("chatter", "/from_argv")])
4272 .unwrap();
4273 let mut node = exec.create_node("talker").expect("node");
4274 let p = node
4275 .create_publisher::<TestMsg>("chatter")
4276 .expect("publisher");
4277 assert_eq!(p.handle.topic_name(), "/from_argv");
4278 }
4279
4280 #[test]
4281 fn create_node_handle_prefers_a_launch_rule_over_argv() {
4282 let mut exec: Executor = Executor::from_session(MockSession::new());
4283 crate::ros_args::install_argv_remaps(&mut exec, [argv_rule("chatter", "/from_argv")])
4284 .unwrap();
4285 exec.declare_remap("talker", "/", "chatter", "/from_launch")
4286 .unwrap();
4287 let mut node = exec.create_node("talker").expect("node");
4288 let p = node
4289 .create_publisher::<TestMsg>("chatter")
4290 .expect("publisher");
4291 assert_eq!(p.handle.topic_name(), "/from_launch");
4292 }
4293
4294 #[test]
4296 fn create_node_handle_prefers_a_flash_launch_rule_over_argv() {
4297 static LAUNCH: [crate::executor::LaunchRemap; 1] = [crate::executor::LaunchRemap::new(
4298 c"talker",
4299 c"/",
4300 c"chatter",
4301 c"/from_flash",
4302 )];
4303 let mut exec: Executor = Executor::from_session(MockSession::new());
4304 crate::ros_args::install_argv_remaps(&mut exec, [argv_rule("chatter", "/from_argv")])
4305 .unwrap();
4306 exec.set_launch_remaps(&LAUNCH);
4307 let mut node = exec.create_node("talker").expect("node");
4308 let p = node
4309 .create_publisher::<TestMsg>("chatter")
4310 .expect("publisher");
4311 assert_eq!(p.handle.topic_name(), "/from_flash");
4312 }
4313
4314 #[test]
4315 fn create_node_handle_passes_an_unmatched_name_through_unchanged() {
4316 let mut exec: Executor = Executor::from_session(MockSession::new());
4320 crate::ros_args::install_argv_remaps(&mut exec, [argv_rule("other", "/x")]).unwrap();
4321 let mut node = exec.create_node("talker").expect("node");
4322 let p = node
4323 .create_publisher::<TestMsg>("chatter")
4324 .expect("publisher");
4325 assert_eq!(p.handle.topic_name(), "chatter");
4326 }
4327
4328 #[test]
4329 fn subscription_builder_and_convenient() {
4330 let mut exec: Executor = Executor::from_session(MockSession::new());
4331 let id = exec.node_builder("n").build().expect("node");
4332
4333 let _h = exec
4335 .node_mut(id)
4336 .subscription("/chatter")
4337 .typed::<TestMsg>()
4338 .qos(QoSProfile::default().keep_last(5))
4339 .build(|_m: &TestMsg| {})
4340 .expect("typed subscription builds");
4341
4342 let _g = exec
4344 .node_mut(id)
4345 .subscription("/raw")
4346 .generic("std_msgs/msg/Int32", "hash")
4347 .build(|_b: &[u8]| {})
4348 .expect("generic subscription builds");
4349
4350 let sc = exec.default_sched_context_id();
4352 let _s = exec
4353 .node_mut(id)
4354 .subscription("/sized")
4355 .typed::<TestMsg>()
4356 .rx_buffer::<64>()
4357 .sched_context(sc)
4358 .build(|_m: &TestMsg| {})
4359 .expect("sized + sched subscription builds");
4360
4361 let _c = exec
4363 .node_mut(id)
4364 .create_subscription::<TestMsg, _>("/conv", |_m: &TestMsg| {})
4365 .expect("convenient typed subscription builds");
4366 }
4367
4368 #[test]
4369 fn generic_message_info_builder() {
4370 let mut exec: Executor = Executor::from_session(MockSession::new());
4373 let id = exec.node_builder("n").build().expect("node");
4374
4375 let _i = exec
4376 .node_mut(id)
4377 .subscription("/info")
4378 .generic("std_msgs/msg/Int32", "hash")
4379 .message_info()
4380 .rx_buffer::<256>()
4381 .build(|_payload: &[u8], info: &nros_core::RawMessageInfo| {
4382 let _ = info.attachment();
4383 })
4384 .expect("generic + message_info subscription builds");
4385 }
4386
4387 #[test]
4388 fn typed_message_info_builder() {
4389 let mut exec: Executor = Executor::from_session(MockSession::new());
4392 let id = exec.node_builder("n").build().expect("node");
4393 let _h = exec
4394 .node_mut(id)
4395 .subscription("/chatter")
4396 .typed::<TestMsg>()
4397 .qos(QoSProfile::default().keep_last(5))
4398 .message_info()
4399 .build(|_m: &TestMsg, _info: Option<&nros_core::MessageInfo>| {})
4400 .expect("typed + message_info subscription builds");
4401 }
4402
4403 #[cfg(feature = "safety-e2e")]
4404 #[test]
4405 fn typed_safety_builder() {
4406 let mut exec: Executor = Executor::from_session(MockSession::new());
4408 let id = exec.node_builder("n").build().expect("node");
4409 let _h = exec
4410 .node_mut(id)
4411 .subscription("/chatter")
4412 .typed::<TestMsg>()
4413 .safety()
4414 .build(|_m: &TestMsg, _status: &nros_rmw::IntegrityStatus| {})
4415 .expect("typed + safety subscription builds");
4416 }
4417
4418 #[test]
4419 fn generator_emitted_chain_compiles() {
4420 let mut exec: Executor = Executor::from_session(MockSession::new());
4423 let id = exec.node_builder("n").build().expect("node");
4424 let _h = exec
4425 .node_mut(id)
4426 .subscription("/topic")
4427 .generic("std_msgs/msg/Int32", "hash")
4428 .qos(QoSProfile::default().keep_last(1))
4429 .rx_buffer::<1024>()
4430 .build(|_data: &[u8]| {})
4431 .expect("generator-shape subscription builds");
4432 }
4433
4434 #[test]
4435 fn nodectx_publisher_and_bridge_shape() {
4436 let mut exec: Executor = Executor::from_session(MockSession::new());
4440 let id = exec.node_builder("n").build().expect("node");
4441
4442 let _p = exec
4444 .node_mut(id)
4445 .create_publisher::<TestMsg>("/p")
4446 .expect("ctx convenient publisher");
4447 let dest_pub = exec
4448 .node_mut(id)
4449 .publisher("/fwd")
4450 .generic("std_msgs/msg/Int32", "hash")
4451 .build()
4452 .expect("ctx generic publisher builds"); let _s = exec
4456 .node_mut(id)
4457 .subscription("/src")
4458 .generic("std_msgs/msg/Int32", "hash")
4459 .message_info()
4460 .build(move |payload: &[u8], _info: &nros_core::RawMessageInfo| {
4461 let _ = dest_pub.publish_serialized(payload);
4462 })
4463 .expect("bridge-shape source subscription builds");
4464 }
4465}