use bacnet_rs::util::debug;
fn main() -> Result<(), Box<dyn std::error::Error>> {
println!("=== BACnet Debug Formatting Utilities Demo ===\n");
demo_property_value_formatting()?;
demo_service_choice_formatting()?;
demo_error_formatting()?;
demo_protocol_structure_analysis()?;
demo_annotated_hex_dumps()?;
demo_complete_packet_analysis()?;
println!("\n=== Debug Formatting Demo Complete ===");
Ok(())
}
fn demo_property_value_formatting() -> Result<(), Box<dyn std::error::Error>> {
println!("1. Property Value Formatting");
println!("=============================");
let boolean_true = &[0x11, 0x01];
let boolean_false = &[0x11, 0x00];
println!(
"Boolean true: {}",
debug::format_property_value(boolean_true)
);
println!(
"Boolean false: {}",
debug::format_property_value(boolean_false)
);
let real_42 = &[0x44, 0x42, 0x28, 0x00, 0x00]; let real_temp = &[0x44, 0x41, 0xB6, 0x66, 0x66]; println!("Real 42.0: {}", debug::format_property_value(real_42));
println!("Real 22.8: {}", debug::format_property_value(real_temp));
let uint_small = &[0x21, 0x05]; let uint_large = &[0x24, 0x00, 0x01, 0x00, 0x00]; println!(
"UInt 5: {}",
debug::format_property_value(uint_small)
);
println!(
"UInt 65536: {}",
debug::format_property_value(uint_large)
);
let ascii_string = &[0x75, 0x05, 0x00, b'H', b'e', b'l', b'l', b'o'];
println!(
"ASCII String: {}",
debug::format_property_value(ascii_string)
);
let utf16_string = &[
0x75, 0x08, 0x04, 0x00, 0x54, 0x00, 0x65, 0x00, 0x73, 0x00, 0x74,
];
println!(
"UTF-16 String: {}",
debug::format_property_value(utf16_string)
);
let enum_units = &[0x91, 0x3E]; println!(
"Enumerated: {}",
debug::format_property_value(enum_units)
);
let analog_input_1 = &[0xC4, 0x00, 0x00, 0x00, 0x01]; let device_1234 = &[0xC4, 0x02, 0x00, 0x04, 0xD2]; println!(
"AI 1: {}",
debug::format_property_value(analog_input_1)
);
println!(
"Device 1234: {}",
debug::format_property_value(device_1234)
);
let date_data = &[0xA1, 0x7C, 0x03, 0x0F, 0x05]; let time_data = &[0xB1, 0x0E, 0x1E, 0x2D, 0x32]; println!("Date: {}", debug::format_property_value(date_data));
println!("Time: {}", debug::format_property_value(time_data));
let octet_data = &[0x83, 0xAA, 0xBB, 0xCC]; println!(
"Octet String: {}",
debug::format_property_value(octet_data)
);
Ok(())
}
fn demo_service_choice_formatting() -> Result<(), Box<dyn std::error::Error>> {
println!("\n2. Service Choice Formatting");
println!("==============================");
let services = [
(12, "Read Property"),
(14, "Read Property Multiple"),
(15, "Write Property"),
(16, "Write Property Multiple"),
(10, "Create Object"),
(11, "Delete Object"),
(6, "Atomic Read File"),
(7, "Atomic Write File"),
(28, "Subscribe COV"),
(20, "Reinitialize Device"),
];
for (code, description) in services {
println!(
"{:2}: {} -> {}",
code,
description,
debug::format_service_choice(code)
);
}
println!(
"99: Unknown Service -> {}",
debug::format_service_choice(99)
);
Ok(())
}
fn demo_error_formatting() -> Result<(), Box<dyn std::error::Error>> {
println!("\n3. Error Formatting");
println!("====================");
let errors = [
(0, 5, "Device busy"),
(1, 2, "Object not found"),
(2, 9, "Property not found"),
(2, 10, "Property value out of range"),
(2, 16, "Write access denied"),
(3, 1, "Out of memory"),
(4, 1, "Authentication required"),
(5, 1, "Service not supported"),
(6, 2, "VT session terminated"),
(7, 1, "Communication timeout"),
];
for (class, code, description) in errors {
println!(
"{} -> {}",
description,
debug::format_bacnet_error(class, code)
);
}
Ok(())
}
fn demo_protocol_structure_analysis() -> Result<(), Box<dyn std::error::Error>> {
println!("\n4. Protocol Structure Analysis");
println!("===============================");
println!("BVLL (BACnet Virtual Link Layer):");
let bvll_data = &[
0x81, 0x0A, 0x00, 0x10, 0x01, 0x00, 0x30, 0x00, 0x0C, 0x02, 0x00, 0x13, 0xB7, 0x19, 0x4D, 0x00, ];
println!("{}", debug::format_bvll_structure(bvll_data));
println!("NPDU (Network Protocol Data Unit):");
let npdu_data = &bvll_data[4..]; println!("{}", debug::format_npdu_structure(npdu_data));
println!("APDU (Application Protocol Data Unit):");
let confirmed_request = &[
0x00, 0x05, 0x0C, 0x0C, 0x02, 0x00, 0x00, 0x01, 0x19, 0x55, ];
println!("Confirmed Request (Read Property):");
println!("{}", debug::format_apdu_structure(confirmed_request));
let complex_ack = &[
0x30, 0x05, 0x0C, 0x0C, 0x02, 0x00, 0x00, 0x01, 0x19, 0x55, 0x3E, 0x44, 0x42, 0x28, 0x00, 0x00, 0x3F, ];
println!("\nComplex ACK (Read Property Response):");
println!("{}", debug::format_apdu_structure(complex_ack));
let error_response = &[
0x50, 0x05, 0x0C, 0x91, 0x01, 0x91, 0x02, ];
println!("\nError Response:");
println!("{}", debug::format_apdu_structure(error_response));
Ok(())
}
fn demo_annotated_hex_dumps() -> Result<(), Box<dyn std::error::Error>> {
println!("\n5. Annotated Hex Dumps");
println!("=======================");
let packet_data = &[
0x81, 0x0A, 0x00, 0x18, 0x01, 0x00, 0x30, 0x00, 0x00, 0x05, 0x0C, 0x0C, 0x02, 0x00, 0x00, 0x01, 0x19, 0x55, 0x3E, 0x44, 0x42, 0x28, 0x00, 0x00, 0x3F, ];
let annotations = vec![
(0, "BVLL Type (0x81 = BACnet/IP)".to_string()),
(
1,
"BVLL Function (0x0A = Original-Unicast-NPDU)".to_string(),
),
(2, "BVLL Length (24 bytes)".to_string()),
(4, "NPDU Version".to_string()),
(5, "NPDU Control".to_string()),
(8, "APDU Type (Confirmed Request)".to_string()),
(9, "Max Segments & Max APDU".to_string()),
(10, "Invoke ID & Service Choice".to_string()),
(11, "Object ID Tag".to_string()),
(16, "Property ID Tag".to_string()),
(18, "Property Value Opening Tag".to_string()),
(19, "Real Value Tag".to_string()),
(24, "Property Value Closing Tag".to_string()),
];
println!("Complete BACnet Packet with Annotations:");
println!("{}", debug::annotated_hex_dump(packet_data, &annotations));
Ok(())
}
fn demo_complete_packet_analysis() -> Result<(), Box<dyn std::error::Error>> {
println!("\n6. Complete Packet Analysis");
println!("============================");
println!("Scenario: Reading temperature from Analog Input 1 on Device 1234\n");
let request_packet = &[
0x81, 0x0A, 0x00, 0x11, 0x01, 0x00, 0x30, 0x00, 0x00, 0x05, 0x0C, 0x0C, 0x02, 0x00, 0x00, 0x01, 0x19, 0x55, ];
println!("REQUEST PACKET ANALYSIS:");
println!("========================");
analyze_complete_packet(request_packet);
let response_packet = &[
0x81, 0x0A, 0x00, 0x18, 0x01, 0x00, 0x30, 0x00, 0x30, 0x05, 0x0C, 0x0C, 0x02, 0x00, 0x00, 0x01, 0x19, 0x55, 0x3E, 0x44, 0x41, 0xB6, 0x66, 0x66, 0x3F, ];
println!("\nRESPONSE PACKET ANALYSIS:");
println!("=========================");
analyze_complete_packet(response_packet);
let error_packet = &[
0x81, 0x0A, 0x00, 0x0B, 0x01, 0x00, 0x30, 0x00, 0x50, 0x05, 0x0C, 0x91, 0x01, 0x91, 0x02, ];
println!("\nERROR PACKET ANALYSIS:");
println!("======================");
analyze_complete_packet(error_packet);
Ok(())
}
fn analyze_complete_packet(packet: &[u8]) {
println!("Raw Packet ({} bytes):", packet.len());
println!("{}", bacnet_rs::util::hex_dump(packet, " "));
if packet.len() >= 4 {
println!("{}", debug::format_bvll_structure(packet));
if packet.len() > 4 {
let npdu_data = &packet[4..];
println!("{}", debug::format_npdu_structure(npdu_data));
let mut apdu_start = 2; if npdu_data.len() > apdu_start {
let control = npdu_data[1];
if (control & 0x20) != 0 {
apdu_start += 2; if npdu_data.len() > apdu_start {
let addr_len = npdu_data[apdu_start] as usize;
apdu_start += 1 + addr_len;
}
}
if (control & 0x08) != 0 {
apdu_start += 2; if npdu_data.len() > apdu_start {
let addr_len = npdu_data[apdu_start] as usize;
apdu_start += 1 + addr_len;
}
}
if npdu_data.len() > apdu_start {
apdu_start += 1; }
if npdu_data.len() > apdu_start {
let apdu_data = &npdu_data[apdu_start..];
println!("{}", debug::format_apdu_structure(apdu_data));
analyze_property_data(apdu_data);
}
}
}
}
println!("{}", "-".repeat(60));
}
fn analyze_property_data(apdu_data: &[u8]) {
if apdu_data.is_empty() {
return;
}
let pdu_type = (apdu_data[0] >> 4) & 0x0F;
if pdu_type == 3 && apdu_data.len() > 10 {
println!("Property Data Analysis:");
let mut pos = 3;
if apdu_data.len() > pos + 5 && apdu_data[pos] == 0x0C {
pos += 5;
}
if apdu_data.len() > pos + 2 && apdu_data[pos] == 0x19 {
pos += 2;
}
if apdu_data.len() > pos && apdu_data[pos] == 0x3E {
pos += 1;
let mut value_end = pos;
while value_end < apdu_data.len() && apdu_data[value_end] != 0x3F {
value_end += 1;
}
if value_end > pos {
let property_value = &apdu_data[pos..value_end];
println!(
" Property Value: {}",
debug::format_property_value(property_value)
);
}
}
}
}