1use super::segmentation_context::{ConfirmedWait, SegmentedRequestLimits};
2use super::*;
3use bacnet_encoding::apdu::MINIMUM_MESSAGE_SIZE;
4
5enum LengthBoundedBy {
12 DiscoveredPeer(u16),
14 LocalConfig(u16),
16 Transport(u16),
18}
19
20impl core::fmt::Display for LengthBoundedBy {
21 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
22 match self {
23 Self::DiscoveredPeer(v) => {
24 write!(f, "the peer's advertised Max APDU Length Accepted of {v}")
25 }
26 Self::LocalConfig(v) => write!(f, "this client's configured maximum of {v}"),
27 Self::Transport(v) => write!(f, "the transport's limit of {v}"),
28 }
29 }
30}
31
32fn check_transmittable_length(peer_or_local: LengthBoundedBy, transport: u16) -> Result<(), Error> {
52 let advertised = match peer_or_local {
53 LengthBoundedBy::DiscoveredPeer(v) | LengthBoundedBy::LocalConfig(v) => v,
54 LengthBoundedBy::Transport(v) => v,
55 };
56 let combined = advertised.min(transport);
57 if combined >= MINIMUM_MESSAGE_SIZE {
58 return Ok(());
59 }
60 let binding = if transport < advertised {
61 LengthBoundedBy::Transport(transport)
62 } else {
63 peer_or_local
64 };
65 Err(Error::Encoding(format!(
66 "maximum transmittable length {combined} is below the {MINIMUM_MESSAGE_SIZE}-octet \
67 MinimumMessageSize every BACnet device accepts (Clause 5.2.1.2); the binding limit is \
68 {binding}"
69 )))
70}
71
72impl<T: TransportPort + 'static> BACnetClient<T> {
73 pub async fn confirmed_request(
88 &self,
89 destination_mac: &[u8],
90 service_choice: ConfirmedServiceChoice,
91 service_data: &[u8],
92 ) -> Result<Bytes, Error> {
93 self.confirmed_request_inner(
94 ConfirmedTarget::Local {
95 mac: destination_mac,
96 },
97 service_choice,
98 service_data,
99 )
100 .await
101 }
102
103 pub async fn confirmed_request_routed(
124 &self,
125 router_mac: &[u8],
126 dest_network: u16,
127 dest_mac: &[u8],
128 service_choice: ConfirmedServiceChoice,
129 service_data: &[u8],
130 ) -> Result<Bytes, Error> {
131 self.confirmed_request_inner(
132 ConfirmedTarget::Routed {
133 router_mac,
134 dest_network,
135 dest_mac,
136 },
137 service_choice,
138 service_data,
139 )
140 .await
141 }
142
143 pub(super) async fn confirmed_request_inner(
144 &self,
145 target: ConfirmedTarget<'_>,
146 service_choice: ConfirmedServiceChoice,
147 service_data: &[u8],
148 ) -> Result<Bytes, Error> {
149 let named = target;
156 if let ConfirmedTarget::Routed {
157 dest_network,
158 dest_mac,
159 ..
160 } = named
161 {
162 check_routed_unicast(dest_network, dest_mac.len())?;
163 }
164 let target = named.localized(self.network.local_network_number().get());
165 if let ConfirmedTarget::Local { mac } = target
168 && self.network.transport().is_group_destination(mac)
169 {
170 return Err(Error::Encoding(
171 "a confirmed request goes to one device, not to a broadcast or group \
172 address (a local broadcast carries only an UNCONFIRMED_REQUEST APDU, \
173 Clause 6.3)"
174 .into(),
175 ));
176 }
177 let routed_forwarded_npci_len = match target {
178 ConfirmedTarget::Local { .. } => None,
179 ConfirmedTarget::Routed { dest_mac, .. } => {
180 Some(forwarded_npci_len(dest_mac.len(), self.local_mac.len())?)
181 }
182 };
183 let _pace = self.pacer.wait(pacing::PaceKey::of(target)).await;
188 let path_lease = match target {
191 ConfirmedTarget::Local { .. } => None,
192 ConfirmedTarget::Routed {
193 router_mac,
194 dest_network,
195 ..
196 } => Some(
197 self.routed_path_limits
198 .acquire(router_mac, dest_network)
199 .await?,
200 ),
201 };
202 let routed_path_max_apdu = path_lease
203 .as_ref()
204 .zip(routed_forwarded_npci_len)
205 .map(|(lease, npci_len)| lease.max_apdu(npci_len));
206 let transaction_peer = target.transaction_peer();
207 let tsm_mac = transaction_peer.tsm_mac;
208 let unsegmented_apdu_size = 4 + service_data.len();
209 let target_transport_max_apdu = self.target_transport_max_apdu_length(target);
210
211 let (peer_max_apdu, remote_max_segments, advertised, peer_segmentation) = {
212 let dt = self.device_table.lock().await;
213 let (device, peer_segmentation) = match named {
218 ConfirmedTarget::Local { mac } => {
219 (dt.get_by_mac(mac), dt.local_peer_segmentation(mac))
220 }
221 ConfirmedTarget::Routed {
222 dest_network,
223 dest_mac,
224 ..
225 } => (
226 dt.get_by_network_address(dest_network, dest_mac),
227 dt.routed_peer_segmentation(dest_network, dest_mac),
228 ),
229 };
230 let max_apdu = device
231 .map(|d| u16::try_from(d.max_apdu_length).unwrap_or(u16::MAX))
232 .unwrap_or(self.config.max_apdu_length);
233 let max_seg = device.and_then(|d| d.max_segments_accepted);
234 let advertised = if device.is_some() {
235 LengthBoundedBy::DiscoveredPeer(max_apdu)
236 } else {
237 LengthBoundedBy::LocalConfig(max_apdu)
238 };
239 (max_apdu, max_seg, advertised, peer_segmentation)
240 };
241 check_transmittable_length(advertised, target_transport_max_apdu)?;
242 if routed_path_max_apdu.is_some_and(|path_max_apdu| {
243 path_max_apdu < MINIMUM_MESSAGE_SIZE
244 && path_max_apdu <= peer_max_apdu.min(target_transport_max_apdu)
245 }) {
246 let ConfirmedTarget::Routed { dest_network, .. } = target else {
247 unreachable!("a routed path limit exists only for a routed target")
248 };
249 return Err(Error::RoutedPathTooLong { dnet: dest_network });
250 }
251 let remote_max_apdu = peer_max_apdu
252 .min(target_transport_max_apdu)
253 .min(routed_path_max_apdu.unwrap_or(u16::MAX));
254 if unsegmented_apdu_size > remote_max_apdu as usize {
255 if let Some(crate::discovery::PeerSegmentation::Authoritative(capability)) =
262 peer_segmentation
263 && matches!(
264 capability,
265 bacnet_types::enums::Segmentation::NONE
266 | bacnet_types::enums::Segmentation::TRANSMIT
267 )
268 {
269 return Err(bacnet_types::error::SegmentationError::PeerCannotReceive {
270 capability,
271 }
272 .into());
273 }
274 return self
275 .segmented_confirmed_request(
276 target,
277 service_choice,
278 service_data,
279 SegmentedRequestLimits {
280 remote_max_apdu,
281 remote_max_segments,
282 routed_forwarded_npci_len,
283 routed_path_lease: path_lease.as_ref(),
284 },
285 )
286 .await;
287 }
288
289 let advertised_max_apdu = self.advertised_max_apdu_length_for_target(target)?;
290 let (invoke_id, registration) = {
291 let mut tsm = self.tsm.lock().await;
292 tsm.register_coordinated_transaction_with_progress(
293 tsm_mac.clone(),
294 transaction_peer.canonical,
295 service_choice,
296 false,
297 )
298 .map_err(|error| Error::Encoding(error.to_string()))?
299 };
300
301 let owner = registration.owner.clone();
302 let mut guard = TransactionGuard::new(
303 Arc::clone(&self.tsm),
304 self.cleanup_tx.clone(),
305 tsm_mac.clone(),
306 invoke_id,
307 owner.clone(),
308 None,
309 );
310
311 let pdu = Apdu::ConfirmedRequest(ConfirmedRequestPdu {
312 segmented: false,
313 more_follows: false,
314 segmented_response_accepted: self.config.segmented_response_accepted,
315 max_segments: self.config.max_segments,
316 max_apdu_length: advertised_max_apdu,
317 invoke_id,
318 sequence_number: None,
319 proposed_window_size: None,
320 service_choice,
321 service_request: Bytes::copy_from_slice(service_data),
322 });
323
324 let mut buf = BytesMut::with_capacity(6 + service_data.len());
325 encode_apdu(&mut buf, &pdu)?;
326
327 if let Some(forwarded_npci_len) = routed_forwarded_npci_len {
328 self.routed_path_limits.install_active(
329 target,
330 tsm_mac.clone(),
331 invoke_id,
332 owner.clone(),
333 forwarded_npci_len,
334 self.network.network_control_ingress_sequence(),
335 );
336 }
337 if !self.routed_path_limits.authorize_attempt(
338 target,
339 &tsm_mac,
340 invoke_id,
341 &owner,
342 buf.len(),
343 ) {
344 guard.mark_completed();
345 self.tsm
346 .lock()
347 .await
348 .cancel_transaction_for_owner(&tsm_mac, invoke_id, &owner);
349 self.enqueue_transaction_cleanup(&tsm_mac, invoke_id, &owner, false, None);
350 return Err(Error::Encoding(
351 "routed send lost its active path authorization".into(),
352 ));
353 }
354
355 if let Err(e) = self.send_confirmed_target_apdu(target, &buf).await {
356 guard.mark_completed();
357 let mut tsm = self.tsm.lock().await;
358 tsm.cancel_transaction_for_owner(&tsm_mac, invoke_id, &owner);
359 drop(tsm);
360 self.enqueue_transaction_cleanup(&tsm_mac, invoke_id, &owner, false, None);
361 return Err(e);
362 }
363
364 let response = self
365 .wait_for_confirmed_response(
366 ConfirmedWait {
367 target,
368 tsm_mac: &tsm_mac,
369 invoke_id,
370 owner: &owner,
371 retry_apdu: Some(&buf),
372 },
373 registration.response,
374 registration.progress,
375 )
376 .await;
377 if response.is_ok()
378 && let Some(lease) = path_lease.as_ref()
379 {
380 lease.mark_terminal_observed();
381 }
382 guard.mark_completed();
383 response.and_then(Self::confirmed_response_result)
384 }
385
386 pub(super) async fn wait_for_confirmed_response(
389 &self,
390 wait_for: ConfirmedWait<'_>,
391 mut response_rx: oneshot::Receiver<TsmResponse>,
392 mut progress_rx: ProgressReceiver,
393 ) -> Result<TsmResponse, Error> {
394 let ConfirmedWait {
395 target,
396 tsm_mac,
397 invoke_id,
398 owner,
399 retry_apdu,
400 } = wait_for;
401 let (request_timeout, segment_timeout, max_retries) = {
402 let tsm = self.tsm.lock().await;
403 let config = tsm.config();
404 (
405 Duration::from_millis(config.apdu_timeout_ms),
406 Duration::from_millis(config.apdu_segment_timeout_ms.saturating_mul(4)),
407 config.apdu_retries,
408 )
409 };
410 let mut retries_sent = 0u16;
411 let mut progress = progress_rx.borrow_and_update();
412
413 loop {
414 let wait = match progress {
415 TransactionProgress::AwaitingResponse => request_timeout,
416 TransactionProgress::SegmentedResponse { .. } => segment_timeout,
417 };
418 let timer = tokio::time::sleep(wait);
419 tokio::pin!(timer);
420
421 tokio::select! {
422 response = &mut response_rx => {
423 return response.map_err(|_| Error::Encoding("TSM response channel closed".into()));
424 }
425 () = progress_rx.changed() => {
428 progress = progress_rx.borrow_and_update();
429 }
430 _ = &mut timer => {
431 match progress {
432 TransactionProgress::AwaitingResponse => {
433 let retry = retry_apdu.is_some()
434 && retries_sent < u16::from(max_retries);
435 let mut tsm = self.tsm.lock().await;
436 match tsm.expire_request_timer(tsm_mac, invoke_id, owner, !retry) {
437 RequestTimerExpiration::Retry => {
438 let Some(retry_apdu) = retry_apdu else {
442 tsm.cancel_transaction_for_owner(tsm_mac, invoke_id, owner);
443 drop(tsm);
444 self.enqueue_transaction_cleanup(
445 tsm_mac,
446 invoke_id,
447 owner,
448 false,
449 None,
450 );
451 return Err(Error::Timeout(request_timeout));
452 };
453 retries_sent += 1;
454 drop(tsm);
455 if !self.routed_path_limits.authorize_attempt(
456 target,
457 tsm_mac,
458 invoke_id,
459 owner,
460 retry_apdu.len(),
461 ) {
462 return (&mut response_rx).await.map_err(|_| {
463 Error::Encoding("TSM response channel closed".into())
464 });
465 }
466 let send_result = self
467 .send_confirmed_target_apdu(target, retry_apdu)
468 .await;
469 if let Err(error) = send_result {
470 let mut tsm = self.tsm.lock().await;
471 match tsm.expire_request_timer(tsm_mac, invoke_id, owner, true) {
472 RequestTimerExpiration::SegmentedResponse { generation } => {
473 progress = TransactionProgress::SegmentedResponse { generation };
474 continue;
475 }
476 RequestTimerExpiration::NoTransaction => {
477 drop(tsm);
478 return (&mut response_rx)
479 .await
480 .map_err(|_| Error::Encoding("TSM response channel closed".into()));
481 }
482 RequestTimerExpiration::TimedOut => {
483 drop(tsm);
484 self.enqueue_transaction_cleanup(
485 tsm_mac,
486 invoke_id,
487 owner,
488 false,
489 None,
490 );
491 return Err(error);
492 }
493 RequestTimerExpiration::Retry => unreachable!(
494 "final retry-send failure disposition cannot authorize another retry"
495 ),
496 }
497 }
498 debug!(
499 invoke_id,
500 attempt = retries_sent,
501 max_retries,
502 "APDU timeout, retrying confirmed request"
503 );
504 }
505 RequestTimerExpiration::SegmentedResponse { generation } => {
506 progress = TransactionProgress::SegmentedResponse { generation };
507 }
508 RequestTimerExpiration::TimedOut => {
509 drop(tsm);
510 self.enqueue_transaction_cleanup(
511 tsm_mac,
512 invoke_id,
513 owner,
514 false,
515 None,
516 );
517 return Err(Error::abort(bacnet_types::enums::AbortReason::TSM_TIMEOUT));
518 }
519 RequestTimerExpiration::NoTransaction => {
520 drop(tsm);
521 return (&mut response_rx)
522 .await
523 .map_err(|_| Error::Encoding("TSM response channel closed".into()));
524 }
525 }
526 }
527 TransactionProgress::SegmentedResponse { generation } => {
528 let mut tsm = self.tsm.lock().await;
529 match tsm.expire_segment_timer(tsm_mac, invoke_id, owner, generation) {
530 SegmentTimerExpiration::Activity { generation } => {
531 progress = TransactionProgress::SegmentedResponse { generation };
532 }
533 SegmentTimerExpiration::AwaitingResponse => {
534 progress = TransactionProgress::AwaitingResponse;
535 }
536 SegmentTimerExpiration::TimedOut => {
537 drop(tsm);
538 #[cfg(test)]
539 self.segmented_cleanup.pause_if_enabled().await;
540 self.enqueue_transaction_cleanup(
541 tsm_mac,
542 invoke_id,
543 owner,
544 false,
545 None,
546 );
547 return Err(Error::abort(bacnet_types::enums::AbortReason::TSM_TIMEOUT));
548 }
549 SegmentTimerExpiration::NoTransaction => {
550 drop(tsm);
551 return (&mut response_rx)
552 .await
553 .map_err(|_| Error::Encoding("TSM response channel closed".into()));
554 }
555 }
556 }
557 }
558 }
559 }
560 }
561 }
562
563 pub(super) fn enqueue_transaction_cleanup(
564 &self,
565 mac: &MacAddr,
566 invoke_id: u8,
567 owner: &TransactionOwner,
568 cancel_tsm: bool,
569 seg_ack_sender: Option<mpsc::Sender<SegmentAckPdu>>,
570 ) {
571 let _ = self.cleanup_tx.send(TransactionCleanup {
572 mac: mac.clone(),
573 invoke_id,
574 owner: owner.clone(),
575 cancel_tsm,
576 seg_ack_sender,
577 });
578 }
579
580 pub(super) fn confirmed_response_result(response: TsmResponse) -> Result<Bytes, Error> {
581 super::confirmed_response_result(response)
582 }
583
584 pub(super) async fn send_confirmed_target_apdu(
585 &self,
586 target: ConfirmedTarget<'_>,
587 apdu: &[u8],
588 ) -> Result<(), Error> {
589 match target {
590 ConfirmedTarget::Local { mac } => {
591 self.network
592 .send_apdu(apdu, mac, true, NetworkPriority::Normal)
593 .await
594 }
595 ConfirmedTarget::Routed {
596 router_mac,
597 dest_network,
598 dest_mac,
599 } => {
600 self.network
601 .send_apdu_routed(
602 apdu,
603 dest_network,
604 dest_mac,
605 router_mac,
606 true,
607 NetworkPriority::Normal,
608 )
609 .await
610 }
611 }
612 }
613
614 pub(super) fn target_transport_max_apdu_length(&self, target: ConfirmedTarget<'_>) -> u16 {
615 self.network
616 .transport()
617 .egress_apdu_limit()
618 .saturating_sub(target.additional_npdu_header_len())
619 }
620
621 pub(super) fn advertised_max_apdu_length_for_target(
622 &self,
623 target: ConfirmedTarget<'_>,
624 ) -> Result<u16, Error> {
625 cap_max_apdu_to_transport(
626 self.config.max_apdu_length,
627 self.target_transport_max_apdu_length(target),
628 )
629 }
630
631 pub async fn unconfirmed_request(
633 &self,
634 destination_mac: &[u8],
635 service_choice: UnconfirmedServiceChoice,
636 service_data: &[u8],
637 ) -> Result<(), Error> {
638 let pdu = Apdu::UnconfirmedRequest(bacnet_encoding::apdu::UnconfirmedRequest {
639 service_choice,
640 service_request: Bytes::copy_from_slice(service_data),
641 });
642
643 let mut buf = BytesMut::with_capacity(2 + service_data.len());
644 encode_apdu(&mut buf, &pdu)?;
645
646 self.network
647 .send_apdu(&buf, destination_mac, false, NetworkPriority::Normal)
648 .await
649 }
650
651 pub async fn broadcast_unconfirmed(
653 &self,
654 service_choice: UnconfirmedServiceChoice,
655 service_data: &[u8],
656 ) -> Result<(), Error> {
657 let pdu = Apdu::UnconfirmedRequest(bacnet_encoding::apdu::UnconfirmedRequest {
658 service_choice,
659 service_request: Bytes::copy_from_slice(service_data),
660 });
661
662 let mut buf = BytesMut::with_capacity(2 + service_data.len());
663 encode_apdu(&mut buf, &pdu)?;
664
665 self.network
666 .broadcast_apdu(&buf, false, NetworkPriority::Normal)
667 .await
668 }
669
670 pub async fn broadcast_global_unconfirmed(
672 &self,
673 service_choice: UnconfirmedServiceChoice,
674 service_data: &[u8],
675 ) -> Result<(), Error> {
676 let pdu = Apdu::UnconfirmedRequest(bacnet_encoding::apdu::UnconfirmedRequest {
677 service_choice,
678 service_request: Bytes::copy_from_slice(service_data),
679 });
680
681 let mut buf = BytesMut::with_capacity(2 + service_data.len());
682 encode_apdu(&mut buf, &pdu)?;
683
684 self.network
685 .broadcast_global_apdu(&buf, false, NetworkPriority::Normal)
686 .await
687 }
688
689 pub async fn broadcast_network_unconfirmed(
695 &self,
696 service_choice: UnconfirmedServiceChoice,
697 service_data: &[u8],
698 dest_network: u16,
699 ) -> Result<(), Error> {
700 let pdu = Apdu::UnconfirmedRequest(bacnet_encoding::apdu::UnconfirmedRequest {
701 service_choice,
702 service_request: Bytes::copy_from_slice(service_data),
703 });
704
705 let mut buf = BytesMut::with_capacity(2 + service_data.len());
706 encode_apdu(&mut buf, &pdu)?;
707
708 if self.network.local_network_number().get() == Some(dest_network) {
709 return self
710 .network
711 .broadcast_apdu(&buf, false, NetworkPriority::Normal)
712 .await;
713 }
714 self.network
715 .broadcast_to_network(&buf, dest_network, false, NetworkPriority::Normal)
716 .await
717 }
718}
719
720#[cfg(test)]
721#[path = "requests_tests.rs"]
722mod transmittable_length_tests;