1use std::{
15 fmt,
16 future::{IntoFuture, Ready},
17 sync::{
18 atomic::{AtomicU32, Ordering},
19 Arc,
20 },
21 time::Duration,
22};
23
24use zenoh::{
25 bytes::{Encoding, OptionZBytes, ZBytes},
26 internal::{
27 bail,
28 runtime::ZRuntime,
29 traits::{
30 EncodingBuilderTrait, QoSBuilderTrait, TimestampBuilderTrait,
31 TimestampInstrumentationBuilderTrait,
32 },
33 TerminatableTask,
34 },
35 key_expr::{keyexpr, KeyExpr},
36 liveliness::LivelinessToken,
37 pubsub::{
38 PublicationBuilder, PublicationBuilderDelete, PublicationBuilderPut, Publisher,
39 PublisherBuilder, PublisherUndeclaration,
40 },
41 qos::{CongestionControl, Priority, Reliability},
42 sample::{Locality, SourceInfo},
43 session::EntityGlobalId,
44 Resolvable, Resolve, Result as ZResult, Session, Wait, KE_ADV_PREFIX, KE_EMPTY,
45};
46use zenoh_macros::ke;
47
48use crate::{
49 advanced_cache::{AdvancedCache, AdvancedCacheBuilder, CacheConfig, KE_UHLC},
50 z_serialize,
51};
52
53pub(crate) static KE_PUB: &keyexpr = ke!("pub");
54
55#[derive(Debug, PartialEq)]
56#[zenoh_macros::unstable]
57pub(crate) enum Sequencing {
58 None,
59 Timestamp,
60 SequenceNumber,
61}
62
63#[zenoh_macros::unstable]
70#[derive(Debug, Default, Clone)]
71pub struct MissDetectionConfig {
72 pub(crate) state_publisher: Option<(Duration, bool)>,
73}
74
75#[zenoh_macros::unstable]
76impl MissDetectionConfig {
77 #[zenoh_macros::unstable]
87 pub fn heartbeat(mut self, period: Duration) -> Self {
88 self.state_publisher = Some((period, false));
89 self
90 }
91
92 #[zenoh_macros::unstable]
103 pub fn sporadic_heartbeat(mut self, period: Duration) -> Self {
104 self.state_publisher = Some((period, true));
105 self
106 }
107}
108
109#[must_use = "Resolvables do nothing unless you resolve them using the `res` method from either `SyncResolve` or `AsyncResolve`"]
111#[zenoh_macros::unstable]
112pub struct AdvancedPublisherBuilder<'a, 'b, 'c> {
113 session: &'a Session,
114 pub_key_expr: ZResult<KeyExpr<'b>>,
115 encoding: Encoding,
116 destination: Locality,
117 reliability: Reliability,
118 congestion_control: CongestionControl,
119 priority: Priority,
120 is_express: bool,
121 meta_key_expr: Option<ZResult<KeyExpr<'c>>>,
122 sequencing: Sequencing,
123 miss_config: Option<MissDetectionConfig>,
124 liveliness: bool,
125 history: Option<CacheConfig>,
126}
127
128#[zenoh_macros::unstable]
129impl fmt::Debug for AdvancedPublisherBuilder<'_, '_, '_> {
130 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
131 f.debug_struct("AdvancedPublisherBuilder")
132 .field("session", &"..")
133 .field("pub_key_expr", &self.pub_key_expr)
134 .field("encoding", &self.encoding)
135 .field("destination", &self.destination)
136 .field("reliability", &self.reliability)
137 .field("congestion_control", &self.congestion_control)
138 .field("priority", &self.priority)
139 .field("is_express", &self.is_express)
140 .field("meta_key_expr", &self.meta_key_expr)
141 .field("sequencing", &self.sequencing)
142 .field("miss_config", &self.miss_config)
143 .field("liveliness", &self.liveliness)
144 .field("history", &self.history)
145 .finish()
146 }
147}
148
149#[zenoh_macros::unstable]
150impl<'a, 'b, 'c> AdvancedPublisherBuilder<'a, 'b, 'c> {
151 #[zenoh_macros::unstable]
152 pub(crate) fn new(builder: PublisherBuilder<'a, 'b>) -> AdvancedPublisherBuilder<'a, 'b, 'c> {
153 AdvancedPublisherBuilder {
154 session: builder.session,
155 pub_key_expr: builder.key_expr,
156 encoding: builder.encoding,
157 destination: builder.destination,
158 reliability: builder.reliability,
159 congestion_control: builder.congestion_control,
160 priority: builder.priority,
161 is_express: builder.is_express,
162 meta_key_expr: None,
163 sequencing: Sequencing::None,
164 miss_config: None,
165 liveliness: false,
166 history: None,
167 }
168 }
169
170 #[zenoh_macros::unstable]
175 #[inline]
176 pub fn allowed_destination(mut self, destination: Locality) -> Self {
177 self.destination = destination;
178 self
179 }
180
181 #[zenoh_macros::unstable]
187 #[inline]
188 pub fn reliability(self, reliability: Reliability) -> Self {
189 Self {
190 reliability,
191 ..self
192 }
193 }
194
195 #[zenoh_macros::unstable]
199 pub fn sample_miss_detection(mut self, config: MissDetectionConfig) -> Self {
200 self.sequencing = Sequencing::SequenceNumber;
201 self.miss_config = Some(config);
202 self
203 }
204
205 #[zenoh_macros::unstable]
209 pub fn cache(mut self, config: CacheConfig) -> Self {
210 if self.sequencing == Sequencing::None {
211 self.sequencing = Sequencing::Timestamp;
212 }
213 self.history = Some(config);
214 self
215 }
216
217 #[zenoh_macros::unstable]
221 pub fn publisher_detection(mut self) -> Self {
222 self.liveliness = true;
223 self
224 }
225
226 #[zenoh_macros::unstable]
230 pub fn publisher_detection_metadata<TryIntoKeyExpr>(mut self, meta: TryIntoKeyExpr) -> Self
231 where
232 TryIntoKeyExpr: TryInto<KeyExpr<'c>>,
233 <TryIntoKeyExpr as TryInto<KeyExpr<'c>>>::Error: Into<zenoh::Error>,
234 {
235 self.meta_key_expr = Some(meta.try_into().map_err(Into::into));
236 self
237 }
238}
239
240#[zenoh_macros::internal_trait]
241#[zenoh_macros::unstable]
242impl EncodingBuilderTrait for AdvancedPublisherBuilder<'_, '_, '_> {
243 #[zenoh_macros::unstable]
245 fn encoding<T: Into<Encoding>>(self, encoding: T) -> Self {
246 Self {
247 encoding: encoding.into(),
248 ..self
249 }
250 }
251}
252
253#[zenoh_macros::internal_trait]
254#[zenoh_macros::unstable]
255impl QoSBuilderTrait for AdvancedPublisherBuilder<'_, '_, '_> {
256 #[inline]
258 #[zenoh_macros::unstable]
259 fn congestion_control(self, congestion_control: CongestionControl) -> Self {
260 Self {
261 congestion_control,
262 ..self
263 }
264 }
265
266 #[inline]
268 #[zenoh_macros::unstable]
269 fn priority(self, priority: Priority) -> Self {
270 Self { priority, ..self }
271 }
272
273 #[inline]
278 #[zenoh_macros::unstable]
279 fn express(self, is_express: bool) -> Self {
280 Self { is_express, ..self }
281 }
282}
283
284#[zenoh_macros::unstable]
285impl<'b> Resolvable for AdvancedPublisherBuilder<'_, 'b, '_> {
286 type To = ZResult<AdvancedPublisher<'b>>;
287}
288
289#[zenoh_macros::unstable]
290impl Wait for AdvancedPublisherBuilder<'_, '_, '_> {
291 #[zenoh_macros::unstable]
292 fn wait(self) -> <Self as Resolvable>::To {
293 AdvancedPublisher::new(self)
294 }
295}
296
297#[zenoh_macros::unstable]
298impl IntoFuture for AdvancedPublisherBuilder<'_, '_, '_> {
299 type Output = <Self as Resolvable>::To;
300 type IntoFuture = Ready<<Self as Resolvable>::To>;
301
302 #[zenoh_macros::unstable]
303 fn into_future(self) -> Self::IntoFuture {
304 std::future::ready(self.wait())
305 }
306}
307
308#[zenoh_macros::unstable]
341pub struct AdvancedPublisher<'a> {
342 publisher: Publisher<'a>,
343 seqnum: Option<Arc<AtomicU32>>,
344 cache: Option<AdvancedCache>,
345 _token: Option<LivelinessToken>,
346 _state_publisher: Option<TerminatableTask>,
347}
348
349#[zenoh_macros::unstable]
350impl fmt::Debug for AdvancedPublisher<'_> {
351 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
352 f.debug_struct("AdvancedPublisher")
353 .field("publisher", &self.publisher)
354 .field(
355 "seqnum",
356 &self
357 .seqnum
358 .as_ref()
359 .map(|seqnum| seqnum.load(Ordering::Relaxed)),
360 )
361 .field("cache", &self.cache.as_ref().map(|_| ".."))
362 .field("token", &self._token.as_ref().map(|_| ".."))
363 .field("state_publisher", &self._state_publisher)
364 .finish()
365 }
366}
367
368#[zenoh_macros::unstable]
369impl<'a> AdvancedPublisher<'a> {
370 #[zenoh_macros::unstable]
371 fn new(conf: AdvancedPublisherBuilder<'_, 'a, '_>) -> ZResult<Self> {
372 let key_expr = conf.pub_key_expr?;
373 let meta = match conf.meta_key_expr {
374 Some(meta) => Some(meta?),
375 None => None,
376 };
377 tracing::debug!("Create AdvancedPublisher{{key_expr: {}}}", &key_expr);
378
379 let publisher = conf
380 .session
381 .declare_publisher(key_expr.clone())
382 .encoding(conf.encoding)
383 .allowed_destination(conf.destination)
384 .reliability(conf.reliability)
385 .congestion_control(conf.congestion_control)
386 .priority(conf.priority)
387 .express(conf.is_express)
388 .wait()?;
389 let id = publisher.id();
390 let suffix = KE_ADV_PREFIX / KE_PUB / &id.zid().into_keyexpr();
391 let suffix = match conf.sequencing {
392 Sequencing::SequenceNumber => {
393 suffix / &KeyExpr::try_from(id.eid().to_string()).unwrap()
394 }
395 _ => suffix / KE_UHLC,
396 };
397 let suffix = match meta {
398 Some(meta) => suffix / &meta,
399 _ => suffix / KE_EMPTY,
401 };
402
403 let seqnum = match conf.sequencing {
404 Sequencing::SequenceNumber => Some(Arc::new(AtomicU32::new(0))),
405 Sequencing::Timestamp => {
406 if conf.session.hlc().is_none() {
407 bail!(
408 "Cannot create AdvancedPublisher {} with Sequencing::Timestamp: \
409 the 'timestamping' setting must be enabled in the Zenoh configuration.",
410 key_expr,
411 )
412 }
413 None
414 }
415 _ => None,
416 };
417
418 let cache = conf
419 .history
420 .map(|h| {
421 AdvancedCacheBuilder::new(conf.session, Ok(key_expr.clone()))
422 .history(h)
423 .queryable_suffix(&suffix)
424 .wait()
425 })
426 .transpose()?;
427
428 let token = if conf.liveliness {
429 tracing::debug!(
430 "AdvancedPublisher{{key_expr: {}}}: Declare liveliness token {}",
431 key_expr,
432 &key_expr / &suffix,
433 );
434 Some(
435 conf.session
436 .liveliness()
437 .declare_token(&key_expr / &suffix)
438 .wait()?,
439 )
440 } else {
441 None
442 };
443
444 let state_publisher = if let Some((period, sporadic)) =
445 conf.miss_config.as_ref().and_then(|c| c.state_publisher)
446 {
447 if let Some(seqnum) = seqnum.as_ref() {
448 tracing::debug!(
449 "AdvancedPublisher{{key_expr: {}}}: Enable {}heartbeat on {} with period {:?}",
450 key_expr,
451 if sporadic { "sporadic " } else { "" },
452 &key_expr / &suffix,
453 period
454 );
455 let seqnum = seqnum.clone();
456 if !sporadic {
457 let publisher = conf.session.declare_publisher(&key_expr / &suffix).wait()?;
458 Some(TerminatableTask::spawn_abortable(
459 ZRuntime::Net,
460 async move {
461 loop {
462 tokio::time::sleep(period).await;
463 let seqnum = seqnum.load(Ordering::Relaxed);
464 if seqnum > 0 {
465 let _ = publisher.put(z_serialize(&(seqnum - 1))).await;
466 }
467 }
468 },
469 ))
470 } else {
471 let mut last_seqnum = 0;
472 let publisher = conf
473 .session
474 .declare_publisher(&key_expr / &suffix)
475 .congestion_control(CongestionControl::Block)
476 .wait()?;
477 Some(TerminatableTask::spawn_abortable(
478 ZRuntime::Net,
479 async move {
480 loop {
481 tokio::time::sleep(period).await;
482 let seqnum = seqnum.load(Ordering::Relaxed);
483 if seqnum > last_seqnum {
484 let _ = publisher.put(z_serialize(&(seqnum - 1))).await;
485 last_seqnum = seqnum;
486 }
487 }
488 },
489 ))
490 }
491 } else {
492 None
493 }
494 } else {
495 None
496 };
497
498 Ok(AdvancedPublisher {
499 publisher,
500 seqnum,
501 cache,
502 _token: token,
503 _state_publisher: state_publisher,
504 })
505 }
506
507 #[zenoh_macros::unstable]
511 pub fn id(&self) -> EntityGlobalId {
512 self.publisher.id()
513 }
514
515 #[inline]
519 #[zenoh_macros::unstable]
520 pub fn key_expr(&self) -> &KeyExpr<'a> {
521 self.publisher.key_expr()
522 }
523
524 #[inline]
528 #[zenoh_macros::unstable]
529 pub fn encoding(&self) -> &Encoding {
530 self.publisher.encoding()
531 }
532
533 #[inline]
537 #[zenoh_macros::unstable]
538 pub fn congestion_control(&self) -> CongestionControl {
539 self.publisher.congestion_control()
540 }
541
542 #[inline]
546 #[zenoh_macros::unstable]
547 pub fn priority(&self) -> Priority {
548 self.publisher.priority()
549 }
550
551 #[inline]
567 #[zenoh_macros::unstable]
568 pub fn put<IntoZBytes>(&self, payload: IntoZBytes) -> AdvancedPublisherPutBuilder<'_>
569 where
570 IntoZBytes: Into<ZBytes>,
571 {
572 let mut builder = self.publisher.put(payload);
573 if let Some(seqnum) = &self.seqnum {
574 let info = Some(SourceInfo::new(
575 self.publisher.id(),
576 seqnum.fetch_add(1, Ordering::Relaxed),
577 ));
578 tracing::trace!(
579 "AdvancedPublisher{{key_expr: {}}}: Put data with {:?}",
580 self.publisher.key_expr(),
581 info
582 );
583 builder = builder.source_info(info);
584 }
585 if let Some(hlc) = self.publisher.session().hlc() {
586 builder = builder.timestamp(hlc.new_timestamp());
587 }
588 AdvancedPublisherPutBuilder {
589 builder,
590 cache: self.cache.as_ref(),
591 }
592 }
593
594 #[zenoh_macros::unstable]
610 pub fn delete(&self) -> AdvancedPublisherDeleteBuilder<'_> {
611 let mut builder = self.publisher.delete();
612 if let Some(seqnum) = &self.seqnum {
613 builder = builder.source_info(Some(SourceInfo::new(
614 self.publisher.id(),
615 seqnum.fetch_add(1, Ordering::Relaxed),
616 )));
617 }
618 if let Some(hlc) = self.publisher.session().hlc() {
619 builder = builder.timestamp(hlc.new_timestamp());
620 }
621 AdvancedPublisherDeleteBuilder {
622 builder,
623 cache: self.cache.as_ref(),
624 }
625 }
626
627 #[zenoh_macros::unstable]
650 pub fn matching_status(&self) -> impl Resolve<ZResult<zenoh::matching::MatchingStatus>> + '_ {
651 self.publisher.matching_status()
652 }
653
654 #[zenoh_macros::unstable]
680 pub fn matching_listener(
681 &self,
682 ) -> zenoh::matching::MatchingListenerBuilder<'_, zenoh::handlers::DefaultHandler> {
683 self.publisher.matching_listener()
684 }
685
686 #[zenoh_macros::unstable]
702 pub fn undeclare(self) -> PublisherUndeclaration<'a> {
703 tracing::debug!(
704 "AdvancedPublisher{{key_expr: {}}}: Undeclare",
705 self.key_expr()
706 );
707 self.publisher.undeclare()
708 }
709}
710
711#[zenoh_macros::unstable]
712pub type AdvancedPublisherPutBuilder<'a> = AdvancedPublicationBuilder<'a, PublicationBuilderPut>;
713#[zenoh_macros::unstable]
714pub type AdvancedPublisherDeleteBuilder<'a> =
715 AdvancedPublicationBuilder<'a, PublicationBuilderDelete>;
716
717#[must_use = "Resolvables do nothing unless you resolve them using `.await` or `zenoh::Wait::wait`"]
718#[derive(Clone)]
719#[zenoh_macros::unstable]
720pub struct AdvancedPublicationBuilder<'a, P> {
721 pub(crate) builder: PublicationBuilder<&'a Publisher<'a>, P>,
722 pub(crate) cache: Option<&'a AdvancedCache>,
723}
724
725#[zenoh_macros::unstable]
726impl<P> fmt::Debug for AdvancedPublicationBuilder<'_, P> {
727 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
728 f.debug_struct("AdvancedPublicationBuilder")
729 .field("builder", &"..")
730 .field("has_cache", &self.cache.is_some())
731 .finish()
732 }
733}
734
735#[zenoh_macros::internal_trait]
736#[zenoh_macros::unstable]
737impl EncodingBuilderTrait for AdvancedPublicationBuilder<'_, PublicationBuilderPut> {
738 #[zenoh_macros::unstable]
740 fn encoding<T: Into<Encoding>>(self, encoding: T) -> Self {
741 Self {
742 builder: self.builder.encoding(encoding),
743 ..self
744 }
745 }
746}
747
748#[zenoh_macros::unstable]
749impl<P> AdvancedPublicationBuilder<'_, P> {
750 pub fn attachment<TA: Into<OptionZBytes>>(self, attachment: TA) -> Self {
755 let attachment: OptionZBytes = attachment.into();
756 Self {
757 builder: self.builder.attachment(attachment),
758 ..self
759 }
760 }
761}
762
763#[zenoh_macros::internal_trait]
764#[zenoh_macros::unstable]
765impl<P> TimestampBuilderTrait for AdvancedPublicationBuilder<'_, P> {
766 #[zenoh_macros::unstable]
768 fn timestamp<TS: Into<Option<uhlc::Timestamp>>>(self, timestamp: TS) -> Self {
769 Self {
770 builder: self.builder.timestamp(timestamp),
771 ..self
772 }
773 }
774}
775
776#[zenoh_macros::internal_trait]
777#[zenoh_macros::unstable]
778impl<P> TimestampInstrumentationBuilderTrait for AdvancedPublicationBuilder<'_, P> {
779 #[zenoh_macros::unstable]
780 fn timestamp_instrumentation<
781 TS: Into<Option<zenoh::timestamp_stack::TimestampInstrumentation>>,
782 >(
783 self,
784 instrumentation: TS,
785 ) -> Self {
786 Self {
787 builder: self.builder.timestamp_instrumentation(instrumentation),
788 ..self
789 }
790 }
791}
792
793#[zenoh_macros::unstable]
794impl<P> Resolvable for AdvancedPublicationBuilder<'_, P> {
795 type To = ZResult<()>;
796}
797
798#[zenoh_macros::unstable]
799impl Wait for AdvancedPublisherPutBuilder<'_> {
800 #[inline]
801 #[zenoh_macros::unstable]
802 fn wait(self) -> <Self as Resolvable>::To {
803 if let Some(cache) = self.cache {
804 cache.cache_sample(zenoh::sample::Sample::from(&self.builder));
805 }
806 self.builder.wait()
807 }
808}
809
810#[zenoh_macros::unstable]
811impl Wait for AdvancedPublisherDeleteBuilder<'_> {
812 #[inline]
813 #[zenoh_macros::unstable]
814 fn wait(self) -> <Self as Resolvable>::To {
815 if let Some(cache) = self.cache {
816 cache.cache_sample(zenoh::sample::Sample::from(&self.builder));
817 }
818 self.builder.wait()
819 }
820}
821
822#[zenoh_macros::unstable]
823impl IntoFuture for AdvancedPublisherPutBuilder<'_> {
824 type Output = <Self as Resolvable>::To;
825 type IntoFuture = Ready<<Self as Resolvable>::To>;
826
827 #[zenoh_macros::unstable]
828 fn into_future(self) -> Self::IntoFuture {
829 std::future::ready(self.wait())
830 }
831}
832
833#[zenoh_macros::unstable]
834impl IntoFuture for AdvancedPublisherDeleteBuilder<'_> {
835 type Output = <Self as Resolvable>::To;
836 type IntoFuture = Ready<<Self as Resolvable>::To>;
837
838 #[zenoh_macros::unstable]
839 fn into_future(self) -> Self::IntoFuture {
840 std::future::ready(self.wait())
841 }
842}