bacnet_network/layer_issuance.rs
1use super::*;
2
3/// One APDU handed to a local-issuance send.
4#[derive(Debug, Clone, Copy)]
5pub struct IssuedApdu<'a> {
6 /// Encoded APDU to wrap in an NPDU.
7 pub apdu: &'a [u8],
8 /// Local data-link destination.
9 pub next_hop: &'a [u8],
10 /// Routed NPDU destination (DNET/DADR), when the peer is behind a router.
11 pub destination: Option<&'a NpduAddress>,
12 /// Whether the NPDU sets the data-expecting-reply flag.
13 pub expecting_reply: bool,
14 /// Network priority of the NPDU.
15 pub priority: NetworkPriority,
16}
17
18impl<T: TransportPort> NetworkLayer<T> {
19 /// Seal original-direct response issuance before joining owned server work.
20 /// Queued writes observe this irreversible seal; writes already started
21 /// remain bounded but cannot be retracted. Network stop/drop also seals.
22 pub fn seal_responses(&self) {
23 self.response_scope.seal();
24 }
25}
26
27impl<T: TransportPort + 'static> NetworkLayer<T> {
28 /// Issue one local unicast operation after encoding its complete NPDU.
29 ///
30 /// `destination` selects routed NPDU addressing; `next_hop` is always the
31 /// local data-link destination. The synchronous callback runs exactly once
32 /// after encoding succeeds, immediately before calling/polling the transport
33 /// send. Constructing this future does not call it. Encoding failure never
34 /// calls it; later transport failure does not undo issuance. This boundary
35 /// does not establish physical emission or remote receipt. A `destination`
36 /// on network 0 or 0xFFFF is an encoding failure: this unicast reaches
37 /// one next hop, and a global broadcast goes out through
38 /// [`NetworkLayer::broadcast_global_apdu`] instead (#1380). A
39 /// `destination` with no DADR is a remote broadcast, which carries only
40 /// an Unconfirmed-Request APDU (Clause 6.3), so any other APDU to it is
41 /// an encoding failure too (#1479). With no `destination`, a `next_hop`
42 /// that is the link's broadcast or another group address is a local
43 /// broadcast as well, but this send takes `next_hop` as given: unlike
44 /// [`NetworkLayer::send_apdu`], it doesn't send an Unconfirmed-Request
45 /// for one as the link's broadcast, and nothing checks it, since a
46 /// response goes back to the MAC its request came from (B/IP refuses a
47 /// group address there, #1677).
48 ///
49 /// Used by server transaction owners whose lifetime ends at local issuance,
50 /// independently of the transport future's eventual Result.
51 pub async fn send_apdu_on_issuance(
52 &self,
53 apdu: &[u8],
54 next_hop: &[u8],
55 destination: Option<&NpduAddress>,
56 expecting_reply: bool,
57 priority: NetworkPriority,
58 on_issuance: impl FnOnce() + Send,
59 ) -> Result<(), Error> {
60 self.send_response_apdu_on_issuance(
61 IssuedApdu {
62 apdu,
63 next_hop,
64 destination,
65 expecting_reply,
66 priority,
67 },
68 &crate::response_route::ResponseRoute::unverified(),
69 on_issuance,
70 )
71 .await
72 }
73
74 /// Encode and issue a response using its immutable ingress route.
75 ///
76 /// Direct responses use only the matching original direct socket. A
77 /// missing, invalid or stale capability cannot fall back to ordinary unicast.
78 /// The callback has the same local issuance contract as
79 /// [`Self::send_apdu_on_issuance`]; neither issuance nor success proves receipt.
80 pub async fn send_response_apdu_on_issuance(
81 &self,
82 issued: IssuedApdu<'_>,
83 route: &crate::response_route::ResponseRoute,
84 on_issuance: impl FnOnce() + Send,
85 ) -> Result<(), Error> {
86 let IssuedApdu {
87 apdu,
88 next_hop,
89 destination,
90 expecting_reply,
91 priority,
92 } = issued;
93 crate::response_route::check_response_destination(destination)?;
94 if destination.is_some_and(|destination| destination.mac_address.is_empty()) {
95 check_broadcast_apdu(apdu, "a destination with no DADR (a remote broadcast)")?;
96 }
97 let buf = crate::response_route::encode_checked_response_npdu(
98 apdu,
99 destination,
100 expecting_reply,
101 priority,
102 )?;
103 let direct = route.direct()?;
104 on_issuance();
105 if let Some(direct) = direct {
106 return direct.send(&buf, &self.response_scope).await;
107 }
108 self.transport
109 .send_unicast_with_data_attributes(&buf, next_hop, &[])
110 .await
111 }
112}