use knust::protocol::knxip::{KnxIpFrame, ServiceType, TunnellingAck, TunnellingRequest};
use knust::transport::{SequenceValidationResult, Tunnel};
use std::net::SocketAddr;
#[allow(clippy::too_many_lines)]
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
env_logger::init();
println!("🔢 KNX/IP Sequence Number Validation Demo");
println!("==========================================\n");
let gateway_addr: SocketAddr = "192.168.1.100:3671".parse()?;
let connection = Tunnel::new_udp(gateway_addr);
println!("📡 Created tunneling connection to {gateway_addr}");
println!(
"Initial expected sequence number: {}\n",
connection.expected_sequence()
);
println!("✅ Testing valid sequence progression:");
for i in 0..5 {
let result = connection.validate_sequence_number(i);
match result {
SequenceValidationResult::Valid => {
println!(
" Sequence {}: ✓ Valid (expected: {})",
i,
connection.expected_sequence() - 1
);
}
_ => {
println!(" Sequence {i}: ❌ Unexpected result: {result:?}");
}
}
}
println!("\n🔄 Testing duplicate frame detection:");
let result = connection.validate_sequence_number(4);
match result {
SequenceValidationResult::Duplicate => {
println!(" Sequence 4 (duplicate): ✓ Correctly identified as duplicate");
}
_ => {
println!(" Sequence 4 (duplicate): ❌ Unexpected result: {result:?}");
}
}
println!("\n❌ Testing invalid sequence detection:");
let result = connection.validate_sequence_number(7);
match result {
SequenceValidationResult::Invalid { expected, received } => {
println!(" Sequence 7: ✓ Correctly identified as invalid");
println!(" Expected: {expected}, Received: {received}");
}
_ => {
println!(" Sequence 7: ❌ Unexpected result: {result:?}");
}
}
println!("\n🔄 Continuing with correct sequence:");
for i in 5..7 {
let result = connection.validate_sequence_number(i);
match result {
SequenceValidationResult::Valid => {
println!(" Sequence {i}: ✓ Valid");
}
_ => {
println!(" Sequence {i}: ❌ Unexpected result: {result:?}");
}
}
}
println!("\n🔄 Testing sequence wraparound (255 -> 0):");
connection.reset_sequence();
for i in 0..256u16 {
#[allow(clippy::cast_possible_truncation)]
let seq = i as u8;
let result = connection.validate_sequence_number(seq);
if i >= 253 || i <= 2 {
match result {
SequenceValidationResult::Valid => {
println!(
" Sequence {}: ✓ Valid (expected next: {})",
seq,
connection.expected_sequence()
);
}
_ => {
println!(" Sequence {seq}: ❌ Unexpected result: {result:?}");
}
}
}
}
println!("\n📦 Testing TunnellingRequest and TunnellingAck structures:");
let sample_cemi = vec![
0x29, 0x00, 0xBC, 0xE0, 0x11, 0x01, 0x01, 0x01, 0x00, 0x81, ];
let request = TunnellingRequest::new(1, 42, sample_cemi.clone());
println!(" Created TunnellingRequest:");
println!(" Channel ID: {}", request.communication_channel_id);
println!(" Sequence: {}", request.sequence_counter);
println!(" CEMI length: {} bytes", request.raw_cemi.len());
let request_body = request.serialize();
let knx_frame = KnxIpFrame::new(ServiceType::TunnellingRequest, request_body);
let frame_data = knx_frame.serialize();
println!(" Total frame size: {} bytes", frame_data.len());
let ack_ok = TunnellingAck::new_ok(1, 42);
let ack_error = TunnellingAck::new_sequence_error(1, 42);
println!("\n TunnellingAck (OK):");
println!(" Channel ID: {}", ack_ok.communication_channel_id);
println!(" Sequence: {}", ack_ok.sequence_counter);
println!(
" Status: 0x{:02X} ({})",
ack_ok.status_code,
ack_ok.status_description()
);
println!(" Success: {}", ack_ok.is_success());
println!("\n TunnellingAck (Sequence Error):");
println!(" Channel ID: {}", ack_error.communication_channel_id);
println!(" Sequence: {}", ack_error.sequence_counter);
println!(
" Status: 0x{:02X} ({})",
ack_error.status_code,
ack_error.status_description()
);
println!(" Success: {}", ack_error.is_success());
println!("\n🎯 Sequence validation ensures:");
println!(" • Frames are processed in correct order");
println!(" • Duplicate frames are acknowledged but not processed twice");
println!(" • Lost frames are detected and appropriate errors are sent");
println!(" • Sequence numbers wrap around correctly (255 -> 0)");
println!(" • Protocol compliance with KNX/IP specification");
println!("\n✨ Demo completed successfully!");
Ok(())
}