From 01ae840390d82a97c0f976c3f086a348f10b49a4 Mon Sep 17 00:00:00 2001 From: Rainer Zaiser Date: Tue, 10 Sep 2024 12:48:02 +0200 Subject: [PATCH] Added integration test for server reinit --- README.md | 2 +- examples/hello_xcp/src/main.rs | 5 +- examples/multi_thread_demo/src/main.rs | 6 +- examples/point_cloud_demo/src/main.rs | 3 +- examples/protobuf_demo/src/main.rs | 5 +- examples/rayon_demo/src/main.rs | 3 +- examples/scoped_threads/src/main.rs | 8 +- examples/single_thread_demo/src/main.rs | 4 +- examples/tokio_demo/src/main.rs | 2 +- src/main.rs | 57 +-- src/reg/registry.rs | 51 +-- src/xcp.rs | 13 +- tests/multi_thread.rs | 3 +- tests/single_thread.rs | 79 +++- tests/test_executor/mod.rs | 513 ++++++++++++------------ tests/tokio_multi_thread.rs | 9 +- tests/tokio_single_thread.rs | 4 +- xcp_client/src/main.rs | 30 +- xcp_lite.a2l | 127 ++++-- xcplib/main_cfg.h | 15 +- xcplib/src/dbg_print.h | 4 +- xcplib/src/platform.c | 10 +- xcplib/src/xcpEthServer.c | 13 +- xcplib/src/xcpEthTl.c | 4 +- xcplib/xcpAppl.c | 6 +- 25 files changed, 550 insertions(+), 426 deletions(-) diff --git a/README.md b/README.md index aafb718..8c24df2 100644 --- a/README.md +++ b/README.md @@ -170,7 +170,7 @@ fn task(calseg: CalSeg) { fn main() { // Initialize XCP driver singleton, the transport layer UDP and enable the automatic A2L writer and upload - let xcp = XcpBuilder::new("xcp_lite").set_log_level(XcpLogLevel::Warn).enable_a2l(true).set_epk("???") + let xcp = XcpBuilder::new("my_module_name").set_log_level(XcpLogLevel::Warn).set_epk("MY_EPK") .start_server(XcpTransportLayer::Udp,[127, 0, 0, 1],5555, 1400,).unwrap(); // Create a calibration parameter set named "calsseg" (struct CalSeg, a MEMORY_SEGMENT in A2L and CANape) diff --git a/examples/hello_xcp/src/main.rs b/examples/hello_xcp/src/main.rs index 46fd3d7..2af2bdf 100644 --- a/examples/hello_xcp/src/main.rs +++ b/examples/hello_xcp/src/main.rs @@ -36,11 +36,10 @@ const CAL_PAGE: CalPage = CalPage { min: 5, max: 10, delay: 100000 }; fn main() { println!("XCP Demo"); - env_logger::Builder::new().filter_level(log::LevelFilter::Debug).init(); + env_logger::Builder::new().filter_level(log::LevelFilter::Info).init(); let xcp = XcpBuilder::new("xcp_demo") .set_log_level(XcpLogLevel::Debug) - .enable_a2l(true) .set_epk("EPK_") .start_server(XcpTransportLayer::Udp, [127, 0, 0, 1], 5555) .unwrap(); @@ -64,6 +63,6 @@ fn main() { calseg.sync(); - //Xcp::get().write_a2l(); + xcp.write_a2l(); } } diff --git a/examples/multi_thread_demo/src/main.rs b/examples/multi_thread_demo/src/main.rs index f5756f1..e44ef6f 100644 --- a/examples/multi_thread_demo/src/main.rs +++ b/examples/multi_thread_demo/src/main.rs @@ -94,12 +94,11 @@ fn main() { println!("XCPlite Multi Thread Demo"); // Logging - env_logger::Builder::new().filter_level(log::LevelFilter::Warn).init(); + env_logger::Builder::new().filter_level(log::LevelFilter::Info).init(); // Initialize XCP driver singleton, the transport layer server and enable the registry let xcp = XcpBuilder::new("multi_thread_demo") .set_log_level(XcpLogLevel::Warn) - .enable_a2l(true) .set_epk("EPK_12345678") .start_server(XcpTransportLayer::Udp, [127, 0, 0, 1], 5555) .unwrap(); @@ -125,8 +124,9 @@ fn main() { })); } + // Test thread::sleep(Duration::from_millis(1000)); - Xcp::get().write_a2l(); + xcp.write_a2l(); t.into_iter().for_each(|t| t.join().unwrap()); diff --git a/examples/point_cloud_demo/src/main.rs b/examples/point_cloud_demo/src/main.rs index 633ce85..d144d02 100644 --- a/examples/point_cloud_demo/src/main.rs +++ b/examples/point_cloud_demo/src/main.rs @@ -110,11 +110,10 @@ fn create_point_cloud() -> PointCloud { fn main() { println!("xcp-lite point cloud demo"); - env_logger::Builder::new().filter_level(log::LevelFilter::Debug).init(); + env_logger::Builder::new().filter_level(log::LevelFilter::Info).init(); let xcp = XcpBuilder::new("point_cloud") .set_log_level(XcpLogLevel::Debug) - .enable_a2l(true) .start_server(XcpTransportLayer::Udp, BIND_ADDR, 5555) .unwrap(); diff --git a/examples/protobuf_demo/src/main.rs b/examples/protobuf_demo/src/main.rs index 9b162bd..8cb0c60 100644 --- a/examples/protobuf_demo/src/main.rs +++ b/examples/protobuf_demo/src/main.rs @@ -151,11 +151,10 @@ pub struct TestData { fn main() { println!("protobuf demo"); - env_logger::Builder::new().filter_level(log::LevelFilter::Debug).init(); + env_logger::Builder::new().filter_level(log::LevelFilter::Info).init(); let xcp = XcpBuilder::new("xcp_demo") .set_log_level(XcpLogLevel::Debug) - .enable_a2l(true) .set_epk("EPK_") .start_server(XcpTransportLayer::Udp, [127, 0, 0, 1], 5555) .unwrap(); @@ -219,6 +218,6 @@ fn main() { thread::sleep(Duration::from_micros(1000000)); - Xcp::get().write_a2l(); // @@@@ test + xcp.write_a2l(); // @@@@ test } } diff --git a/examples/rayon_demo/src/main.rs b/examples/rayon_demo/src/main.rs index 7af0cac..d54355e 100644 --- a/examples/rayon_demo/src/main.rs +++ b/examples/rayon_demo/src/main.rs @@ -133,7 +133,7 @@ fn render(pixels: &mut [u8], row: usize, length: usize, upper_left: Complex fn main() { println!("xcp-lite rayon mandelbrot demo"); - env_logger::Builder::new().filter_level(log::LevelFilter::Debug).init(); + env_logger::Builder::new().filter_level(log::LevelFilter::Info).init(); const BIND_ADDR: [u8; 4] = [192, 168, 0, 83]; // const BIND_ADDR: [u8; 4] = [127, 0, 0, 1]; @@ -141,7 +141,6 @@ fn main() { let xcp = XcpBuilder::new("mandelbrot") .set_log_level(XcpLogLevel::Debug) - .enable_a2l(true) .set_epk("EPK") .start_server(XcpTransportLayer::Udp, BIND_ADDR, 5555) .unwrap(); diff --git a/examples/scoped_threads/src/main.rs b/examples/scoped_threads/src/main.rs index 8b26ced..48befcf 100644 --- a/examples/scoped_threads/src/main.rs +++ b/examples/scoped_threads/src/main.rs @@ -44,13 +44,12 @@ fn task1(calseg: CalSeg) { if counter > calseg.max { counter = calseg.min; } - // info!("Counter: {}", counter); + event.trigger(); thread::sleep(Duration::from_micros(calseg.delay as u64)); calseg.sync(); - Xcp::get().write_a2l(); } } @@ -59,11 +58,10 @@ fn task1(calseg: CalSeg) { fn main() { println!("XCP Demo"); - env_logger::Builder::new().filter_level(log::LevelFilter::Debug).init(); + env_logger::Builder::new().filter_level(log::LevelFilter::Info).init(); let xcp = XcpBuilder::new("xcp_demo") - .set_log_level(XcpLogLevel::Debug) - .enable_a2l(true) + .set_log_level(XcpLogLevel::Info) .set_epk("EPK_") .start_server(XcpTransportLayer::Udp, [127, 0, 0, 1], 5555) .unwrap(); diff --git a/examples/single_thread_demo/src/main.rs b/examples/single_thread_demo/src/main.rs index f8eab38..c51b4ee 100644 --- a/examples/single_thread_demo/src/main.rs +++ b/examples/single_thread_demo/src/main.rs @@ -58,7 +58,7 @@ fn main() { // The A2L file will be finalized on XCP connection and can be uploaded by CANape let xcp = XcpBuilder::new("single_thread_demo") .set_log_level(XcpLogLevel::Info) // Set log level of the XCP server - .enable_a2l(true) // Enabl A2L generation + // Enabl A2L generation .set_epk("EPK_") // Set the EPK string for A2L version check, length must be %4 .start_server(XcpTransportLayer::Udp, [127, 0, 0, 1] /*[172, 19, 11, 24]*/, 5555) .unwrap(); @@ -112,7 +112,7 @@ fn main() { thread::sleep(Duration::from_millis(10)); // 100 Hz - Xcp::get().write_a2l(); + xcp.write_a2l(); } // Stop the XCP server diff --git a/examples/tokio_demo/src/main.rs b/examples/tokio_demo/src/main.rs index 8f7edaf..186c1a1 100644 --- a/examples/tokio_demo/src/main.rs +++ b/examples/tokio_demo/src/main.rs @@ -109,7 +109,7 @@ async fn main() -> Result<(), Box> { // Start tokio XCP server // Initialize the xcplib transport and protocol layer only, not the server - let xcp: &'static Xcp = XcpBuilder::new("tokio_demo").set_log_level(XcpLogLevel::Debug).enable_a2l(true).tl_start().unwrap(); + let xcp: &'static Xcp = XcpBuilder::new("tokio_demo").set_log_level(XcpLogLevel::Debug).tl_start().unwrap(); let xcp_task = tokio::spawn(xcp_server::xcp_task(xcp, [127, 0, 0, 1], 5555)); // let mut xcp_server = xcp_server::XcpServer::new([127, 0, 0, 1], 5555); diff --git a/src/main.rs b/src/main.rs index 9f66058..59757ed 100644 --- a/src/main.rs +++ b/src/main.rs @@ -50,10 +50,6 @@ struct Args { /// Port number #[arg(short, long, default_value_t = 5555)] port: u16, - - /// Don't create A2L file - #[arg(short, long, default_value_t = false)] - no_a2l: bool, } //----------------------------------------------------------------------------- @@ -124,14 +120,14 @@ struct CalPage { run: bool, // Stop all tasks run1: bool, // Stop demo task1 run2: bool, // Stop demo task2 - cycle_time_us: u32, // Cycle time of main loop in microseconds + cycle_time_ms: u16, // Cycle time of main loop task in milliseconds } const CAL_PAGE: CalPage = CalPage { run: true, run1: true, run2: true, - cycle_time_us: 50000, // 50ms + cycle_time_ms: 100, // 100ms }; //--------------------------------------------------- @@ -349,20 +345,16 @@ fn main() { env_logger::Builder::new().filter_level(log_level.to_log_level_filter()).init(); // Initialize XCP driver singleton, the transport layer server and enable the A2L writer - let xcp_builder = XcpBuilder::new("xcp_lite") + let xcp = XcpBuilder::new("xcp_lite") .set_log_level(log_level) - .enable_a2l(!args.no_a2l) // .set_segment_size(1500-20-8) // no jumbo frames // .set_epk(build_info::format!("{}", $.timestamp)); // Create new EPK from build info - .set_epk("EPK_"); - - let xcp = match xcp_builder.start_server(if args.tcp { XcpTransportLayer::Tcp } else { XcpTransportLayer::Udp }, args.bind, args.port) { - Err(res) => { - error!("XCP server initialization failed: {:?}", res); - return; - } - Ok(xcp) => xcp, - }; + .set_epk("EPK_") + .start_server(if args.tcp { XcpTransportLayer::Tcp } else { XcpTransportLayer::Udp }, args.bind, args.port) + .map_err(|e| { + panic!("XCP server initialization failed: {:?}", e); + }) + .unwrap(); // Register a static calibration page let calpage00 = CAL_PAGE0.get().unwrap(); @@ -385,7 +377,7 @@ fn main() { .add_field(calseg_field!(CAL_PAGE.run, 0, 1, "bool")) .add_field(calseg_field!(CAL_PAGE.run1, 0, 1, "bool")) .add_field(calseg_field!(CAL_PAGE.run2, 0, 1, "bool")) - .add_field(calseg_field!(CAL_PAGE.cycle_time_us, "us", "main task cycle time")); + .add_field(calseg_field!(CAL_PAGE.cycle_time_ms, "ms", "main task cycle time")); // Create calibration segments for CAL_PAGE1 and CAL_PAGE2, add fields with macro derive(XcpTypeDescription)) let calseg1 = xcp.create_calseg("CalPage1", &CAL_PAGE1, true); @@ -435,11 +427,13 @@ fn main() { if !calseg.run { break; } - thread::sleep(Duration::from_micros(calseg.cycle_time_us as u64)); + thread::sleep(Duration::from_millis(calseg.cycle_time_ms as u64)); // Variables on stack and heap - mainloop_counter1 += 1; - *mainloop_counter2 += 2; + if xcp.is_connected() { + mainloop_counter1 += 1; + } + *mainloop_counter2 += 1; mainloop_map[0][0] = mainloop_counter1 as u8; // Capture variable from heap @@ -477,9 +471,7 @@ fn main() { // Finalize A2l after 2s delay // This is just for testing, to force immediate creation of A2L file // Without this, the A2L file will be automatically written on XCP connect, to be available for download by CANape - if !args.no_a2l && mainloop_counter1 == 1 { - thread::sleep(Duration::from_secs(2)); - + if !xcp.is_connected() && *mainloop_counter2 == (2000 / calseg.cycle_time_ms as u16) as u64 { // Test A2L write xcp.write_a2l(); @@ -488,9 +480,21 @@ fn main() { // Test freeze request // xcp.set_freeze_request(); + } + + // Terminate after more than 10s disconnected to test shutdown behaviour - // Test shutdown - // break; + // This is just for testing, to force immediate creation of A2L file + // Without this, the A2L file will be automatically written on XCP connect, to be available for download by CANape + if !xcp.is_connected() { + if *mainloop_counter2 % 200 == 0 { + println!("."); + } + if mainloop_counter1 == (10000 / calseg.cycle_time_ms as u16) as u64 { + // after 10s when disconnected + thread::sleep(Duration::from_secs(2)); + break; + } } } info!("Main task finished"); @@ -502,5 +506,6 @@ fn main() { info!("All tasks finished"); // Stop and shutdown the XCP server + info!("Stop XCP server"); xcp.stop_server(); } diff --git a/src/reg/registry.rs b/src/reg/registry.rs index 7f4ea25..9d2bb3a 100644 --- a/src/reg/registry.rs +++ b/src/reg/registry.rs @@ -549,6 +549,7 @@ impl RegistryCharacteristicList { #[derive(Debug)] pub struct Registry { + freeze: bool, name: Option<&'static str>, tl_params: Option, mod_par: RegistryEpk, @@ -568,6 +569,7 @@ impl Registry { /// Create a measurement and calibration registry pub fn new() -> Registry { Registry { + freeze: false, name: None, tl_params: None, mod_par: RegistryEpk::new(), @@ -580,7 +582,8 @@ impl Registry { /// Clear (for test only) pub fn clear(&mut self) { - debug!("Clear and close registry"); + debug!("Registry clear()"); + self.freeze = false; self.name = None; self.tl_params = None; self.mod_par = RegistryEpk::new(); @@ -590,15 +593,20 @@ impl Registry { self.measurement_list = RegistryMeasurementList::new(); } - /// Close registry - pub fn close(&mut self) { - debug!("Close registry"); - self.name = None; + /// Freeze registry + pub fn freeze(&mut self) { + debug!("Registry freeze()"); + self.freeze = true; + } + + /// Get freeze status + pub fn is_frozen(&self) -> bool { + self.freeze } /// Set name pub fn set_name(&mut self, name: &'static str) { - debug!("set_name: {}", name); + debug!("Registry set_name({})", name); self.name = Some(name); } @@ -609,9 +617,7 @@ impl Registry { // Set EPK pub fn set_epk(&mut self, epk: &'static str, epk_addr: u32) { - debug!("set_epk: {} 0x{:08X}", epk, epk_addr); - assert!(self.name.is_some(), "Registry is closed"); - + debug!("Registry set_epk: {} 0x{:08X}", epk, epk_addr); self.mod_par.epk = Some(epk); self.mod_par.epk_addr = epk_addr; } @@ -623,24 +629,22 @@ impl Registry { // Set transport layer parameters pub fn set_tl_params(&mut self, protocol_name: &'static str, addr: Ipv4Addr, port: u16) { - debug!("set_tl_params: {} {} {}", protocol_name, addr, port); - assert!(self.name.is_some(), "Registry is closed"); - + debug!("Registry set_tl_params: {} {} {}", protocol_name, addr, port); self.tl_params = Some(RegistryXcpTransportLayer { protocol_name, addr, port }); } // Add an event pub fn add_event(&mut self, event: XcpEvent) { - debug!("add_event: num={}, index={}", event.get_num(), event.get_index()); - assert!(self.name.is_some(), "Registry is closed"); + debug!("Registry add_event: num={}, index={}", event.get_num(), event.get_index()); + assert!(!self.is_frozen(), "Registry is closed"); self.event_list.push(event); } // Add a calibration segment pub fn add_cal_seg(&mut self, name: &'static str, addr: u32, addr_ext: u8, size: u32) { - debug!("add_cal_seg: {} {}:0x{:08X}-{} ", name, addr_ext, addr, size); - assert!(self.name.is_some(), "Registry is closed"); + debug!("Registry add_cal_seg: {} {}:0x{:08X}-{} ", name, addr_ext, addr, size); + assert!(!self.is_frozen(), "Registry is closed"); // Length of calseg should be %4 to avoid problems with CANape and checksum calculations // Address should also be %4 @@ -660,7 +664,7 @@ impl Registry { } pub fn get_measurement_list(&self) -> &Vec { - println!("get_measurement_list, len = {}", self.measurement_list.0.len()); + println!("Registry get_measurement_list, len = {}", self.measurement_list.0.len()); &self.measurement_list.0 } @@ -671,7 +675,7 @@ impl Registry { /// If the registry is closed pub fn add_measurement(&mut self, mut m: RegistryMeasurement) { debug!( - "add_measurement: {} type={:?}[{},{}] event={}+({})", + "Registry add_measurement: {} type={:?}[{},{}] event={}+({})", m.name, m.datatype, m.x_dim, @@ -681,7 +685,7 @@ impl Registry { ); // Panic if registry is closed - assert!(self.name.is_some(), "Registry is closed"); + assert!(!self.is_frozen(), "Registry is closed"); // Append event index to name in case of a multi instance event (index>0) if m.event.get_index() > 0 { @@ -708,11 +712,10 @@ impl Registry { /// If a measurement with the same name already exists /// If the registry is closed pub fn add_characteristic(&mut self, c: RegistryCharacteristic) { - debug!("add_characteristic: {:?}.{} type={:?} offset={}", c.calseg_name, c.name, c.datatype, c.addr_offset); - debug!("add_characteristic: {:?}", c); + debug!("Registry add_characteristic: {:?}.{} type={:?} offset={}", c.calseg_name, c.name, c.datatype, c.addr_offset); // Panic if registry is closed - assert!(self.name.is_some(), "Registry is closed"); + assert!(!self.is_frozen(), "Registry is closed"); // Panic if duplicate for c1 in self.characteristic_list.iter() { @@ -736,8 +739,8 @@ impl Registry { /// Returns true, if file is rewritten due to changes pub fn write(&mut self) -> Result { // Error if registry is closed - if self.name.is_none() { - return Err("Registry is closed"); + if self.is_frozen() { + return Err("Registry is frozen!"); } // Sort measurement and calibration lists to get deterministic order diff --git a/src/xcp.rs b/src/xcp.rs index 195ae37..7d91c00 100644 --- a/src/xcp.rs +++ b/src/xcp.rs @@ -351,7 +351,6 @@ impl XcpTransportLayer { pub struct XcpBuilder { log_level: XcpLogLevel, // log level for the server name: &'static str, // Registry name, file name for the registry A2L generator - a2l_enable: bool, // Enable A2L file generation from registry epk: &'static str, // EPK string for A2L version check } @@ -361,7 +360,6 @@ impl XcpBuilder { XcpBuilder { log_level: XcpLogLevel::Info, name, - a2l_enable: true, epk: "EPK", } } @@ -372,12 +370,6 @@ impl XcpBuilder { self } - /// Enable A2L file generation - pub fn enable_a2l(mut self, a2l_enable: bool) -> Self { - self.a2l_enable = a2l_enable; - self - } - /// Set the EPK to enable the XCP tool to check the A2L file fits the code pub fn set_epk(mut self, epk: &'static str) -> Self { self.epk = epk; @@ -430,6 +422,7 @@ impl XcpBuilder { { let mut r = xcp.registry.lock().unwrap(); r.set_name(self.name); + r.set_tl_params(tl.protocol_name(), ipv4_addr, port); // Transport layer parameters r.set_epk(self.epk, Xcp::XCP_EPK_ADDR); // EPK } @@ -704,7 +697,7 @@ impl Xcp { /// This function force the A2L to be written immediately pub fn write_a2l(&self) { // Do nothing, if the registry is already written, or does not exist - if self.registry.lock().unwrap().get_name().is_none() { + if self.registry.lock().unwrap().is_frozen() { return; } @@ -729,7 +722,7 @@ impl Xcp { // A2l is no longer needed yet, free memory // Another call to a2l_write will do nothing // All registrations from now on, will cause panic - r.close(); + r.freeze(); } } diff --git a/tests/multi_thread.rs b/tests/multi_thread.rs index 8d68005..e4cf9dd 100644 --- a/tests/multi_thread.rs +++ b/tests/multi_thread.rs @@ -159,7 +159,6 @@ async fn test_multi_thread() { // Initialize XCP driver singleton, the transport layer server and enable the A2L writer let xcp = match XcpBuilder::new("xcp_lite") .set_log_level(OPTION_XCP_LOG_LEVEL) - .enable_a2l(true) .set_epk("EPK_TEST") .start_server(XcpTransportLayer::Udp, [127, 0, 0, 1], 5555) { @@ -183,7 +182,7 @@ async fn test_multi_thread() { v.push(t); } - test_executor(xcp, false, true).await; // Start the test executor XCP client + test_executor(xcp, test_executor::TestMode::MultiThreadDAQ).await; // Start the test executor XCP client for t in v { t.join().ok(); diff --git a/tests/single_thread.rs b/tests/single_thread.rs index 26696b2..3a7b8d4 100644 --- a/tests/single_thread.rs +++ b/tests/single_thread.rs @@ -1,6 +1,8 @@ // single_thread // Integration test for XCP in a single thread application -// Uses the test XCP client in test_executor +// Uses the test XCP client in module test_executor + +// cargo test --features=json --features=auto_reg -- --test-threads=1 --nocapture --test test_single_thread use xcp::*; use xcp_type_description::prelude::*; @@ -156,31 +158,66 @@ async fn test_single_thread() { info!("Running test_single_thread"); - // Initialize XCP driver singleton, the transport layer server and enable the A2L writer - let xcp = match XcpBuilder::new("xcp_lite") - .set_log_level(OPTION_XCP_LOG_LEVEL) - .enable_a2l(true) - .set_epk("EPK_TEST") - .start_server(XcpTransportLayer::Udp, [127, 0, 0, 1], 5555) - { - Err(res) => { - error!("XCP initialization failed: {:?}", res); - return; - } - Ok(xcp) => xcp, - }; + // Initialize the XCP driver singleton + let xcp = Xcp::get(); // Create a calibration segment let cal_seg = xcp.create_calseg("cal_seg", &CAL_PAR1, false); - // Create a test task - let t1 = thread::spawn(move || { - task(cal_seg); - }); + // Test calibration and measurement in a single thread + { + info!(""); + info!("================================================================="); + info!("XCP server initialization, pass 1"); + + // Initialize the XCPserver, transport layer and protocoll layer + let xcp = match XcpBuilder::new("xcp_lite") + .set_log_level(OPTION_XCP_LOG_LEVEL) + .set_epk("EPK_TEST") + .start_server(XcpTransportLayer::Udp, [127, 0, 0, 1], 5555) + { + Err(res) => { + error!("XCP initialization failed: {:?}", res); + return; + } + Ok(xcp) => xcp, + }; + + // Create a test task + let c = cal_seg.clone(); + let t1 = thread::spawn(move || { + task(c); + }); + + test_executor(xcp, test_executor::TestMode::SingleThreadDAQ).await; // Start the test executor XCP client + + t1.join().ok(); + xcp.stop_server(); + } - test_executor(xcp, true, false).await; // Start the test executor XCP client + // Reinitialize the XCP server a second time, to check correct shutdown behaviour + { + info!(""); + info!("================================================================="); + info!("XCP server initialization, pass 2"); + + // Initialize the XCPserver, transport layer and protocoll layer + let xcp = match XcpBuilder::new("xcp_lite") + .set_log_level(OPTION_XCP_LOG_LEVEL) + .set_epk("EPK_TEST") + .start_server(XcpTransportLayer::Udp, [127, 0, 0, 1], 5555) + { + Err(res) => { + error!("XCP initialization failed: {:?}", res); + return; + } + Ok(xcp) => xcp, + }; + + test_executor(xcp, test_executor::TestMode::ConnectOnly).await; // Start the test executor XCP client + + xcp.stop_server(); + } - t1.join().ok(); - xcp.stop_server(); std::fs::remove_file("xcp_client.a2l").ok(); } diff --git a/tests/test_executor/mod.rs b/tests/test_executor/mod.rs index 0a37897..691cd3c 100644 --- a/tests/test_executor/mod.rs +++ b/tests/test_executor/mod.rs @@ -12,8 +12,6 @@ use log::{debug, error, info, trace, warn}; use tokio::time::{Duration, Instant}; use xcp::Xcp; -use xcp::XcpLogLevel; - use xcp_client::a2l::*; use xcp_client::xcp_client::*; @@ -174,31 +172,44 @@ impl XcpDaqDecoder for DaqDecoder { //----------------------------------------------------------------------- // Execute tests -pub async fn test_executor(xcp: &Xcp, single_thread: bool, multi_thread: bool) { + +#[derive(Debug, Clone, Copy, PartialEq)] +pub enum TestMode { + ConnectOnly, + SingleThreadDAQ, + MultiThreadDAQ, +} + +pub async fn test_executor(xcp: &Xcp, test_mode: TestMode) { tokio::time::sleep(Duration::from_millis(500)).await; - info!("Start test executor"); + info!("Start test executor in {:?}", test_mode); //------------------------------------------------------------------------------------------------------------------------------------- // Create xcp_client and connect the XCP server - info!("Connect"); + info!("XCP CONNECT"); let dest_addr: Result = "127.0.0.1:5555".parse(); let local_addr: Result = "0.0.0.0:0".parse(); - debug!("dest_addr: {:?}", dest_addr); - debug!("local_addr: {:?}", local_addr); + debug!(" dest_addr: {:?}", dest_addr); + debug!(" local_addr: {:?}", local_addr); let mut xcp_client = XcpClient::new(dest_addr.unwrap(), local_addr.unwrap()); - let daq_decoder = Arc::new(Mutex::new(DaqDecoder::new())); let serv_text_decoder = ServTextDecoder::new(); xcp_client.connect(Arc::clone(&daq_decoder), serv_text_decoder).await.unwrap(); - tokio::time::sleep(Duration::from_micros(10000)).await; - - info!("XCP session status: {:?}", xcp.get_session_status()); + info!(" session status: {:?}", xcp.get_session_status()); assert!(xcp.get_session_status().contains(xcp::XcpSessionStatus::SS_CONNECTED)); + //------------------------------------------------------------------------------------------------------------------------------------- + // Get id + // tokio::time::sleep(Duration::from_micros(10000)).await; + // info!("XCP GET_ID"); + // if let id = xcp_client.get_id().await.unwrap(); + // info!(" id = {}", id); + // tokio::time::sleep(Duration::from_micros(10000)).await; + //------------------------------------------------------------------------------------------------------------------------------------- // Check command timeout using a command CC_NOP (non standard) without response - info!("Check timeout"); + info!("Check command timeout handling"); let res = xcp_client.command(CC_NOP).await; // Check unknown command match res { Ok(_) => panic!("Should timeout"), @@ -235,260 +246,260 @@ pub async fn test_executor(xcp: &Xcp, single_thread: bool, multi_thread: bool) { } } - //------------------------------------------------------------------------------------------------------------------------------------- - // Upload A2L file - tokio::time::sleep(Duration::from_micros(10000)).await; - info!("Upload A2l"); - xcp_client.upload_a2l().await.unwrap(); - tokio::time::sleep(Duration::from_micros(10000)).await; - - //------------------------------------------------------------------------------------------------------------------------------------- - // Check EPK upload - let res = xcp_client.short_upload(0x80000000, 0, 8).await; - let resp: Vec = match res { - Err(e) => { - panic!("Could not upload EPK, Error: {}", e); - } - Ok(r) => r, - }; - let epk = resp[1..=8].to_vec(); - let epk_string = String::from_utf8(epk.clone()).unwrap(); - info!("Upload EPK = {} {:?}\n", epk_string, epk); - assert_eq!(epk_string, "EPK_TEST", "Unexpected EPK string"); - - //------------------------------------------------------------------------------------------------------------------------------------- - // Create calibration objects for CalPage1.cycle_time_us and CalPage1.run - - // Create calibration object for CalPage1.cycle_time_us - debug!("Create calibration object CalPage1.cycle_time_us"); - let cycle_time_us = xcp_client - .create_calibration_object("CalPage1.cycle_time_us") - .await - .expect("could not create calibration object CalPage1.cycle_time_us"); - - // Create a calibration object for CalPage.run - debug!("Create calibration object CalPage1.run"); - let run = xcp_client.create_calibration_object("CalPage1.run").await.expect("could not create calibration object CalPage1.run"); - let v = xcp_client.get_value_u64(run); - assert_eq!(v, 1); - - //------------------------------------------------------------------------------------------------------------------------------------- - // DAQ test single_thread or multi_thread - if single_thread || multi_thread { + if test_mode != TestMode::ConnectOnly { + //------------------------------------------------------------------------------------------------------------------------------------- + // Upload A2L file + tokio::time::sleep(Duration::from_micros(10000)).await; + info!("Upload A2l"); + xcp_client.upload_a2l().await.unwrap(); tokio::time::sleep(Duration::from_micros(10000)).await; - info!("Start data acquisition test"); - // Create a calibration object for CalPage1.counter_max - // Set counter_max to 15 - let counter_max = xcp_client - .create_calibration_object("CalPage1.counter_max") + //------------------------------------------------------------------------------------------------------------------------------------- + // Check EPK upload + let res = xcp_client.short_upload(0x80000000, 0, 8).await; + let resp: Vec = match res { + Err(e) => { + panic!("Could not upload EPK, Error: {}", e); + } + Ok(r) => r, + }; + let epk = resp[1..=8].to_vec(); + let epk_string = String::from_utf8(epk.clone()).unwrap(); + info!("Upload EPK = {} {:?}\n", epk_string, epk); + assert_eq!(epk_string, "EPK_TEST", "Unexpected EPK string"); + + //------------------------------------------------------------------------------------------------------------------------------------- + // Create calibration objects for CalPage1.cycle_time_us and CalPage1.run + + // Create calibration object for CalPage1.cycle_time_us + debug!("Create calibration object CalPage1.cycle_time_us"); + let cycle_time_us = xcp_client + .create_calibration_object("CalPage1.cycle_time_us") .await - .expect("could not create calibration object CalPage1.counter_max"); - xcp_client.set_value_u64(counter_max, 15).await.unwrap(); - - // Set cycle time - xcp_client.set_value_u64(cycle_time_us, TASK_SLEEP_TIME_US).await.unwrap(); // 1us - - // Measurement test loop - // Create a measurement DAQ list with all instances MULTI_THREAD_TASK_COUNT of measurement counter and counter_max - // Hard coded order and size in DaqDecoder (counter_max, counter, cal_test, ...) - let mut bytes: u32 = 0; - // for multi_thread - if multi_thread { - for i in 1..=MULTI_THREAD_TASK_COUNT { - let counter = "counter_".to_string() + &i.to_string(); - let counter_max = "counter_max_".to_string() + &i.to_string(); - let cal_test = "cal_test_".to_string() + &i.to_string(); - let loop_counter = "loop_counter_".to_string() + &i.to_string(); - let changes = "changes_".to_string() + &i.to_string(); - let test1 = "test1_".to_string() + &i.to_string(); - let test2 = "test2_".to_string() + &i.to_string(); - let test3 = "test3_".to_string() + &i.to_string(); - let test4 = "test4_".to_string() + &i.to_string(); - - xcp_client.create_measurement_object(counter_max.as_str()).unwrap(); - xcp_client.create_measurement_object(counter.as_str()).unwrap(); - xcp_client.create_measurement_object(cal_test.as_str()).unwrap(); - xcp_client.create_measurement_object(loop_counter.as_str()).unwrap(); - xcp_client.create_measurement_object(changes.as_str()).unwrap(); - xcp_client.create_measurement_object(test1.as_str()).unwrap(); - xcp_client.create_measurement_object(test2.as_str()).unwrap(); - xcp_client.create_measurement_object(test3.as_str()).unwrap(); - xcp_client.create_measurement_object(test4.as_str()).unwrap(); - - bytes += 32 + 32; // counter 4 + counter_max 4 + cal_test 8 + loop_counter 8 + changes 8 + test1-4 32 + .expect("could not create calibration object CalPage1.cycle_time_us"); + + // Create a calibration object for CalPage.run + debug!("Create calibration object CalPage1.run"); + let run = xcp_client.create_calibration_object("CalPage1.run").await.expect("could not create calibration object CalPage1.run"); + let v = xcp_client.get_value_u64(run); + assert_eq!(v, 1); + + //------------------------------------------------------------------------------------------------------------------------------------- + // DAQ test single_thread or multi_thread + if test_mode == TestMode::SingleThreadDAQ || test_mode == TestMode::MultiThreadDAQ { + tokio::time::sleep(Duration::from_micros(10000)).await; + info!("Start data acquisition test"); + + // Create a calibration object for CalPage1.counter_max + // Set counter_max to 15 + let counter_max = xcp_client + .create_calibration_object("CalPage1.counter_max") + .await + .expect("could not create calibration object CalPage1.counter_max"); + xcp_client.set_value_u64(counter_max, 15).await.unwrap(); + + // Set cycle time + xcp_client.set_value_u64(cycle_time_us, TASK_SLEEP_TIME_US).await.unwrap(); // 1us + + // Measurement test loop + // Create a measurement DAQ list with all instances MULTI_THREAD_TASK_COUNT of measurement counter and counter_max + // Hard coded order and size in DaqDecoder (counter_max, counter, cal_test, ...) + let mut bytes: u32 = 0; + // for multi_thread + if test_mode == TestMode::MultiThreadDAQ { + for i in 1..=MULTI_THREAD_TASK_COUNT { + let counter = "counter_".to_string() + &i.to_string(); + let counter_max = "counter_max_".to_string() + &i.to_string(); + let cal_test = "cal_test_".to_string() + &i.to_string(); + let loop_counter = "loop_counter_".to_string() + &i.to_string(); + let changes = "changes_".to_string() + &i.to_string(); + let test1 = "test1_".to_string() + &i.to_string(); + let test2 = "test2_".to_string() + &i.to_string(); + let test3 = "test3_".to_string() + &i.to_string(); + let test4 = "test4_".to_string() + &i.to_string(); + + xcp_client.create_measurement_object(counter_max.as_str()).unwrap(); + xcp_client.create_measurement_object(counter.as_str()).unwrap(); + xcp_client.create_measurement_object(cal_test.as_str()).unwrap(); + xcp_client.create_measurement_object(loop_counter.as_str()).unwrap(); + xcp_client.create_measurement_object(changes.as_str()).unwrap(); + xcp_client.create_measurement_object(test1.as_str()).unwrap(); + xcp_client.create_measurement_object(test2.as_str()).unwrap(); + xcp_client.create_measurement_object(test3.as_str()).unwrap(); + xcp_client.create_measurement_object(test4.as_str()).unwrap(); + + bytes += 32 + 32; // counter 4 + counter_max 4 + cal_test 8 + loop_counter 8 + changes 8 + test1-4 32 + } } - } - // for single_thread - else { - xcp_client.create_measurement_object("counter_max").unwrap(); - xcp_client.create_measurement_object("counter").unwrap(); - bytes += 8; - } - xcp_client.start_measurement().await.unwrap(); - - // Test for DURATION_DAQ_TEST_MS time, do a calibration of counter_max to 255 in the middle of the time - let starttime = Instant::now(); - tokio::time::sleep(Duration::from_millis(DURATION_DAQ_TEST_MS / 2)).await; - xcp_client.set_value_u64(counter_max, 255).await.unwrap(); // Calibrate counter_max - tokio::time::sleep(Duration::from_millis(DURATION_DAQ_TEST_MS / 2)).await; - let dt = starttime.elapsed().as_secs_f64(); - let duration_ms = dt * 1000.0; - - // Stop DAQ - xcp_client.stop_measurement().await.unwrap(); - - // Check results - { - let d = daq_decoder.lock().unwrap(); - info!("DAQ test cycle time = {}us", TASK_SLEEP_TIME_US); - if multi_thread { - info!("DAQ test thread count = {}", MULTI_THREAD_TASK_COUNT); - info!( - "DAQ test target data rate {} MByte/s", - (1.0 / TASK_SLEEP_TIME_US as f64) * (bytes * MULTI_THREAD_TASK_COUNT as u32) as f64 - ); + // for single_thread + else { + xcp_client.create_measurement_object("counter_max").unwrap(); + xcp_client.create_measurement_object("counter").unwrap(); + bytes += 8; } - info!(" signals = {}", MULTI_THREAD_TASK_COUNT * 8); - info!(" cycles = {}", d.daq_events[0]); - info!(" events = {}", d.tot_events); - info!(" bytes per cycle = {}", bytes); - assert_ne!(d.tot_events, 0); - assert!(d.daq_events[0] > 0); - info!(" test duration = {:.3}ms", duration_ms); - info!(" average datarate = {:.3} MByte/s", (bytes as f64 * d.tot_events as f64) / 1000.0 / duration_ms,); - assert!(duration_ms > DURATION_DAQ_TEST_MS as f64 && duration_ms < DURATION_DAQ_TEST_MS as f64 + 100.0); - let avg_cycletime_us = (duration_ms * 1000.0) / d.daq_events[0] as f64; - info!(" task cycle time:",); - info!(" average = {}us", avg_cycletime_us,); - info!(" min = {}us", d.daq0_timestamp_min); - info!(" max = {}us", d.daq0_timestamp_max); - let jitter = d.daq0_timestamp_max - d.daq0_timestamp_min; - info!(" jitter = {}us", jitter); - //assert!(jitter < 150); // us tolerance - let diff: f64 = (d.daq0_timestamp_min as f64 - TASK_SLEEP_TIME_US as f64).abs(); - info!(" ecu task cpu time = {:.1}us", diff); - //assert!(diff < 50.0); // us tolerance - if multi_thread { - assert_eq!(d.daq_max, (MULTI_THREAD_TASK_COUNT - 1) as u8); - // Check all max counters are now 255 - for i in 0..MULTI_THREAD_TASK_COUNT { - assert_eq!(d.max_counter[i], 255); + xcp_client.start_measurement().await.unwrap(); + + // Test for DURATION_DAQ_TEST_MS time, do a calibration of counter_max to 255 in the middle of the time + let starttime = Instant::now(); + tokio::time::sleep(Duration::from_millis(DURATION_DAQ_TEST_MS / 2)).await; + xcp_client.set_value_u64(counter_max, 255).await.unwrap(); // Calibrate counter_max + tokio::time::sleep(Duration::from_millis(DURATION_DAQ_TEST_MS / 2)).await; + let dt = starttime.elapsed().as_secs_f64(); + let duration_ms = dt * 1000.0; + + // Stop DAQ + xcp_client.stop_measurement().await.unwrap(); + + // Check results + { + let d = daq_decoder.lock().unwrap(); + info!("DAQ test cycle time = {}us", TASK_SLEEP_TIME_US); + if test_mode == TestMode::MultiThreadDAQ { + info!("DAQ test thread count = {}", MULTI_THREAD_TASK_COUNT); + info!( + "DAQ test target data rate {} MByte/s", + (1.0 / TASK_SLEEP_TIME_US as f64) * (bytes * MULTI_THREAD_TASK_COUNT as u32) as f64 + ); } - } else { - assert_eq!(d.daq_max, 0); - assert_eq!(d.max_counter[0], 255); // @@@@ + info!(" signals = {}", MULTI_THREAD_TASK_COUNT * 8); + info!(" cycles = {}", d.daq_events[0]); + info!(" events = {}", d.tot_events); + info!(" bytes per cycle = {}", bytes); + assert_ne!(d.tot_events, 0); + assert!(d.daq_events[0] > 0); + info!(" test duration = {:.3}ms", duration_ms); + info!(" average datarate = {:.3} MByte/s", (bytes as f64 * d.tot_events as f64) / 1000.0 / duration_ms,); + assert!(duration_ms > DURATION_DAQ_TEST_MS as f64 && duration_ms < DURATION_DAQ_TEST_MS as f64 + 100.0); + let avg_cycletime_us = (duration_ms * 1000.0) / d.daq_events[0] as f64; + info!(" task cycle time:",); + info!(" average = {}us", avg_cycletime_us,); + info!(" min = {}us", d.daq0_timestamp_min); + info!(" max = {}us", d.daq0_timestamp_max); + let jitter = d.daq0_timestamp_max - d.daq0_timestamp_min; + info!(" jitter = {}us", jitter); + //assert!(jitter < 150); // us tolerance + let diff: f64 = (d.daq0_timestamp_min as f64 - TASK_SLEEP_TIME_US as f64).abs(); + info!(" ecu task cpu time = {:.1}us", diff); + //assert!(diff < 50.0); // us tolerance + if test_mode == TestMode::MultiThreadDAQ { + assert_eq!(d.daq_max, (MULTI_THREAD_TASK_COUNT - 1) as u8); + // Check all max counters are now 255 + for i in 0..MULTI_THREAD_TASK_COUNT { + assert_eq!(d.max_counter[i], 255); + } + } else { + assert_eq!(d.daq_max, 0); + assert_eq!(d.max_counter[0], 255); // @@@@ + } + assert_eq!(d.odt_max, 0); } - assert_eq!(d.odt_max, 0); } - } - // Wait some time to be sure the queue is emptied - // The XCP server should not respond to STOP while the queue is not empty - // But the queue of the client may still contain data or the control channel may need some time - tokio::time::sleep(Duration::from_millis(500)).await; - - //------------------------------------------------------------------------------------------------------------------------------------- - // Calibration test - if single_thread || multi_thread { - // Test signed - debug!("Create calibration object CalPage1.test_i16"); - let test_i32 = xcp_client - .create_calibration_object("CalPage1.TestInts.test_i16") - .await - .expect("could not create calibration object CalPage1.test_i16"); - let v = xcp_client.get_value_i64(test_i32); - debug!("test_i32 = {}", v); - xcp_client.set_value_i64(test_i32, 1).await.unwrap(); - let v = xcp_client.get_value_i64(test_i32); - debug!("test_i32 = {}", v); - xcp_client.set_value_i64(test_i32, -1).await.unwrap(); - let v = xcp_client.get_value_i64(test_i32); - debug!("test_i32 = {}", v); - - // Check page switching - // Check page is ram - info!("Check ecu cal page"); - let mut page: u8 = xcp_client.get_ecu_page().await.unwrap(); - assert!(page == 0); - page = xcp_client.get_xcp_page().await.unwrap(); - assert!(page == 0); - - // Mark the ram page in variable cal_seg.page - let mut cal_seg_page = xcp_client.create_calibration_object("CalPage1.page").await.expect("could not create calibration object CalPage1.page"); - xcp_client // init page variable in ram page of cal_seg - .set_value_u64(cal_seg_page, 0) - .await - .unwrap(); - // Switch to default - xcp_client.set_ecu_page(1).await.unwrap(); - xcp_client.set_xcp_page(1).await.unwrap(); - tokio::time::sleep(Duration::from_micros(100000)).await; - // Check if cal_seg.page marker is default - cal_seg_page = xcp_client.create_calibration_object("CalPage1.page").await.expect("could not create calibration object CalPage1.page"); - page = xcp_client.get_value_u64(cal_seg_page) as u8; - assert_eq!(page, 1); - // Check if get cal page returns default - page = xcp_client.get_xcp_page().await.unwrap(); - assert_eq!(page, 1); - page = xcp_client.get_ecu_page().await.unwrap(); - assert_eq!(page, 1); - // Switch back to ram - xcp_client.set_xcp_page(0).await.unwrap(); - xcp_client.set_ecu_page(0).await.unwrap(); - - // Calibration test loop - // Do MAX_ITER test calibrations on cal_seg.cal_test, task will panic if cal_seg.test_u64 has not the expected pattern - { - const MAX_ITER: u32 = 10000; - const TASK_SLEEP_TIME_US: u64 = 50; // us thread - const LOG_LEVEL: XcpLogLevel = XcpLogLevel::Warn; // for XCP server - - tokio::time::sleep(Duration::from_micros(10000)).await; - info!("start calibration test"); - - // Set task cycle time to TASK_SLEEP_TIME_US - xcp_client.set_value_u64(cycle_time_us, TASK_SLEEP_TIME_US).await.unwrap(); - Xcp::get().set_log_level(LOG_LEVEL); - - // Create calibration variable CalPage1.cal_test - let res = a2l_reader::a2l_find_characteristic(xcp_client.get_a2l_file().unwrap(), "CalPage1.cal_test").unwrap(); - let addr_cal_test = res.0.addr; - debug!("download cal_test = 0x{:X}\n", res.0.addr); - - // Calibration loop - // Set calibration variable cal_test to a defined pattern which will be checked by the server application task - let start_time = Instant::now(); - for i in 0..MAX_ITER { - let cal_test = i as u64 + (((i as u64) << 32) ^ 0x5555555500000000u64); // The server task will check this pattern and panic if it is wrong - trace!("download cal_test = {:X}", cal_test); - - xcp_client // SHORT_DOWNLOAD cal_seg.test_u64 - .short_download(addr_cal_test, 0, &cal_test.to_le_bytes()) - .await - .unwrap(); - } - let elapsed_time = start_time.elapsed().as_micros(); - let download_time = elapsed_time as f64 / MAX_ITER as f64; - info!( - "calibration test loop done, {} iterations, duration={}ms, {}us per download, {:.1} KBytes/s", - MAX_ITER, - elapsed_time / 1000, - download_time, - MAX_ITER as f64 * 8000.0 / elapsed_time as f64 - ); - if download_time > 100.0 { - warn!("Calibration download time ({}us) is too high!", download_time); + // Wait some time to be sure the queue is emptied + // The XCP server should not respond to STOP while the queue is not empty + // But the queue of the client may still contain data or the control channel may need some time + tokio::time::sleep(Duration::from_millis(500)).await; + + //------------------------------------------------------------------------------------------------------------------------------------- + // Calibration test + if test_mode == TestMode::SingleThreadDAQ || test_mode == TestMode::MultiThreadDAQ { + // Test signed + debug!("Create calibration object CalPage1.test_i16"); + let test_i32 = xcp_client + .create_calibration_object("CalPage1.TestInts.test_i16") + .await + .expect("could not create calibration object CalPage1.test_i16"); + let v = xcp_client.get_value_i64(test_i32); + debug!("test_i32 = {}", v); + xcp_client.set_value_i64(test_i32, 1).await.unwrap(); + let v = xcp_client.get_value_i64(test_i32); + debug!("test_i32 = {}", v); + xcp_client.set_value_i64(test_i32, -1).await.unwrap(); + let v = xcp_client.get_value_i64(test_i32); + debug!("test_i32 = {}", v); + + // Check page switching + // Check page is ram + info!("Check ecu cal page"); + let mut page: u8 = xcp_client.get_ecu_page().await.unwrap(); + assert!(page == 0); + page = xcp_client.get_xcp_page().await.unwrap(); + assert!(page == 0); + + // Mark the ram page in variable cal_seg.page + let mut cal_seg_page = xcp_client.create_calibration_object("CalPage1.page").await.expect("could not create calibration object CalPage1.page"); + xcp_client // init page variable in ram page of cal_seg + .set_value_u64(cal_seg_page, 0) + .await + .unwrap(); + // Switch to default + xcp_client.set_ecu_page(1).await.unwrap(); + xcp_client.set_xcp_page(1).await.unwrap(); + tokio::time::sleep(Duration::from_micros(100000)).await; + // Check if cal_seg.page marker is default + cal_seg_page = xcp_client.create_calibration_object("CalPage1.page").await.expect("could not create calibration object CalPage1.page"); + page = xcp_client.get_value_u64(cal_seg_page) as u8; + assert_eq!(page, 1); + // Check if get cal page returns default + page = xcp_client.get_xcp_page().await.unwrap(); + assert_eq!(page, 1); + page = xcp_client.get_ecu_page().await.unwrap(); + assert_eq!(page, 1); + // Switch back to ram + xcp_client.set_xcp_page(0).await.unwrap(); + xcp_client.set_ecu_page(0).await.unwrap(); + + // Calibration test loop + // Do MAX_ITER test calibrations on cal_seg.cal_test, task will panic if cal_seg.test_u64 has not the expected pattern + { + const MAX_ITER: u32 = 5000; // Number of calibrations + const TASK_SLEEP_TIME_US: u64 = 50; // Checking task cycle time + + tokio::time::sleep(Duration::from_micros(10000)).await; + info!("start calibration test"); + + // Speed up task cycle time to TASK_SLEEP_TIME_US, this will determine the calseg.sync() rate and pattern checking rate + xcp_client.set_value_u64(cycle_time_us, TASK_SLEEP_TIME_US).await.unwrap(); + + // Create calibration variable CalPage1.cal_test + let res = a2l_reader::a2l_find_characteristic(xcp_client.get_a2l_file().unwrap(), "CalPage1.cal_test").unwrap(); + let addr_cal_test = res.0.addr; + debug!("download cal_test = 0x{:X}\n", res.0.addr); + + // Calibration loop + // Set calibration variable cal_test to a defined pattern which will be checked by the server application task + let start_time = Instant::now(); + for i in 0..MAX_ITER { + let cal_test = i as u64 + (((i as u64) << 32) ^ 0x5555555500000000u64); // The server task will check this pattern and panic if it is wrong + trace!("download cal_test = {:X}", cal_test); + + xcp_client // SHORT_DOWNLOAD cal_seg.test_u64 + .short_download(addr_cal_test, 0, &cal_test.to_le_bytes()) + .await + .unwrap(); + } + let elapsed_time = start_time.elapsed().as_micros(); + let download_time = elapsed_time as f64 / MAX_ITER as f64; + info!( + "calibration test loop done, {} iterations, duration={}ms, {}us per download, {:.1} KBytes/s", + MAX_ITER, + elapsed_time / 1000, + download_time, + MAX_ITER as f64 * 8000.0 / elapsed_time as f64 + ); + if download_time > 100.0 { + warn!("Calibration download time ({}us) is too high!", download_time); + } } } - } - // Stop test task - xcp_client.set_value_u64(run, 0).await.unwrap(); + // Stop test task + xcp_client.set_value_u64(run, 0).await.unwrap(); + } // Disconnect info!("DISCONNECT"); xcp_client.disconnect().await.unwrap(); - //std::fs::remove_file("test_upload.a2l").ok(); + std::fs::remove_file("test_upload.a2l").ok(); } diff --git a/tests/tokio_multi_thread.rs b/tests/tokio_multi_thread.rs index 6b2ac54..7297082 100644 --- a/tests/tokio_multi_thread.rs +++ b/tests/tokio_multi_thread.rs @@ -156,12 +156,7 @@ async fn test_tokio_multi_thread() { // Start tokio XCP server // Initialize the xcplib transport and protocol layer only, not the server - let xcp: &'static Xcp = XcpBuilder::new("tokio_demo") - .set_log_level(OPTION_XCP_LOG_LEVEL) - .enable_a2l(true) - .set_epk("EPK_TEST") - .tl_start() - .unwrap(); + let xcp: &'static Xcp = XcpBuilder::new("tokio_demo").set_log_level(OPTION_XCP_LOG_LEVEL).set_epk("EPK_TEST").tl_start().unwrap(); let _xcp_task = tokio::spawn(xcp_server::xcp_task(xcp, [127, 0, 0, 1], 5555)); // Create a calibration segment @@ -177,7 +172,7 @@ async fn test_tokio_multi_thread() { v.push(t); } - test_executor(xcp, false, true).await; // Start the test executor XCP client + test_executor(xcp, test_executor::TestMode::MultiThreadDAQ).await; // Start the test executor XCP client for t in v { t.join().ok(); diff --git a/tests/tokio_single_thread.rs b/tests/tokio_single_thread.rs index 49fa994..a4ef30b 100644 --- a/tests/tokio_single_thread.rs +++ b/tests/tokio_single_thread.rs @@ -157,9 +157,9 @@ async fn test_tokio_single_thread() { // Initialize the xcplib transport and protocol layer only, not the server let xcp: &'static Xcp = XcpBuilder::new("tokio_demo") .set_log_level(OPTION_XCP_LOG_LEVEL) - .enable_a2l(true) .set_epk("EPK_TEST") .tl_start() + .map_err(|e| error!("{}", e)) .unwrap(); let _xcp_task = tokio::spawn(xcp_server::xcp_task(xcp, [127, 0, 0, 1], 5555)); @@ -171,7 +171,7 @@ async fn test_tokio_single_thread() { task(cal_seg); }); - test_executor(xcp, true, false).await; // Start the test executor XCP client + test_executor(xcp, test_executor::TestMode::SingleThreadDAQ).await; // Start the test executor XCP client t1.join().ok(); diff --git a/xcp_client/src/main.rs b/xcp_client/src/main.rs index 70ffdab..e35037a 100644 --- a/xcp_client/src/main.rs +++ b/xcp_client/src/main.rs @@ -232,21 +232,25 @@ async fn main() { byte_count as f64 / elapsed_time as f64 ); info!("Expected {} events/s, {} byte/s", 1_000_000 / 50 * 10, 90 * 1_000_000 / 50 * 10); - assert_ne!(event_count, 0); - - // Stop demo task - // Test shutdown - // Create a calibration object for CalPage.run - // if let Ok(run) = xcp_client.create_calibration_object("CalPage.run").await { - // let v = xcp_client.get_value_u64(run); - // info!("CalPage.run = {}", v); - // assert_eq!(v, 1); - // xcp_client.set_value_u64(run, 0).await.unwrap(); - // } else { - // warn!("CalPage.run not found"); - // } // Disconnect info!("XCP Disconnect"); xcp_client.disconnect().await.unwrap(); } + +// Start/Stop demo task +// Create a calibration object for CalPage.run +// impl XcpClient { +// async fn set_run(&mut self, state: bool) -> Result<(), XcpError> { +// if let Ok(run) = self.create_calibration_object("CalPage.run").await { +// let v = self.get_value_u64(run); +// info!("CalPage.run = {}", v); +// assert_eq!(v, 1); +// self.set_value_u64(run, state as u64).await.unwrap(); +// } else { +// warn!("CalPage.run not found"); +// } + +// Ok(()) +// } +// } diff --git a/xcp_lite.a2l b/xcp_lite.a2l index 63e1104..a49d828 100644 --- a/xcp_lite.a2l +++ b/xcp_lite.a2l @@ -59,14 +59,14 @@ /begin MOD_PAR "" - EPK "EPK_TEST" + EPK "EPK_" ADDR_EPK 0x80000000 /begin MEMORY_SEGMENT - epk "" DATA FLASH INTERN 0x80000000 8 -1 -1 -1 -1 -1 + epk "" DATA FLASH INTERN 0x80000000 4 -1 -1 -1 -1 -1 /end MEMORY_SEGMENT /begin MEMORY_SEGMENT - cal_seg "" DATA FLASH INTERN 0x80010000 72 -1 -1 -1 -1 -1 + CalPage "" DATA FLASH INTERN 0x80010000 6 -1 -1 -1 -1 -1 /begin IF_DATA XCP /begin SEGMENT /* index: */ 1 /* pages: */ 2 /* ext: */ 0 0 0 /begin CHECKSUM XCP_ADD_44 MAX_BLOCK_SIZE 0xFFFF EXTERNAL_FUNCTION "" /end CHECKSUM @@ -74,6 +74,26 @@ /begin PAGE 0x1 ECU_ACCESS_DONT_CARE XCP_READ_ACCESS_DONT_CARE XCP_WRITE_ACCESS_NOT_ALLOWED /end PAGE /end SEGMENT /end IF_DATA + /end MEMORY_SEGMENT + /begin MEMORY_SEGMENT + CalPage1 "" DATA FLASH INTERN 0x80020000 56 -1 -1 -1 -1 -1 + /begin IF_DATA XCP + /begin SEGMENT /* index: */ 2 /* pages: */ 2 /* ext: */ 0 0 0 + /begin CHECKSUM XCP_ADD_44 MAX_BLOCK_SIZE 0xFFFF EXTERNAL_FUNCTION "" /end CHECKSUM + /begin PAGE 0x0 ECU_ACCESS_DONT_CARE XCP_READ_ACCESS_DONT_CARE XCP_WRITE_ACCESS_DONT_CARE /end PAGE + /begin PAGE 0x1 ECU_ACCESS_DONT_CARE XCP_READ_ACCESS_DONT_CARE XCP_WRITE_ACCESS_NOT_ALLOWED /end PAGE + /end SEGMENT + /end IF_DATA + /end MEMORY_SEGMENT + /begin MEMORY_SEGMENT + CalPage2 "" DATA FLASH INTERN 0x80030000 216 -1 -1 -1 -1 -1 + /begin IF_DATA XCP + /begin SEGMENT /* index: */ 3 /* pages: */ 2 /* ext: */ 0 0 0 + /begin CHECKSUM XCP_ADD_44 MAX_BLOCK_SIZE 0xFFFF EXTERNAL_FUNCTION "" /end CHECKSUM + /begin PAGE 0x0 ECU_ACCESS_DONT_CARE XCP_READ_ACCESS_DONT_CARE XCP_WRITE_ACCESS_DONT_CARE /end PAGE + /begin PAGE 0x1 ECU_ACCESS_DONT_CARE XCP_READ_ACCESS_DONT_CARE XCP_WRITE_ACCESS_NOT_ALLOWED /end PAGE + /end SEGMENT + /end IF_DATA /end MEMORY_SEGMENT /end MOD_PAR @@ -110,12 +130,25 @@ OPTIONAL_LEVEL1_CMD GET_VERSION /end PROTOCOL_LAYER /begin DAQ - DYNAMIC 0 1 0 OPTIMISATION_TYPE_DEFAULT ADDRESS_EXTENSION_FREE IDENTIFICATION_FIELD_TYPE_RELATIVE_BYTE GRANULARITY_ODT_ENTRY_SIZE_DAQ_BYTE 0xF8 OVERLOAD_INDICATION_PID + DYNAMIC 0 14 0 OPTIMISATION_TYPE_DEFAULT ADDRESS_EXTENSION_FREE IDENTIFICATION_FIELD_TYPE_RELATIVE_BYTE GRANULARITY_ODT_ENTRY_SIZE_DAQ_BYTE 0xF8 OVERLOAD_INDICATION_PID /begin TIMESTAMP_SUPPORTED 0x1 SIZE_DWORD UNIT_1US TIMESTAMP_FIXED /end TIMESTAMP_SUPPORTED -/begin EVENT "task" "task" 0 DAQ 0xFF 0 0 0 CONSISTENCY DAQ /end EVENT +/begin EVENT "mainloop" "mainloop" 0 DAQ 0xFF 0 0 0 CONSISTENCY DAQ /end EVENT +/begin EVENT "mainloop_map" "mainloop" 1 DAQ 0xFF 0 0 0 CONSISTENCY DAQ /end EVENT +/begin EVENT "static_event" "static_e" 2 DAQ 0xFF 0 0 0 CONSISTENCY DAQ /end EVENT +/begin EVENT "task1" "task1" 3 DAQ 0xFF 0 0 0 CONSISTENCY DAQ /end EVENT +/begin EVENT "task2_inst_1" "task2__1" 4 DAQ 0xFF 0 0 0 CONSISTENCY DAQ /end EVENT +/begin EVENT "task2_inst_2" "task2__2" 5 DAQ 0xFF 0 0 0 CONSISTENCY DAQ /end EVENT +/begin EVENT "task2_inst_3" "task2__3" 6 DAQ 0xFF 0 0 0 CONSISTENCY DAQ /end EVENT +/begin EVENT "task2_inst_4" "task2__4" 7 DAQ 0xFF 0 0 0 CONSISTENCY DAQ /end EVENT +/begin EVENT "task2_inst_5" "task2__5" 8 DAQ 0xFF 0 0 0 CONSISTENCY DAQ /end EVENT +/begin EVENT "task2_inst_6" "task2__6" 9 DAQ 0xFF 0 0 0 CONSISTENCY DAQ /end EVENT +/begin EVENT "task2_inst_7" "task2__7" 10 DAQ 0xFF 0 0 0 CONSISTENCY DAQ /end EVENT +/begin EVENT "task2_inst_8" "task2__8" 11 DAQ 0xFF 0 0 0 CONSISTENCY DAQ /end EVENT +/begin EVENT "task2_inst_9" "task2__9" 12 DAQ 0xFF 0 0 0 CONSISTENCY DAQ /end EVENT +/begin EVENT "task2_static" "task2_st" 13 DAQ 0xFF 0 0 0 CONSISTENCY DAQ /end EVENT /end DAQ @@ -123,32 +156,72 @@ /end IF_DATA -/begin GROUP Cal "" /begin REF_CHARACTERISTIC /end REF_CHARACTERISTIC /end GROUP +/begin CHARACTERISTIC calpage00.task1_cycle_time_us "task1 cycle time" VALUE 0x14800C U32 0 NO_COMPU_METHOD 0 4294967295 PHYS_UNIT "us" ECU_ADDRESS_EXTENSION 1 /end CHARACTERISTIC +/begin CHARACTERISTIC calpage00.task2_cycle_time_us "task2 cycle time" VALUE 0x148010 U32 0 NO_COMPU_METHOD 0 4294967295 PHYS_UNIT "us" ECU_ADDRESS_EXTENSION 1 /end CHARACTERISTIC +/begin CHARACTERISTIC static_vars.test_f64 "Test static f64" VALUE 0x14827C F32 0 NO_COMPU_METHOD -1000000000000 1000000000000 ECU_ADDRESS_EXTENSION 1 /begin IF_DATA XCP /begin DAQ_EVENT FIXED_EVENT_LIST EVENT 2 /end DAQ_EVENT /end IF_DATA /end CHARACTERISTIC +/begin CHARACTERISTIC static_vars.test_u32 "Test static u32" VALUE 0x148278 U32 0 NO_COMPU_METHOD 0 4294967295 ECU_ADDRESS_EXTENSION 1 /begin IF_DATA XCP /begin DAQ_EVENT FIXED_EVENT_LIST EVENT 2 /end DAQ_EVENT /end IF_DATA /end CHARACTERISTIC +/begin GROUP Cal "" /begin REF_CHARACTERISTIC calpage00.task1_cycle_time_us calpage00.task2_cycle_time_us static_vars.test_f64 static_vars.test_u32 /end REF_CHARACTERISTIC /end GROUP + +/begin CHARACTERISTIC CalPage.cycle_time_ms "main task cycle time" VALUE 0x80010000 U16 0 NO_COMPU_METHOD 0 65535 PHYS_UNIT "ms" /end CHARACTERISTIC +/begin CHARACTERISTIC CalPage.run "" VALUE 0x80010002 U8 0 NO_COMPU_METHOD 0 1 PHYS_UNIT "bool" /end CHARACTERISTIC +/begin CHARACTERISTIC CalPage.run1 "" VALUE 0x80010003 U8 0 NO_COMPU_METHOD 0 1 PHYS_UNIT "bool" /end CHARACTERISTIC +/begin CHARACTERISTIC CalPage.run2 "" VALUE 0x80010004 U8 0 NO_COMPU_METHOD 0 1 PHYS_UNIT "bool" /end CHARACTERISTIC +/begin GROUP CalPage "" /begin REF_CHARACTERISTIC CalPage.cycle_time_ms CalPage.run CalPage.run1 CalPage.run2 /end REF_CHARACTERISTIC /end GROUP + +/begin CHARACTERISTIC CalPage1.TestInts.test_bool "" VALUE 0x8002002A U8 0 NO_COMPU_METHOD 0 255 /end CHARACTERISTIC +/begin CHARACTERISTIC CalPage1.TestInts.test_f32 "" VALUE 0x80020020 F32 0 NO_COMPU_METHOD -1000000000000 1000000000000 /end CHARACTERISTIC +/begin CHARACTERISTIC CalPage1.TestInts.test_f64 "" VALUE 0x80020010 F64 0 NO_COMPU_METHOD -1000000000000 1000000000000 /end CHARACTERISTIC +/begin CHARACTERISTIC CalPage1.TestInts.test_i16 "" VALUE 0x80020026 S16 0 NO_COMPU_METHOD -32768 32767 /end CHARACTERISTIC +/begin CHARACTERISTIC CalPage1.TestInts.test_i32 "" VALUE 0x8002001C S32 0 NO_COMPU_METHOD -2147483648 2147483647 /end CHARACTERISTIC +/begin CHARACTERISTIC CalPage1.TestInts.test_i64 "" VALUE 0x80020008 S64 0 NO_COMPU_METHOD -1000000000000 1000000000000 /end CHARACTERISTIC +/begin CHARACTERISTIC CalPage1.TestInts.test_i8 "" VALUE 0x80020029 S8 0 NO_COMPU_METHOD -128 127 /end CHARACTERISTIC +/begin CHARACTERISTIC CalPage1.TestInts.test_u16 "" VALUE 0x80020024 U16 0 NO_COMPU_METHOD 0 65535 /end CHARACTERISTIC +/begin CHARACTERISTIC CalPage1.TestInts.test_u32 "" VALUE 0x80020018 U32 0 NO_COMPU_METHOD 0 4294967295 /end CHARACTERISTIC +/begin CHARACTERISTIC CalPage1.TestInts.test_u64 "" VALUE 0x80020000 U64 0 NO_COMPU_METHOD 0 1000000000000 /end CHARACTERISTIC +/begin CHARACTERISTIC CalPage1.TestInts.test_u8 "" VALUE 0x80020028 U8 0 NO_COMPU_METHOD 0 255 /end CHARACTERISTIC +/begin CHARACTERISTIC CalPage1.counter_max "" VALUE 0x80020030 U32 0 NO_COMPU_METHOD 0 4294967295 /end CHARACTERISTIC +/begin GROUP CalPage1 "" /begin REF_CHARACTERISTIC CalPage1.TestInts.test_bool CalPage1.TestInts.test_f32 CalPage1.TestInts.test_f64 CalPage1.TestInts.test_i16 CalPage1.TestInts.test_i32 CalPage1.TestInts.test_i64 CalPage1.TestInts.test_i8 CalPage1.TestInts.test_u16 CalPage1.TestInts.test_u32 CalPage1.TestInts.test_u64 CalPage1.TestInts.test_u8 CalPage1.counter_max /end REF_CHARACTERISTIC /end GROUP -/begin CHARACTERISTIC CalPage1.TestInts.test_bool "" VALUE 0x8001002A U8 0 NO_COMPU_METHOD 0 255 /end CHARACTERISTIC -/begin CHARACTERISTIC CalPage1.TestInts.test_f32 "" VALUE 0x80010020 F32 0 NO_COMPU_METHOD -1000000000000 1000000000000 /end CHARACTERISTIC -/begin CHARACTERISTIC CalPage1.TestInts.test_f64 "" VALUE 0x80010010 F64 0 NO_COMPU_METHOD -1000000000000 1000000000000 /end CHARACTERISTIC -/begin CHARACTERISTIC CalPage1.TestInts.test_i16 "" VALUE 0x80010026 S16 0 NO_COMPU_METHOD -32768 32767 /end CHARACTERISTIC -/begin CHARACTERISTIC CalPage1.TestInts.test_i32 "" VALUE 0x8001001C S32 0 NO_COMPU_METHOD -2147483648 2147483647 /end CHARACTERISTIC -/begin CHARACTERISTIC CalPage1.TestInts.test_i64 "" VALUE 0x80010008 S64 0 NO_COMPU_METHOD -1000000000000 1000000000000 /end CHARACTERISTIC -/begin CHARACTERISTIC CalPage1.TestInts.test_i8 "" VALUE 0x80010029 S8 0 NO_COMPU_METHOD -128 127 /end CHARACTERISTIC -/begin CHARACTERISTIC CalPage1.TestInts.test_u16 "" VALUE 0x80010024 U16 0 NO_COMPU_METHOD 0 65535 /end CHARACTERISTIC -/begin CHARACTERISTIC CalPage1.TestInts.test_u32 "" VALUE 0x80010018 U32 0 NO_COMPU_METHOD 0 4294967295 /end CHARACTERISTIC -/begin CHARACTERISTIC CalPage1.TestInts.test_u64 "" VALUE 0x80010000 U64 0 NO_COMPU_METHOD 0 1000000000000 /end CHARACTERISTIC -/begin CHARACTERISTIC CalPage1.TestInts.test_u8 "" VALUE 0x80010028 U8 0 NO_COMPU_METHOD 0 255 /end CHARACTERISTIC -/begin CHARACTERISTIC CalPage1.cal_test "" VALUE 0x80010000 U64 0 NO_COMPU_METHOD 0 1000000000000 /end CHARACTERISTIC -/begin CHARACTERISTIC CalPage1.counter_max "" VALUE 0x80010038 U32 0 NO_COMPU_METHOD 0 4294967295 /end CHARACTERISTIC -/begin CHARACTERISTIC CalPage1.cycle_time_us "" VALUE 0x8001003C U32 0 NO_COMPU_METHOD 0 4294967295 /end CHARACTERISTIC -/begin CHARACTERISTIC CalPage1.page "" VALUE 0x80010040 U8 0 NO_COMPU_METHOD 0 255 /end CHARACTERISTIC -/begin CHARACTERISTIC CalPage1.run "" VALUE 0x80010041 U8 0 NO_COMPU_METHOD 0 255 /end CHARACTERISTIC -/begin GROUP cal_seg "" /begin REF_CHARACTERISTIC CalPage1.TestInts.test_bool CalPage1.TestInts.test_f32 CalPage1.TestInts.test_f64 CalPage1.TestInts.test_i16 CalPage1.TestInts.test_i32 CalPage1.TestInts.test_i64 CalPage1.TestInts.test_i8 CalPage1.TestInts.test_u16 CalPage1.TestInts.test_u32 CalPage1.TestInts.test_u64 CalPage1.TestInts.test_u8 CalPage1.cal_test CalPage1.counter_max CalPage1.cycle_time_us CalPage1.page CalPage1.run /end REF_CHARACTERISTIC /end GROUP +/begin CHARACTERISTIC CalPage2.ampl "Amplitude" VALUE 0x80030080 F64 0 NO_COMPU_METHOD 0 400 PHYS_UNIT "Volt" /end CHARACTERISTIC +/begin CHARACTERISTIC CalPage2.array "Demo curve" CURVE 0x80030000 F64 0 NO_COMPU_METHOD 0 100 /begin AXIS_DESCR FIX_AXIS NO_INPUT_QUANTITY NO_COMPU_METHOD 16 0 15 FIX_AXIS_PAR_DIST 0 1 16 /end AXIS_DESCR PHYS_UNIT "ms" /end CHARACTERISTIC +/begin CHARACTERISTIC CalPage2.map "Demo map" MAP 0x80030090 U8 0 NO_COMPU_METHOD -100 100 /begin AXIS_DESCR FIX_AXIS NO_INPUT_QUANTITY NO_COMPU_METHOD 8 0 7 FIX_AXIS_PAR_DIST 0 1 8 /end AXIS_DESCR /begin AXIS_DESCR FIX_AXIS NO_INPUT_QUANTITY NO_COMPU_METHOD 9 0 8 FIX_AXIS_PAR_DIST 0 1 9 /end AXIS_DESCR PHYS_UNIT "ms" /end CHARACTERISTIC +/begin CHARACTERISTIC CalPage2.period "Period" VALUE 0x80030088 F64 0 NO_COMPU_METHOD 0 1000 PHYS_UNIT "s" /end CHARACTERISTIC +/begin GROUP CalPage2 "" /begin REF_CHARACTERISTIC CalPage2.ampl CalPage2.array CalPage2.map CalPage2.period /end REF_CHARACTERISTIC /end GROUP -/begin MEASUREMENT cal_test "" A_UINT64 NO_COMPU_METHOD 0 0 0 1000000000000 PHYS_UNIT "" ECU_ADDRESS 0x4 ECU_ADDRESS_EXTENSION 2 /begin IF_DATA XCP /begin DAQ_EVENT FIXED_EVENT_LIST EVENT 0 /end DAQ_EVENT /end IF_DATA /end MEASUREMENT -/begin MEASUREMENT counter "" ULONG NO_COMPU_METHOD 0 0 0 4294967295 PHYS_UNIT "" ECU_ADDRESS 0x10 ECU_ADDRESS_EXTENSION 2 /begin IF_DATA XCP /begin DAQ_EVENT FIXED_EVENT_LIST EVENT 0 /end DAQ_EVENT /end IF_DATA /end MEASUREMENT -/begin MEASUREMENT counter_max "" ULONG NO_COMPU_METHOD 0 0 0 4294967295 PHYS_UNIT "" ECU_ADDRESS 0xC ECU_ADDRESS_EXTENSION 2 /begin IF_DATA XCP /begin DAQ_EVENT FIXED_EVENT_LIST EVENT 0 /end DAQ_EVENT /end IF_DATA /end MEASUREMENT +/begin MEASUREMENT array1 "" FLOAT64_IEEE NO_COMPU_METHOD 0 0 -1000000000000 1000000000000 PHYS_UNIT "" ECU_ADDRESS 0x3F7F4 ECU_ADDRESS_EXTENSION 2 MATRIX_DIM 256 /begin IF_DATA XCP /begin DAQ_EVENT FIXED_EVENT_LIST EVENT 3 /end DAQ_EVENT /end IF_DATA /end MEASUREMENT +/begin COMPU_METHOD channel.Conv "" LINEAR "%6.3" "Volt" COEFFS_LINEAR 1 0 /end COMPU_METHOD +/begin MEASUREMENT channel "sine: f64" FLOAT64_IEEE channel.Conv 0 0 -1000000000000 1000000000000 PHYS_UNIT "Volt" ECU_ADDRESS 0xD0000 ECU_ADDRESS_EXTENSION 2 /begin IF_DATA XCP /begin DAQ_EVENT FIXED_EVENT_LIST EVENT 13 /end DAQ_EVENT /end IF_DATA /end MEASUREMENT +/begin COMPU_METHOD channel_1.Conv "" LINEAR "%6.3" "Volt" COEFFS_LINEAR 1 0 /end COMPU_METHOD +/begin MEASUREMENT channel_1 "sine: f64" FLOAT64_IEEE channel_1.Conv 0 0 -1000000000000 1000000000000 PHYS_UNIT "Volt" ECU_ADDRESS 0x40000 ECU_ADDRESS_EXTENSION 2 /begin IF_DATA XCP /begin DAQ_EVENT FIXED_EVENT_LIST EVENT 4 /end DAQ_EVENT /end IF_DATA /end MEASUREMENT +/begin COMPU_METHOD channel_2.Conv "" LINEAR "%6.3" "Volt" COEFFS_LINEAR 1 0 /end COMPU_METHOD +/begin MEASUREMENT channel_2 "sine: f64" FLOAT64_IEEE channel_2.Conv 0 0 -1000000000000 1000000000000 PHYS_UNIT "Volt" ECU_ADDRESS 0x50000 ECU_ADDRESS_EXTENSION 2 /begin IF_DATA XCP /begin DAQ_EVENT FIXED_EVENT_LIST EVENT 5 /end DAQ_EVENT /end IF_DATA /end MEASUREMENT +/begin COMPU_METHOD channel_3.Conv "" LINEAR "%6.3" "Volt" COEFFS_LINEAR 1 0 /end COMPU_METHOD +/begin MEASUREMENT channel_3 "sine: f64" FLOAT64_IEEE channel_3.Conv 0 0 -1000000000000 1000000000000 PHYS_UNIT "Volt" ECU_ADDRESS 0x60000 ECU_ADDRESS_EXTENSION 2 /begin IF_DATA XCP /begin DAQ_EVENT FIXED_EVENT_LIST EVENT 6 /end DAQ_EVENT /end IF_DATA /end MEASUREMENT +/begin COMPU_METHOD channel_4.Conv "" LINEAR "%6.3" "Volt" COEFFS_LINEAR 1 0 /end COMPU_METHOD +/begin MEASUREMENT channel_4 "sine: f64" FLOAT64_IEEE channel_4.Conv 0 0 -1000000000000 1000000000000 PHYS_UNIT "Volt" ECU_ADDRESS 0x70000 ECU_ADDRESS_EXTENSION 2 /begin IF_DATA XCP /begin DAQ_EVENT FIXED_EVENT_LIST EVENT 7 /end DAQ_EVENT /end IF_DATA /end MEASUREMENT +/begin COMPU_METHOD channel_5.Conv "" LINEAR "%6.3" "Volt" COEFFS_LINEAR 1 0 /end COMPU_METHOD +/begin MEASUREMENT channel_5 "sine: f64" FLOAT64_IEEE channel_5.Conv 0 0 -1000000000000 1000000000000 PHYS_UNIT "Volt" ECU_ADDRESS 0x80000 ECU_ADDRESS_EXTENSION 2 /begin IF_DATA XCP /begin DAQ_EVENT FIXED_EVENT_LIST EVENT 8 /end DAQ_EVENT /end IF_DATA /end MEASUREMENT +/begin COMPU_METHOD channel_6.Conv "" LINEAR "%6.3" "Volt" COEFFS_LINEAR 1 0 /end COMPU_METHOD +/begin MEASUREMENT channel_6 "sine: f64" FLOAT64_IEEE channel_6.Conv 0 0 -1000000000000 1000000000000 PHYS_UNIT "Volt" ECU_ADDRESS 0x90000 ECU_ADDRESS_EXTENSION 2 /begin IF_DATA XCP /begin DAQ_EVENT FIXED_EVENT_LIST EVENT 9 /end DAQ_EVENT /end IF_DATA /end MEASUREMENT +/begin COMPU_METHOD channel_7.Conv "" LINEAR "%6.3" "Volt" COEFFS_LINEAR 1 0 /end COMPU_METHOD +/begin MEASUREMENT channel_7 "sine: f64" FLOAT64_IEEE channel_7.Conv 0 0 -1000000000000 1000000000000 PHYS_UNIT "Volt" ECU_ADDRESS 0xA0000 ECU_ADDRESS_EXTENSION 2 /begin IF_DATA XCP /begin DAQ_EVENT FIXED_EVENT_LIST EVENT 10 /end DAQ_EVENT /end IF_DATA /end MEASUREMENT +/begin COMPU_METHOD channel_8.Conv "" LINEAR "%6.3" "Volt" COEFFS_LINEAR 1 0 /end COMPU_METHOD +/begin MEASUREMENT channel_8 "sine: f64" FLOAT64_IEEE channel_8.Conv 0 0 -1000000000000 1000000000000 PHYS_UNIT "Volt" ECU_ADDRESS 0xB0000 ECU_ADDRESS_EXTENSION 2 /begin IF_DATA XCP /begin DAQ_EVENT FIXED_EVENT_LIST EVENT 11 /end DAQ_EVENT /end IF_DATA /end MEASUREMENT +/begin COMPU_METHOD channel_9.Conv "" LINEAR "%6.3" "Volt" COEFFS_LINEAR 1 0 /end COMPU_METHOD +/begin MEASUREMENT channel_9 "sine: f64" FLOAT64_IEEE channel_9.Conv 0 0 -1000000000000 1000000000000 PHYS_UNIT "Volt" ECU_ADDRESS 0xC0000 ECU_ADDRESS_EXTENSION 2 /begin IF_DATA XCP /begin DAQ_EVENT FIXED_EVENT_LIST EVENT 12 /end DAQ_EVENT /end IF_DATA /end MEASUREMENT +/begin MEASUREMENT counter "" ULONG NO_COMPU_METHOD 0 0 0 4294967295 PHYS_UNIT "" ECU_ADDRESS 0x3F7E0 ECU_ADDRESS_EXTENSION 2 /begin IF_DATA XCP /begin DAQ_EVENT FIXED_EVENT_LIST EVENT 3 /end DAQ_EVENT /end IF_DATA /end MEASUREMENT +/begin MEASUREMENT counter_u16 "wrapping counter: u16" UWORD NO_COMPU_METHOD 0 0 0 65535 PHYS_UNIT "" ECU_ADDRESS 0x3F7E6 ECU_ADDRESS_EXTENSION 2 /begin IF_DATA XCP /begin DAQ_EVENT FIXED_EVENT_LIST EVENT 3 /end DAQ_EVENT /end IF_DATA /end MEASUREMENT +/begin MEASUREMENT counter_u32 "wrapping counter: u32" ULONG NO_COMPU_METHOD 0 0 0 4294967295 PHYS_UNIT "" ECU_ADDRESS 0x3F7E8 ECU_ADDRESS_EXTENSION 2 /begin IF_DATA XCP /begin DAQ_EVENT FIXED_EVENT_LIST EVENT 3 /end DAQ_EVENT /end IF_DATA /end MEASUREMENT +/begin MEASUREMENT counter_u64 "wrapping counter: u64" A_UINT64 NO_COMPU_METHOD 0 0 0 1000000000000 PHYS_UNIT "" ECU_ADDRESS 0x3F7EC ECU_ADDRESS_EXTENSION 2 /begin IF_DATA XCP /begin DAQ_EVENT FIXED_EVENT_LIST EVENT 3 /end DAQ_EVENT /end IF_DATA /end MEASUREMENT +/begin MEASUREMENT counter_u8 "wrapping counter: u8" UBYTE NO_COMPU_METHOD 0 0 0 255 PHYS_UNIT "" ECU_ADDRESS 0x3F7E5 ECU_ADDRESS_EXTENSION 2 /begin IF_DATA XCP /begin DAQ_EVENT FIXED_EVENT_LIST EVENT 3 /end DAQ_EVENT /end IF_DATA /end MEASUREMENT +/begin MEASUREMENT mainloop_counter1 "" A_UINT64 NO_COMPU_METHOD 0 0 0 1000000000000 PHYS_UNIT "" ECU_ADDRESS 0xFFF8 ECU_ADDRESS_EXTENSION 2 /begin IF_DATA XCP /begin DAQ_EVENT FIXED_EVENT_LIST EVENT 0 /end DAQ_EVENT /end IF_DATA /end MEASUREMENT +/begin MEASUREMENT mainloop_counter2 "" A_UINT64 NO_COMPU_METHOD 0 0 0 1000000000000 PHYS_UNIT "" ECU_ADDRESS 0x0 ECU_ADDRESS_EXTENSION 2 /begin IF_DATA XCP /begin DAQ_EVENT FIXED_EVENT_LIST EVENT 0 /end DAQ_EVENT /end IF_DATA /end MEASUREMENT +/begin MEASUREMENT mainloop_map "2D map on heap" A_UINT64 NO_COMPU_METHOD 0 0 0 1000000000000 PHYS_UNIT "" ECU_ADDRESS 0x10000 ECU_ADDRESS_EXTENSION 2 MATRIX_DIM 16 16 /begin IF_DATA XCP /begin DAQ_EVENT FIXED_EVENT_LIST EVENT 1 /end DAQ_EVENT /end IF_DATA /end MEASUREMENT -/begin GROUP task "" /begin REF_MEASUREMENT cal_test counter counter_max /end REF_MEASUREMENT /end GROUP +/begin GROUP mainloop "" /begin REF_MEASUREMENT mainloop_counter1 mainloop_counter2 /end REF_MEASUREMENT /end GROUP +/begin GROUP task1 "" /begin REF_MEASUREMENT array1 counter counter_u16 counter_u32 counter_u64 counter_u8 /end REF_MEASUREMENT /end GROUP +/begin GROUP task2_inst "" /begin REF_MEASUREMENT channel_1 channel_2 channel_3 channel_4 channel_5 channel_6 channel_7 channel_8 channel_9 /end REF_MEASUREMENT /end GROUP /end MODULE /end PROJECT diff --git a/xcplib/main_cfg.h b/xcplib/main_cfg.h index 7cbc7fc..c43ad05 100644 --- a/xcplib/main_cfg.h +++ b/xcplib/main_cfg.h @@ -8,11 +8,16 @@ Licensed under the MIT license.See LICENSE file in the project root for details. */ -// When static library is used for Rust xcp-lite, consider the following options which are compiled into it +// When static library xcplib is used for Rust xcp-lite, consider the following options which are compiled into it /* main_cfg.h: + XCP_ENABLE_DBG_PRINTS // Enable debug prints + XCP_DEFAULT_DEBUG_LEVEL // Default debug level 1-5 OPTION_MTU // UDP MTU + XCPTL_ENABLE_TCP + XCPTL_ENABLE_UDP + CLOCK_USE_APP_TIME_US or CLOCK_USE_UTC_TIME_NS // Clock resolution, TAI or ARB epoch xcptl_cfg.h: XCPTL_QUEUE_SIZE // Allocate static memory for transmit queue, an entry has XCPTL_MAX_SEGMENT_SIZE bytes @@ -31,12 +36,12 @@ #define OFF 0 // Debug prints -#define OPTION_ENABLE_DBG_PRINTS ON -#define OPTION_DEBUG_LEVEL 0 +#define XCP_ENABLE_DBG_PRINTS +#define XCP_DEFAULT_DEBUG_LEVEL 2 /*1 - Error, 2 - Warn, 3 - Info, 4 - Trace, 5 - Debug */ // Set clock resolution (for clock function in platform.c) #define CLOCK_USE_APP_TIME_US -//#define CLOCK_USE_UTC_TIME_NS +// #define CLOCK_USE_UTC_TIME_NS // Ethernet Transport Layer @@ -46,6 +51,8 @@ // TCP or/and UDP server enabled #define XCPTL_ENABLE_TCP #define XCPTL_ENABLE_UDP +// #define XCP_SERVER_FORCEFULL_TERMINATION // Otherwise use gracefull server thread termination in xcplib + // #define PLATFORM_ENABLE_GET_LOCAL_ADDR // #define PLATFORM_ENABLE_KEYBOARD diff --git a/xcplib/src/dbg_print.h b/xcplib/src/dbg_print.h index 177422e..1f204ba 100644 --- a/xcplib/src/dbg_print.h +++ b/xcplib/src/dbg_print.h @@ -9,9 +9,9 @@ //------------------------------------------------------------------------------- // Debug print -#if OPTION_ENABLE_DBG_PRINTS +#ifdef XCP_ENABLE_DBG_PRINTS -extern unsigned int gDebugLevel; +extern uint8_t gDebugLevel; #define DBG_LEVEL gDebugLevel /* diff --git a/xcplib/src/platform.c b/xcplib/src/platform.c index d3f6dba..ea59aef 100644 --- a/xcplib/src/platform.c +++ b/xcplib/src/platform.c @@ -242,7 +242,8 @@ BOOL socketBind(SOCKET sock, uint8_t* addr, uint16_t port) { return TRUE; } - +// Shutdown socket +// Block rx and tx direction BOOL socketShutdown(SOCKET sock) { if (sock != INVALID_SOCKET) { shutdown(sock, SHUT_RDWR); @@ -250,6 +251,8 @@ BOOL socketShutdown(SOCKET sock) { return TRUE; } +// Close socket +// Make addr reusable BOOL socketClose(SOCKET *sp) { if (*sp != INVALID_SOCKET) { close(*sp); @@ -463,7 +466,8 @@ BOOL socketBind(SOCKET sock, uint8_t *addr, uint16_t port) { return TRUE; } - +// Shutdown socket +// Block rx and tx direction BOOL socketShutdown(SOCKET sock) { if (sock != INVALID_SOCKET) { @@ -472,6 +476,8 @@ BOOL socketShutdown(SOCKET sock) { return TRUE; } +// Close socket +// Make addr reusable BOOL socketClose(SOCKET* sockp) { if (*sockp != INVALID_SOCKET) { diff --git a/xcplib/src/xcpEthServer.c b/xcplib/src/xcpEthServer.c index 3111c7d..9c3aed1 100644 --- a/xcplib/src/xcpEthServer.c +++ b/xcplib/src/xcpEthServer.c @@ -89,23 +89,20 @@ BOOL XcpEthServerInit(const uint8_t* addr, uint16_t port, BOOL useTCP) BOOL XcpEthServerShutdown() { +#ifdef XCP_SERVER_FORCEFULL_TERMINATION // Forcefull termination - if (gXcpServer.isInit) { + DBG_PRINT3("Disconnect, cancel threads and shutdown XCP!\n"); XcpDisconnect(); cancel_thread(gXcpServer.ReceiveThreadHandle); cancel_thread(gXcpServer.TransmitThreadHandle); - gXcpServer.ReceiveThreadRunning = FALSE; - gXcpServer.TransmitThreadRunning = FALSE; - XcpTlShutdown(); + XcpEthTlShutdown(); gXcpServer.isInit = FALSE; socketCleanup(); XcpReset(); } - - +#else // Gracefull termination - /* if (gXcpServer.isInit) { XcpDisconnect(); gXcpServer.ReceiveThreadRunning = FALSE; @@ -117,7 +114,7 @@ BOOL XcpEthServerShutdown() { socketCleanup(); XcpReset(); } - */ +#endif return TRUE; } diff --git a/xcplib/src/xcpEthTl.c b/xcplib/src/xcpEthTl.c index 577bd6c..4144ea3 100644 --- a/xcplib/src/xcpEthTl.c +++ b/xcplib/src/xcpEthTl.c @@ -708,7 +708,7 @@ BOOL XcpEthTlHandleCommands(uint32_t timeout_ms) { return handleXcpCommand(n, &msgBuf, NULL, 0); } else { - socketShutdown(gXcpTl.Sock); + socketShutdown(gXcpTl.Sock); // Let the receive thread terminate without error message return FALSE; // Should not happen } } @@ -717,7 +717,7 @@ BOOL XcpEthTlHandleCommands(uint32_t timeout_ms) { DBG_PRINT3("XCP Master closed TCP connection! XCP disconnected.\n"); XcpDisconnect(); sleepMs(100); - socketShutdown(gXcpTl.Sock); + socketShutdown(gXcpTl.Sock); // Let the receive thread terminate without error message socketClose(&gXcpTl.Sock); return TRUE; // Ok, TCP socket closed } diff --git a/xcplib/xcpAppl.c b/xcplib/xcpAppl.c index babbb2e..ab0eb4a 100644 --- a/xcplib/xcpAppl.c +++ b/xcplib/xcpAppl.c @@ -16,8 +16,8 @@ #include "xcpLite.h" #include "xcpAppl.h" -#if OPTION_ENABLE_DBG_PRINTS -unsigned int gDebugLevel = OPTION_DEBUG_LEVEL; +#ifdef XCP_ENABLE_DBG_PRINTS +uint8_t gDebugLevel = XCP_DEFAULT_DEBUG_LEVEL; #endif #ifdef XCP_ENABLE_USER_COMMAND @@ -31,7 +31,7 @@ static BOOL write_delay = FALSE; void ApplXcpSetLogLevel(uint8_t level) { gDebugLevel = level; - //DBG_PRINTF(level,"Set log level to %d\n", level); + if (level>2) DBG_PRINTF_WARNING("Set log level to %d\n", level); } /**************************************************************************/