1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
//! Recipient_List writes, the cap on how many destinations the list holds
//! (#1098), and the same cap on the lists routing accepts (#1124).
use decode_destination;
use ;
use ;
use Error;
use PropertyValue;
use cratecommon;
/// The most destinations a Notification Class Recipient_List holds (#1098).
///
/// A site usually routes a class to a few front ends, operator workstations
/// or gateways, and the AE-CRL-B building block (Annex K.2.25) asks for room
/// for at least 8, so 32 leaves ample headroom. The bound also keeps the list
/// small on the wire. A destination naming a device encodes in at most 27
/// octets, and one naming an address with a MAC of up to 6 octets in at most
/// 35, so a full list of either kind takes at most 1,120 octets and a
/// ReadProperty of it fits B/IP's 1476-octet APDU unsegmented. An address
/// MAC is at most [`BACnetAddress::MAX_MAC_LEN`] octets (#1124), so no
/// destination takes more than 47 octets and no full list more than 1,504.
///
/// A write that would leave more destinations fails with RESOURCES /
/// NO_SPACE_TO_WRITE_PROPERTY, naming the first destination past the cap;
/// AddListElement reports that as NO_SPACE_TO_ADD_LIST_ELEMENT at the request
/// element that brought it (Clause 15.1.1.3). `add_destination` refuses the
/// same way, without the element.
///
/// Routing holds every Notification Class to the same bound, so each event
/// transition fans out to at most this many notifications per class (#1124).
/// Only a custom NOTIFICATION_CLASS object can serve a longer list, and the
/// transition then goes to none of it: see
/// [`RecipientLookupOutcome::RecipientListTooLong`](super::RecipientLookupOutcome::RecipientListTooLong).
pub const MAX_RECIPIENT_LIST_DESTINATIONS: usize = 32;
/// The refusal of a destination past [`MAX_RECIPIENT_LIST_DESTINATIONS`].
pub
/// The refusal of a written list's destination at `index` (from 0), the first
/// past the cap, named so ChangeList-Error can report it (#1048).
/// Why a framed Recipient_List was not taken.
pub
/// Decode a framed Recipient_List (a BACnetLIST of BACnetDestination, Clause
/// 12.21) of at most [`MAX_RECIPIENT_LIST_DESTINATIONS`] destinations, all or
/// nothing. Destinations are checked in order, so the first one that fails to
/// decode, or the first past the cap, decides the error. A destination past
/// the cap is refused before it is decoded, as Calendar's Date_List does, so
/// no list costs more than one destination past the cap to refuse.
///
/// The write path and routing share this decoder, so the object never stores
/// a list routing would refuse.
pub
/// Decode a whole-list Recipient_List write. Only the framed wire form
/// (`ApplicationData`) is a Recipient_List value (#1125); anything else, a
/// malformed destination included, fails with PROPERTY / INVALID_DATA_TYPE.
pub
/// Refuse a destination given through `add_destination` that a framed write
/// could not carry: an address whose MAC is longer than
/// [`BACnetAddress::MAX_MAC_LEN`] octets fails, as the write would, with
/// PROPERTY / INVALID_DATA_TYPE (#1124). Stored, it would make the class's
/// own list undecodable and routing would drop every destination with it.
pub