1pub mod error;
49pub mod config;
50pub mod address;
51
52pub mod dpt;
54pub mod cemi;
55pub mod frame;
56
57pub mod group;
59pub mod group_cache;
60pub mod tunnel;
61pub mod filter;
62pub mod heartbeat;
63pub mod error_tracker;
64pub mod metrics;
65pub mod diagnostics;
66pub mod server;
67pub mod device;
68pub mod factory;
69
70pub use error::{KnxError, KnxResult};
72pub use config::{
73 KnxServerConfig, KnxDeviceConfig, GroupObjectConfig,
74 GroupObjectFlagsConfig, TunnelConfig,
75};
76pub use address::{GroupAddress, IndividualAddress, AddressType, GroupAddressRange};
77pub use dpt::{
78 DptId, DptValue, DptCodec, DptRegistry, BoxedDptCodec,
79 encode_dpt9, decode_dpt9,
80};
81pub use cemi::{CemiFrame, MessageCode, Priority, Apci, AdditionalInfo, AdditionalInfoType};
82pub use frame::{KnxFrame, FrameBuilder, KnxNetIpHeader, ServiceType, Hpai, DibDeviceInfo, SupportedServiceFamilies, ServiceFamily};
83pub use group::{GroupObject, GroupObjectTable, GroupObjectFlags, GroupEvent};
84pub use tunnel::{
85 TunnelConnection,
86 ConnectRequest, ConnectResponse, ConnectionType,
87 TunnellingRequest, TunnellingAck,
88 ConnectionStateRequest, ConnectionStateResponse,
89 DisconnectRequest, DisconnectResponse,
90 SequenceTracker, ReceivedValidation, AckValidation, SequenceStatsSnapshot,
92 AckWaiter, AckResult, AckMessage, AckWaiterStatsSnapshot,
93 TunnelFsm, TunnelState, TunnelErrorReason, FsmStatsSnapshot,
94};
95pub use config::TunnelBehaviorConfig;
96pub use filter::{
97 FilterChain, FilterChainConfig, FilterChainStats, FilterChainStatsSnapshot,
98 FilterDirection, FilterResult, FrameEnvelope,
99 PaceFilter, PaceFilterConfig, PaceState, PaceFilterStats, PaceFilterStatsSnapshot,
100 QueueFilter, QueueFilterConfig, QueuePriority, QueueFilterStats, QueueFilterStatsSnapshot,
101 RetryFilter, RetryFilterConfig, CircuitBreakerState, RetryFilterStats, RetryFilterStatsSnapshot,
102};
103pub use heartbeat::{
104 HeartbeatAction, HeartbeatSchedule, HeartbeatScheduler, HeartbeatSchedulerConfig,
105 HeartbeatStatsSnapshot,
106};
107pub use group_cache::{
108 GroupValueCache, GroupValueCacheConfig, CacheEntry, CacheEntryInfo,
109 CacheStatsSnapshot, UpdateSource,
110};
111pub use error_tracker::{
112 SendErrorTracker, SendErrorTrackerConfig, ErrorCategory, TrackingResult,
113 ChannelErrorSummary, TrackerStatsSnapshot,
114};
115pub use metrics::{
116 KnxMetricsSnapshot, KnxMetricsCollector, ConnectionMetricsSnapshot,
117};
118pub use diagnostics::{
119 KnxDiagnostics, DiagnosticResult, DiagnosticSeverity, DiagnosticRule,
120 DiagnosticConfig,
121};
122pub use server::{KnxServer, ServerState, ServerEvent, ConnectionManager};
123pub use device::{KnxDevice, KnxDeviceBuilder};
124pub use factory::{KnxDeviceFactory, register_knx_factory};
125
126pub const KNXNETIP_VERSION: u8 = 0x10;
128
129pub const DEFAULT_PORT: u16 = 3671;
131
132pub const DEFAULT_MULTICAST_ADDR: &str = "224.0.23.12";
134
135pub mod prelude {
137 pub use crate::{
138 KnxError, KnxResult,
140 GroupAddress, IndividualAddress,
142 DptId, DptValue, DptCodec, DptRegistry,
144 KnxDevice, KnxDeviceBuilder,
146 KnxServer, KnxServerConfig,
147 GroupObject, GroupObjectTable, GroupEvent,
149 KnxDeviceConfig, GroupObjectConfig,
151 };
152}
153
154#[cfg(test)]
155mod tests {
156 use super::*;
157 use crate::cemi::MessageCode;
158
159 #[test]
160 fn test_group_address_parsing() {
161 let addr: GroupAddress = "1/2/3".parse().unwrap();
162 assert_eq!(addr.main(), 1);
163 assert_eq!(addr.middle(), 2);
164 assert_eq!(addr.sub(), 3);
165 assert_eq!(addr.to_string(), "1/2/3");
166 }
167
168 #[test]
169 fn test_individual_address_parsing() {
170 let addr: IndividualAddress = "1.2.3".parse().unwrap();
171 assert_eq!(addr.area(), 1);
172 assert_eq!(addr.line(), 2);
173 assert_eq!(addr.device(), 3);
174 assert_eq!(addr.to_string(), "1.2.3");
175 }
176
177 #[test]
178 fn test_dpt_id_parsing() {
179 let dpt: DptId = "1.001".parse().unwrap();
180 assert_eq!(dpt.main, 1);
181 assert_eq!(dpt.sub, 1);
182 assert!(dpt.to_string().contains("1.001"));
184 }
185
186 #[test]
187 fn test_dpt_registry_lookup() {
188 let registry = DptRegistry::new();
189
190 assert!(registry.get(&DptId::new(1, 1)).is_some());
192 assert!(registry.get(&DptId::new(5, 1)).is_some());
193 assert!(registry.get(&DptId::new(9, 1)).is_some());
194 }
195
196 #[test]
197 fn test_service_type_round_trip() {
198 let service = ServiceType::TunnellingRequest;
199 let code: u16 = service.into();
200 let parsed = ServiceType::try_from(code).unwrap();
201 assert_eq!(parsed, service);
202 }
203
204 #[test]
205 fn test_cemi_frame_creation() {
206 let frame = CemiFrame::group_value_write(
207 IndividualAddress::new(1, 1, 1),
208 GroupAddress::three_level(1, 0, 1),
209 vec![0x00], );
211
212 assert_eq!(frame.message_code, MessageCode::LDataInd);
213 }
214
215 #[test]
216 fn test_group_address_range() {
217 let range = GroupAddressRange::new(
218 GroupAddress::three_level(1, 0, 0),
219 GroupAddress::three_level(1, 0, 255),
220 );
221
222 assert!(range.contains(&GroupAddress::three_level(1, 0, 100)));
223 assert!(!range.contains(&GroupAddress::three_level(1, 1, 0)));
224 }
225
226 #[test]
227 fn test_dpt_value_encoding() {
228 let registry = DptRegistry::new();
230 let codec = registry.get(&DptId::new(1, 1)).unwrap();
231
232 let encoded = codec.encode(&DptValue::Bool(true)).unwrap();
233 assert_eq!(encoded, vec![0x01]);
234
235 let decoded = codec.decode(&[0x01]).unwrap();
236 assert_eq!(decoded, DptValue::Bool(true));
237 }
238
239 #[test]
240 fn test_knx_device_builder() {
241 let device = KnxDeviceBuilder::new("test-device", "Test Device")
242 .individual_address(IndividualAddress::new(1, 1, 1))
243 .description("A test device")
244 .build()
245 .unwrap();
246
247 assert_eq!(device.id(), "test-device");
248 assert_eq!(device.individual_address().to_string(), "1.1.1");
249 }
250
251 #[test]
252 fn test_factory_protocol() {
253 use mabi_core::{DeviceFactory, Protocol};
254
255 let factory = KnxDeviceFactory::new();
256 assert_eq!(factory.protocol(), Protocol::KnxIp);
257 }
258}