bacnet_server/server/
rpm_budget.rs1use super::*;
3
4#[derive(Clone, Copy, Debug, PartialEq, Eq)]
10pub struct ReadPropertyMultipleBudget {
11 pub max_result_elements: usize,
18 pub max_service_ack_bytes: usize,
20}
21
22impl Default for ReadPropertyMultipleBudget {
23 fn default() -> Self {
24 Self {
25 max_result_elements: 256,
26 max_service_ack_bytes: 16384,
27 }
28 }
29}
30
31impl ReadPropertyMultipleBudget {
32 pub fn validate(&self) -> Result<(), Error> {
34 for (name, value) in [
35 ("rpm_max_result_elements", self.max_result_elements),
36 ("rpm_max_service_ack_bytes", self.max_service_ack_bytes),
37 ] {
38 if value == 0 {
39 return Err(Error::Encoding(format!("{name} must be positive")));
40 }
41 }
42 Ok(())
43 }
44}
45
46impl<T: TransportPort + 'static> ServerBuilder<T> {
47 pub fn read_property_multiple_budget(mut self, budget: ReadPropertyMultipleBudget) -> Self {
49 self.config.read_property_multiple_budget = budget;
50 self
51 }
52}
53
54impl BipServerBuilder {
55 pub fn read_property_multiple_budget(mut self, budget: ReadPropertyMultipleBudget) -> Self {
57 self.config.read_property_multiple_budget = budget;
58 self
59 }
60}
61
62#[cfg(feature = "sc-tls")]
63impl ScServerBuilder {
64 pub fn read_property_multiple_budget(mut self, budget: ReadPropertyMultipleBudget) -> Self {
66 self.config.read_property_multiple_budget = budget;
67 self
68 }
69}
70
71#[cfg(test)]
72mod tests {
73 use super::*;
74 use crate::server::test_transport::TestTransport;
75
76 #[tokio::test]
77 async fn rpm_defaults_builders_and_validation_before_start() {
78 let defaults = ReadPropertyMultipleBudget::default();
79 assert_eq!(
80 (defaults.max_result_elements, defaults.max_service_ack_bytes),
81 (256, 16384)
82 );
83 assert_eq!(
84 ServerConfig::default().read_property_multiple_budget,
85 defaults
86 );
87 assert!(format!("{:?}", ServerConfig::default()).contains("read_property_multiple_budget"));
88 ReadPropertyMultipleBudget {
89 max_result_elements: usize::MAX,
90 max_service_ack_bytes: usize::MAX,
91 }
92 .validate()
93 .unwrap();
94 for budget in [
95 ReadPropertyMultipleBudget {
96 max_result_elements: 0,
97 ..defaults
98 },
99 ReadPropertyMultipleBudget {
100 max_service_ack_bytes: 0,
101 ..defaults
102 },
103 ] {
104 let direct = BACnetServer::start(
105 ServerConfig {
106 read_property_multiple_budget: budget,
107 ..Default::default()
108 },
109 ObjectDatabase::new(),
110 TestTransport::never_start(),
111 )
112 .await;
113 let generic = BACnetServer::generic_builder()
114 .transport(TestTransport::never_start())
115 .read_property_multiple_budget(budget)
116 .build()
117 .await;
118 let bip = BACnetServer::bip_builder()
119 .port(0)
120 .read_property_multiple_budget(budget)
121 .build()
122 .await;
123 for error in [direct.err(), generic.err(), bip.err()] {
124 assert!(matches!(error, Some(Error::Encoding(m)) if m.contains("rpm_max_")));
125 }
126 assert!(
127 bacnet_objects::DeviceIdentity::default()
128 .with_max_apdu(3)
129 .is_err()
130 );
131 }
132 }
133}