use canlink_hal::isotp::{AddressingMode, FrameSize, IsoTpChannel, IsoTpConfig, StMin};
use canlink_hal::{BackendConfig, CanBackend, CanMessage};
use canlink_mock::{MockBackend, MockConfig};
use std::time::Duration;
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
println!("=== CANLink HAL - ISO-TP Transfer Protocol Example ===\n");
basic_isotp_example().await?;
println!("\n{}\n", "=".repeat(60));
uds_diagnostic_example().await?;
println!("\n{}\n", "=".repeat(60));
configuration_examples()?;
println!("\n=== All examples completed successfully! ===");
Ok(())
}
async fn basic_isotp_example() -> Result<(), Box<dyn std::error::Error>> {
println!("--- Basic ISO-TP Example ---\n");
println!("1. Creating Mock backend with preset ISO-TP responses...");
let preset_messages = vec![
CanMessage::new_standard(0x7E8, &[0x02, 0x50, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00])?,
CanMessage::new_standard(0x7E8, &[0x06, 0x62, 0xF1, 0x90, 0x57, 0x44, 0x42, 0x00])?,
];
let mock_config = MockConfig::with_preset_messages(preset_messages);
let mut backend = MockBackend::with_config(mock_config);
backend.initialize(&BackendConfig::new("mock"))?;
backend.open_channel(0)?;
println!(" ✓ Backend created with preset responses\n");
println!("2. Configuring ISO-TP channel...");
let config = IsoTpConfig::builder()
.tx_id(0x7E0) .rx_id(0x7E8) .block_size(0) .st_min(StMin::Milliseconds(10)) .timeout(Duration::from_millis(1000)) .build()?;
println!(" TX ID: 0x{:03X}", config.tx_id);
println!(" RX ID: 0x{:03X}", config.rx_id);
println!(" Block Size: {}", config.block_size);
println!(" STmin: {:?}", config.st_min);
println!(" Timeout: {:?}", config.rx_timeout);
println!();
println!("3. Creating ISO-TP channel...");
let mut channel = IsoTpChannel::new(backend, config)?;
println!(" ✓ Channel created\n");
println!("4. Sending DiagnosticSessionControl request (Single Frame)...");
let request = vec![0x10, 0x01]; println!(" Request: {:02X?}", request);
channel.send(&request).await?;
println!(" ✓ Request sent\n");
println!("5. Receiving response...");
let response = channel.receive().await?;
println!(" Response: {:02X?}", response);
if response.len() >= 2 && response[0] == 0x50 {
println!(" ✓ Positive response received (Session activated)\n");
}
println!("6. Sending ReadDataByIdentifier request...");
let request2 = vec![0x22, 0xF1, 0x90]; println!(" Request: {:02X?}", request2);
channel.send(&request2).await?;
println!(" ✓ Request sent\n");
println!("7. Receiving VIN response...");
let response2 = channel.receive().await?;
println!(" Response: {:02X?}", response2);
if response2.len() >= 4 && response2[0] == 0x62 {
println!(" ✓ Positive response received\n");
}
println!("--- Basic ISO-TP Example Complete ---");
Ok(())
}
async fn uds_diagnostic_example() -> Result<(), Box<dyn std::error::Error>> {
println!("--- UDS Diagnostic Communication Example ---\n");
let preset_messages = vec![
CanMessage::new_standard(0x7E8, &[0x02, 0x7E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00])?,
CanMessage::new_standard(0x7E8, &[0x04, 0x67, 0x01, 0x12, 0x34, 0x00, 0x00, 0x00])?,
CanMessage::new_standard(0x7E8, &[0x03, 0x7F, 0x27, 0x22, 0x00, 0x00, 0x00, 0x00])?,
];
let mock_config = MockConfig::with_preset_messages(preset_messages);
let mut backend = MockBackend::with_config(mock_config);
backend.initialize(&BackendConfig::new("mock"))?;
backend.open_channel(0)?;
let config = IsoTpConfig::builder()
.tx_id(0x7E0)
.rx_id(0x7E8)
.timeout(Duration::from_millis(2000))
.build()?;
let mut channel = IsoTpChannel::new(backend, config)?;
println!("1. Sending TesterPresent...");
let tester_present = vec![0x3E, 0x00];
channel.send(&tester_present).await?;
let response = channel.receive().await?;
print_uds_response("TesterPresent", &response);
println!("\n2. Sending SecurityAccess (Request Seed)...");
let security_seed = vec![0x27, 0x01];
channel.send(&security_seed).await?;
let response = channel.receive().await?;
print_uds_response("SecurityAccess Seed", &response);
println!("\n3. Sending SecurityAccess (Send Key)...");
let security_key = vec![0x27, 0x02, 0xAB, 0xCD];
channel.send(&security_key).await?;
let response = channel.receive().await?;
print_uds_response("SecurityAccess Key", &response);
println!("\n--- UDS Diagnostic Example Complete ---");
Ok(())
}
fn print_uds_response(service_name: &str, response: &[u8]) {
print!(" {} Response: {:02X?} - ", service_name, response);
if response.is_empty() {
println!("Empty response");
return;
}
match response[0] {
0x7F => {
if response.len() >= 3 {
let service = response[1];
let nrc = response[2];
let nrc_name = match nrc {
0x10 => "generalReject",
0x12 => "subFunctionNotSupported",
0x13 => "incorrectMessageLengthOrInvalidFormat",
0x14 => "responseTooLong",
0x21 => "busyRepeatRequest",
0x22 => "conditionsNotCorrect",
0x24 => "requestSequenceError",
0x25 => "noResponseFromSubnetComponent",
0x26 => "failurePreventsExecutionOfRequestedAction",
0x31 => "requestOutOfRange",
0x33 => "securityAccessDenied",
0x35 => "invalidKey",
0x36 => "exceededNumberOfAttempts",
0x37 => "requiredTimeDelayNotExpired",
0x72 => "generalProgrammingFailure",
0x78 => "requestCorrectlyReceivedResponsePending",
_ => "unknown",
};
println!(
"Negative Response (Service=0x{:02X}, NRC=0x{:02X} {})",
service, nrc, nrc_name
);
} else {
println!("Negative Response (malformed)");
}
}
sid if sid >= 0x40 => {
let original_sid = sid - 0x40;
println!("Positive Response (Service=0x{:02X})", original_sid);
}
_ => {
println!("Unknown response format");
}
}
}
fn configuration_examples() -> Result<(), Box<dyn std::error::Error>> {
println!("--- ISO-TP Configuration Examples ---\n");
println!("1. Standard OBD-II Configuration:");
let obd_config = IsoTpConfig::builder()
.tx_id(0x7DF) .rx_id(0x7E8) .timeout(Duration::from_millis(1000))
.build()?;
println!(
" TX: 0x{:03X}, RX: 0x{:03X}",
obd_config.tx_id, obd_config.rx_id
);
println!("\n2. Extended CAN ID Configuration:");
let extended_config = IsoTpConfig::builder()
.tx_id(0x18DA00F1) .rx_id(0x18DAF100)
.extended_ids(true)
.timeout(Duration::from_millis(2000))
.build()?;
println!(
" TX: 0x{:08X} (extended), RX: 0x{:08X} (extended)",
extended_config.tx_id, extended_config.rx_id
);
println!("\n3. High-Speed Transfer Configuration:");
let fast_config = IsoTpConfig::builder()
.tx_id(0x7E0)
.rx_id(0x7E8)
.block_size(0) .st_min(StMin::Microseconds(100)) .timeout(Duration::from_millis(5000))
.build()?;
println!(" Block Size: {} (unlimited)", fast_config.block_size);
println!(" STmin: {:?}", fast_config.st_min);
println!("\n4. Conservative Configuration (for older ECUs):");
let slow_config = IsoTpConfig::builder()
.tx_id(0x7E0)
.rx_id(0x7E8)
.block_size(8) .st_min(StMin::Milliseconds(25)) .timeout(Duration::from_millis(10000))
.max_wait_count(20) .build()?;
println!(" Block Size: {}", slow_config.block_size);
println!(" STmin: {:?}", slow_config.st_min);
println!(" Max Wait Count: {}", slow_config.max_wait_count);
println!("\n5. CAN-FD Configuration:");
let fd_config = IsoTpConfig::builder()
.tx_id(0x7E0)
.rx_id(0x7E8)
.frame_size(FrameSize::Fd64) .build()?;
println!(" Frame Size: {:?}", fd_config.frame_size);
println!("\n6. Extended Addressing Mode:");
let ext_addr_config = IsoTpConfig::builder()
.tx_id(0x7E0)
.rx_id(0x7E8)
.addressing_mode(AddressingMode::Extended {
target_address: 0x01,
})
.build()?;
println!(" Addressing Mode: {:?}", ext_addr_config.addressing_mode);
println!("\n7. Mixed Addressing Mode:");
let mixed_config = IsoTpConfig::builder()
.tx_id(0x7E0)
.rx_id(0x7E8)
.addressing_mode(AddressingMode::Mixed {
address_extension: 0xF1,
})
.build()?;
println!(" Addressing Mode: {:?}", mixed_config.addressing_mode);
println!("\n8. Custom Padding Configuration:");
let padded_config = IsoTpConfig::builder()
.tx_id(0x7E0)
.rx_id(0x7E8)
.padding_enabled(true)
.padding_byte(0xAA) .build()?;
println!(" Padding Enabled: {}", padded_config.padding_enabled);
println!(" Padding Byte: 0x{:02X}", padded_config.padding_byte);
println!("\n--- Configuration Examples Complete ---");
Ok(())
}