From c96a278044742e6515eecb05d8e14670724e1867 Mon Sep 17 00:00:00 2001 From: Martin Heyden Date: Wed, 22 May 2024 12:22:09 -0400 Subject: [PATCH 01/18] added base for restbus simulation --- opendut-edgar/restbus-simulation/Cargo.toml | 11 ++ .../{ => src}/arxml_parser.rs | 19 +- .../{ => src}/arxml_structs.rs | 2 +- .../{ => src}/arxml_utils.rs | 3 +- .../src/restbus_simulation.rs | 56 ++++++ .../restbus-simulation/src/restbus_utils.rs | 179 ++++++++++++++++++ 6 files changed, 256 insertions(+), 14 deletions(-) create mode 100755 opendut-edgar/restbus-simulation/Cargo.toml rename opendut-edgar/restbus-simulation/{ => src}/arxml_parser.rs (96%) rename opendut-edgar/restbus-simulation/{ => src}/arxml_structs.rs (94%) rename opendut-edgar/restbus-simulation/{ => src}/arxml_utils.rs (97%) create mode 100755 opendut-edgar/restbus-simulation/src/restbus_simulation.rs create mode 100755 opendut-edgar/restbus-simulation/src/restbus_utils.rs diff --git a/opendut-edgar/restbus-simulation/Cargo.toml b/opendut-edgar/restbus-simulation/Cargo.toml new file mode 100755 index 00000000..7d0691a9 --- /dev/null +++ b/opendut-edgar/restbus-simulation/Cargo.toml @@ -0,0 +1,11 @@ +[package] +name = "restbus" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +autosar-data = "0.9.0" +nix = "0.25.1" +tokio = { version = "1", features = ["full"] } diff --git a/opendut-edgar/restbus-simulation/arxml_parser.rs b/opendut-edgar/restbus-simulation/src/arxml_parser.rs similarity index 96% rename from opendut-edgar/restbus-simulation/arxml_parser.rs rename to opendut-edgar/restbus-simulation/src/arxml_parser.rs index c830d7f1..b07ffc37 100755 --- a/opendut-edgar/restbus-simulation/arxml_parser.rs +++ b/opendut-edgar/restbus-simulation/src/arxml_parser.rs @@ -8,31 +8,21 @@ use crate::arxml_structs::*; use crate::arxml_utils::*; /* -- Arxml parser that is able to extract all values necessary for a restbus simulation -- See main method for usage example. + Arxml parser that is able to extract all values necessary for a restbus simulation */ /* - TODO: - - finish parsing and fill up structures - - What about TPConfig and get_init_value_from_signals and get_init_value_from_signals? - - create restbus simulation based on parsed data in a different source code file - include signal desc - Improvements at some stage: - Provide options to store parsed data for quicker restart - - Put structure defintions in separete source code file - be able to manually add stuff to restbus -> provide interface - Code inside DEBUG comments will be removed at a later stage */ -// Future restbus simulation structure used to setup and control restbus simulation. Will be moved to seprarate source code file. -/*pub struct RestbusSimulation { - -}*/ - // Parser structure pub struct ArxmlParser { } @@ -318,7 +308,7 @@ impl ArxmlParser { let frame_name = get_required_item_name( &frame, "Frame"); - let addressing_mode = if let Some(CharacterData::Enum(value)) = can_frame_triggering + let addressing_mode_str = if let Some(CharacterData::Enum(value)) = can_frame_triggering .get_sub_element(ElementName::CanAddressingMode) .and_then(|elem| elem.character_data()) { @@ -327,6 +317,11 @@ impl ArxmlParser { EnumItem::Standard.to_string() }; + let mut addressing_mode: bool = false; + if addressing_mode_str.to_uppercase() == String::from("EXTENDED") { + addressing_mode = true; + } + let frame_rx_behavior = get_optional_string( can_frame_triggering, ElementName::CanFrameRxBehavior); diff --git a/opendut-edgar/restbus-simulation/arxml_structs.rs b/opendut-edgar/restbus-simulation/src/arxml_structs.rs similarity index 94% rename from opendut-edgar/restbus-simulation/arxml_structs.rs rename to opendut-edgar/restbus-simulation/src/arxml_structs.rs index 7c69a25c..814d6668 100755 --- a/opendut-edgar/restbus-simulation/arxml_structs.rs +++ b/opendut-edgar/restbus-simulation/src/arxml_structs.rs @@ -13,7 +13,7 @@ pub struct CanFrameTriggering { pub frame_triggering_name: String, pub frame_name: String, pub can_id: i64, - pub addressing_mode: String, + pub addressing_mode: bool, pub frame_rx_behavior: String, pub frame_tx_behavior: String, pub rx_range_lower: i64, diff --git a/opendut-edgar/restbus-simulation/arxml_utils.rs b/opendut-edgar/restbus-simulation/src/arxml_utils.rs similarity index 97% rename from opendut-edgar/restbus-simulation/arxml_utils.rs rename to opendut-edgar/restbus-simulation/src/arxml_utils.rs index 6c7883be..295f7b1b 100755 --- a/opendut-edgar/restbus-simulation/arxml_utils.rs +++ b/opendut-edgar/restbus-simulation/src/arxml_utils.rs @@ -477,4 +477,5 @@ pub fn process_signal_group(signal_group: &Element, grouped_signals.push(isignal_group_struct); Some(()) -} \ No newline at end of file +} + diff --git a/opendut-edgar/restbus-simulation/src/restbus_simulation.rs b/opendut-edgar/restbus-simulation/src/restbus_simulation.rs new file mode 100755 index 00000000..a8f409b1 --- /dev/null +++ b/opendut-edgar/restbus-simulation/src/restbus_simulation.rs @@ -0,0 +1,56 @@ +use crate::arxml_structs::*; +use crate::restbus_utils::*; + +/* + Restbus simulation that makes use of the structures parsed by the ARXML parser. Makes use of the Linux Kernel CAN Broadcast Manager +*/ + +/* +- TODO: + - create restbus simulation based on parsed data in a different source code file + - use 100ms for NM pdus +*/ + + +pub struct RestbusSimulation { +} + +impl RestbusSimulation { + /*pub fn play_from_can_cluster(&self, can_cluster: &CanCluster, interface: &String) { + let mut pdus: Vec<&PDU> = Vec::new(); + + for can_frame_triggering in can_cluster.can_frame_triggerings.values() { + for pdu_mapping in &can_frame_triggering.pdu_mappings { + pdus.push(&pdu_mapping.pdu); + } + } + }*/ + + pub fn play_single_bcm_frame(&self, ifname: &String, count: u32, ival1_tv_sec: i64, ival1_tv_usec: i64, + ival2_tv_sec: i64, ival2_tv_usec: i64, can_id: u32, can_dlc: u8, addressing_mode: bool, data_vector: &Vec) -> Result + { + + let sock = create_socket()?; + + connect_socket(sock, ifname)?; + + let mut write_bytes: Vec = Vec::new(); + + let mut can_frames: Vec = Vec::new(); + + can_frames.push(create_can_frame_structure(can_id, can_dlc, addressing_mode, data_vector)); + + create_bcm_structure_bytes(count, ival1_tv_sec, ival1_tv_usec, ival2_tv_sec, ival2_tv_usec, can_id, &can_frames, &mut write_bytes); + + println!("write byte is {}", write_bytes.len()); + for byte in &write_bytes { + print!("{:02x} ", byte); + } + println!(""); + + write_socket(sock, &write_bytes, write_bytes.len())?; + + return Ok(true); + + } +} \ No newline at end of file diff --git a/opendut-edgar/restbus-simulation/src/restbus_utils.rs b/opendut-edgar/restbus-simulation/src/restbus_utils.rs new file mode 100755 index 00000000..d718c176 --- /dev/null +++ b/opendut-edgar/restbus-simulation/src/restbus_utils.rs @@ -0,0 +1,179 @@ +/* + HELPER METHODS just for restbus-simulation +*/ + +use nix::libc::{__c_anonymous_sockaddr_can_can_addr, __c_anonymous_sockaddr_can_tp, connect, if_nametoindex, sockaddr, sockaddr_can, socket, timeval, write, AF_CAN, CAN_BCM, CAN_EFF_FLAG, SOCK_DGRAM}; +use std::ffi::CString; +use std::mem; +use std::io::Error; +use std::slice; +use std::os::raw::c_void; + +#[repr(C)] +#[derive(Debug)] +pub struct BcmMsgHead { + opcode: u32, + flags: u32, + count: u32, + ival1: timeval, + ival2: timeval, + can_id: u32, + nframes: u32, +} + +#[repr(C, packed)] +#[derive(Debug)] +pub struct CanFrame { + can_id: u32, + can_dlc: u8, + __pad: u8, + __res0: u8, + __res1: u8, + data: [u8; 8], +} + +pub enum OPCODE { + TxSetup = 1, +/* TxDelete, + TxRead, + TxSend, + RxSetup, + RxDelete, + RxRead, + TxStatus, + TxExpired, + RxStatus, + RxTimeout, + RxChanged*/ +} + +pub enum BCMFlags { + SetTimer = 0x0001, + StartTimer = 0x0002, +/* TxCountEvt = 0x0004, + TxAnnounce = 0x0008, + TxCpCanId = 0x0010, + RxFilterId = 0x0020, + RxCheckDlc = 0x0040, + RxNoAutotimer = 0x0080, + RxAnnounceResume = 0x0100, + TxResetMultiIdx = 0x0200, + RxRtrFrame = 0x0400, + CanFdFrame = 0x0800*/ +} + + +fn vec_to_c_void(vec: &Vec) -> *const c_void { + vec.as_ptr() as *const c_void +} + +pub fn create_socket() -> Result { + let sock = unsafe { + socket(AF_CAN, SOCK_DGRAM, CAN_BCM) + }; + + if sock < 0 { + return Err(format!("Could not create socket due to {}", Error::last_os_error())); + } + + return Ok(sock); +} + +pub fn connect_socket(sock: i32, ifname: &String) -> Result { + let ifindex = unsafe { + if let Ok(c_ifname) = CString::new(ifname.as_str()) { + if_nametoindex(c_ifname.as_ptr()) + } else { + return Err(format!("Could not get ifindex from {}", ifname)); + } + }; + + let sock_addr_can_tp = __c_anonymous_sockaddr_can_tp { + rx_id: 0, + tx_id: 0 + }; + + let can_addr = __c_anonymous_sockaddr_can_can_addr { + tp: sock_addr_can_tp + }; + + let my_sockaddr: sockaddr_can = sockaddr_can { + can_family: AF_CAN as u16, + can_ifindex: ifindex as i32, + can_addr: can_addr + }; + + let sockaddr_can_ptr: *const sockaddr_can = &my_sockaddr as *const sockaddr_can; + let sockaddr_ptr = sockaddr_can_ptr as *const sockaddr; + + //let conv_addr = SockaddrLike::from_raw(my_sockaddr, Some(mem::size_of::<&sockaddr_can>() as u32)); + let connect_res = unsafe { + connect(sock, sockaddr_ptr, mem::size_of::<&sockaddr_can>() as u32) + }; + + if connect_res < 0 { + return Err(format!("Could not connect socket due to {}", Error::last_os_error())); + } + + return Ok(connect_res); +} + +pub fn write_socket(sock: i32, write_bytes: &Vec, count: usize) -> Result { + let wres = unsafe { + write(sock, vec_to_c_void(&write_bytes), count) + }; + if wres < 0 { + return Err(format!("Could not write to socket due to {}", Error::last_os_error())); + } + + return Ok(wres); +} + +pub fn create_can_frame_structure(can_id: u32, can_dlc: u8, addressing_mode: bool, data_vector: &Vec) -> CanFrame { + let mut data: [u8; 8] = [0; 8]; + + let mut i = 0; + while i < data_vector.len() { + data[i] = data_vector[i]; + i += 1; + } + + let mut eflag: u32 = 0x0; + + if addressing_mode { + eflag = CAN_EFF_FLAG; + } + + return CanFrame { + can_id: can_id | eflag, + can_dlc: can_dlc, + __pad: 0, + __res0: 0, + __res1: 0, + data: data, + }; +} + +pub fn create_bcm_structure_bytes(count: u32, ival1_tv_sec: i64, ival1_tv_usec: i64 + , ival2_tv_sec: i64, ival2_tv_usec: i64, can_id: u32, frames: &Vec, write_bytes: &mut Vec) { + let head: BcmMsgHead = BcmMsgHead { + opcode: OPCODE::TxSetup as u32, + flags: BCMFlags::SetTimer as u32 | BCMFlags::StartTimer as u32, + count: count, + ival1: timeval { tv_sec: ival1_tv_sec, tv_usec: ival1_tv_usec }, + ival2: timeval { tv_sec: ival2_tv_sec, tv_usec: ival2_tv_usec }, + can_id: can_id, + nframes: frames.len() as u32, + }; + + let ptr: *const u8 = &head as *const BcmMsgHead as *const u8; + let bytes: &[u8] = unsafe { slice::from_raw_parts(ptr, mem::size_of::()) }; + + write_bytes.extend_from_slice(bytes); + + for frame in frames { + let ptr: *const u8 = frame as *const CanFrame as *const u8; + let bytes: &[u8] = unsafe { slice::from_raw_parts(ptr, mem::size_of::()) }; + write_bytes.extend_from_slice(bytes); + } +} \ No newline at end of file From 5a93f398630beacdeecaf0c8846985db85f20ea5 Mon Sep 17 00:00:00 2001 From: Martin Heyden Date: Fri, 24 May 2024 11:10:00 -0400 Subject: [PATCH 02/18] added support for sending multiple messages --- .../restbus-simulation/src/arxml_parser.rs | 10 +- .../restbus-simulation/src/arxml_structs.rs | 6 -- .../restbus-simulation/src/arxml_utils.rs | 78 ++++++++++++++- .../src/restbus_simulation.rs | 43 +++++++-- .../restbus-simulation/src/restbus_structs.rs | 65 +++++++++++++ .../restbus-simulation/src/restbus_utils.rs | 94 +++++++------------ 6 files changed, 215 insertions(+), 81 deletions(-) create mode 100755 opendut-edgar/restbus-simulation/src/restbus_structs.rs diff --git a/opendut-edgar/restbus-simulation/src/arxml_parser.rs b/opendut-edgar/restbus-simulation/src/arxml_parser.rs index b07ffc37..9a6b3952 100755 --- a/opendut-edgar/restbus-simulation/src/arxml_parser.rs +++ b/opendut-edgar/restbus-simulation/src/arxml_parser.rs @@ -1,11 +1,13 @@ -use core::panic; +use crate::arxml_structs::*; +use crate::arxml_utils::*; + use std::time::Instant; use std::collections::HashMap; +use core::panic; + use autosar_data::{AutosarModel, CharacterData, Element, ElementName, EnumItem}; -use crate::arxml_structs::*; -use crate::arxml_utils::*; /* Arxml parser that is able to extract all values necessary for a restbus simulation @@ -145,7 +147,7 @@ impl ArxmlParser { let mut ungrouped_signals: Vec = Vec::new(); self.handle_isignals(pdu, &mut grouped_signals, &mut ungrouped_signals); - + let isginal_ipdu: ISignalIPDU = ISignalIPDU { cyclic_timing_period_value: cyclic_timing_period_value, cyclic_timing_period_tolerance: cyclic_timing_period_tolerance, diff --git a/opendut-edgar/restbus-simulation/src/arxml_structs.rs b/opendut-edgar/restbus-simulation/src/arxml_structs.rs index 814d6668..5a22401d 100755 --- a/opendut-edgar/restbus-simulation/src/arxml_structs.rs +++ b/opendut-edgar/restbus-simulation/src/arxml_structs.rs @@ -51,12 +51,6 @@ pub enum PDU { diag_pdu_type: String }*/ -/*pub struct NMPDU { // Seems to be only needed for Ethernet, not CAN - nm_signal: String, - start_pos: i64, - length: i64 -}*/ - #[derive(Debug)] pub struct ISignalIPDU { pub cyclic_timing_period_value: f64, diff --git a/opendut-edgar/restbus-simulation/src/arxml_utils.rs b/opendut-edgar/restbus-simulation/src/arxml_utils.rs index 295f7b1b..117ccb60 100755 --- a/opendut-edgar/restbus-simulation/src/arxml_utils.rs +++ b/opendut-edgar/restbus-simulation/src/arxml_utils.rs @@ -1,11 +1,14 @@ /* HELPER METHODS */ -use autosar_data::{CharacterData, Element, ElementName, EnumItem}; +use crate::arxml_structs::*; +use crate::restbus_structs::*; +use crate::restbus_utils::*; use std::collections::HashMap; -use crate::arxml_structs::*; +use autosar_data::{CharacterData, Element, ElementName, EnumItem}; + pub fn decode_integer(cdata: &CharacterData) -> Option { if let CharacterData::String(text) = cdata { @@ -479,3 +482,74 @@ pub fn process_signal_group(signal_group: &Element, Some(()) } +// should normally only add one TimedCanFrame but multiple may be added in case multiple pdu mappings exist +pub fn get_timed_can_frame(can_frame_triggering: &CanFrameTriggering, timed_can_frames: &mut Vec) { + let can_id: u32 = can_frame_triggering.can_id as u32; + let can_dlc: u8 = can_frame_triggering.frame_length as u8; + let addressing_mode: bool = can_frame_triggering.addressing_mode; + for pdu_mapping in &can_frame_triggering.pdu_mappings { + let mut count: u32 = 0; + let mut ival1_tv_sec: i64 = 0; + let mut ival1_tv_usec: i64 = 0; + let mut ival2_tv_sec: i64 = 0; + let mut ival2_tv_usec: i64 = 0; + let init_values: Vec; + match &pdu_mapping.pdu { + PDU::ISignalIPDU(pdu) => { + count = pdu.number_of_repetitions as u32; + + if pdu.repetition_period_value != 0.0 { + ival1_tv_sec = pdu.repetition_period_value.trunc() as i64; + let fraction: f64 = pdu.repetition_period_value % 1.0; + ival1_tv_usec = (fraction * 1_000_000.0).trunc() as i64; + } + + if pdu.cyclic_timing_period_value != 0.0 { + ival2_tv_sec = pdu.cyclic_timing_period_value.trunc() as i64; + let fraction: f64 = pdu.cyclic_timing_period_value % 1.0; + ival2_tv_usec = (fraction * 1_000_000.0).trunc() as i64; + } + + init_values = extract_init_values(pdu.unused_bit_pattern, + &pdu.ungrouped_signals, + &pdu.grouped_signals, + pdu_mapping.length, + &pdu_mapping.byte_order); + } + PDU::NMPDU(pdu) => { + ival2_tv_usec = 100000; // every 100 ms + init_values = extract_init_values(pdu.unused_bit_pattern, + &pdu.ungrouped_signals, + &pdu.grouped_signals, + pdu_mapping.length, + &pdu_mapping.byte_order); + } + } + timed_can_frames.push(create_time_can_frame_structure(count, ival1_tv_sec, ival1_tv_usec, ival2_tv_sec, + ival2_tv_usec, can_id, can_dlc, addressing_mode, &init_values)); + } +} + +pub fn get_timed_can_frame_from_id(can_clusters: &HashMap, bus_name: String, can_id: i64) -> Vec { + let mut timed_can_frames: Vec = Vec::new(); + + if let Some(can_cluster) = can_clusters.get(&bus_name) { + if let Some(can_frame_triggering) = can_cluster.can_frame_triggerings.get(&can_id) { + get_timed_can_frame(can_frame_triggering, &mut timed_can_frames); + } + } + + return timed_can_frames +} + +pub fn get_timed_can_frames_from_bus(can_clusters: &HashMap, bus_name: String) -> Vec { + let mut timed_can_frames: Vec = Vec::new(); + + if let Some(can_cluster) = can_clusters.get(&bus_name) { + for can_frame_triggering in can_cluster.can_frame_triggerings.values() { + get_timed_can_frame(can_frame_triggering, &mut timed_can_frames) + } + } + + return timed_can_frames +} \ No newline at end of file diff --git a/opendut-edgar/restbus-simulation/src/restbus_simulation.rs b/opendut-edgar/restbus-simulation/src/restbus_simulation.rs index a8f409b1..fdb1a4e1 100755 --- a/opendut-edgar/restbus-simulation/src/restbus_simulation.rs +++ b/opendut-edgar/restbus-simulation/src/restbus_simulation.rs @@ -1,5 +1,5 @@ -use crate::arxml_structs::*; use crate::restbus_utils::*; +use crate::restbus_structs::*; /* Restbus simulation that makes use of the structures parsed by the ARXML parser. Makes use of the Linux Kernel CAN Broadcast Manager @@ -16,17 +16,42 @@ pub struct RestbusSimulation { } impl RestbusSimulation { - /*pub fn play_from_can_cluster(&self, can_cluster: &CanCluster, interface: &String) { - let mut pdus: Vec<&PDU> = Vec::new(); + pub fn play_all(&self, timed_can_frames: &Vec, ifname: &String) -> Result { + let sock = create_socket()?; + + connect_socket(sock, ifname)?; + + let mut write_bytes_global: Vec> = Vec::new(); - for can_frame_triggering in can_cluster.can_frame_triggerings.values() { - for pdu_mapping in &can_frame_triggering.pdu_mappings { - pdus.push(&pdu_mapping.pdu); + for timed_can_frame in timed_can_frames { + let mut write_bytes: Vec = Vec::new(); + + let mut can_frames: Vec = Vec::new(); + + can_frames.push( + create_can_frame_structure(timed_can_frame.can_id, timed_can_frame.can_dlc, timed_can_frame.addressing_mode, &timed_can_frame.data_vector)); + + create_bcm_structure_bytes(timed_can_frame.count, timed_can_frame.ival1.tv_sec, timed_can_frame.ival1.tv_usec, + timed_can_frame.ival2.tv_sec, timed_can_frame.ival2.tv_usec, timed_can_frame.can_id, &can_frames, &mut write_bytes); + + println!("write byte is {}", write_bytes.len()); + for byte in &write_bytes { + print!("{:02x} ", byte); } + println!(""); + + write_bytes_global.push(write_bytes); + } - }*/ - pub fn play_single_bcm_frame(&self, ifname: &String, count: u32, ival1_tv_sec: i64, ival1_tv_usec: i64, + for write_bytes in write_bytes_global { + write_socket(sock, &write_bytes, write_bytes.len())?; + } + + return Ok(true); + } + + /*pub fn play_single_bcm_frame(&self, ifname: &String, count: u32, ival1_tv_sec: i64, ival1_tv_usec: i64, ival2_tv_sec: i64, ival2_tv_usec: i64, can_id: u32, can_dlc: u8, addressing_mode: bool, data_vector: &Vec) -> Result { @@ -52,5 +77,5 @@ impl RestbusSimulation { return Ok(true); - } + }*/ } \ No newline at end of file diff --git a/opendut-edgar/restbus-simulation/src/restbus_structs.rs b/opendut-edgar/restbus-simulation/src/restbus_structs.rs new file mode 100755 index 00000000..a6fe9475 --- /dev/null +++ b/opendut-edgar/restbus-simulation/src/restbus_structs.rs @@ -0,0 +1,65 @@ +use nix::libc::timeval; + +#[repr(C)] +#[derive(Debug)] +pub struct BcmMsgHead { + pub opcode: u32, + pub flags: u32, + pub count: u32, + pub ival1: timeval, + pub ival2: timeval, + pub can_id: u32, + pub nframes: u32, +} + +#[repr(C, packed)] +#[derive(Debug)] +pub struct CanFrame { + pub can_id: u32, + pub can_dlc: u8, + pub __pad: u8, + pub __res0: u8, + pub __res1: u8, + pub data: [u8; 8], +} + +#[derive(Debug)] +pub struct TimedCanFrame { + pub can_id: u32, + pub can_dlc: u8, + pub addressing_mode: bool, + pub data_vector: Vec, + pub count: u32, + pub ival1: timeval, + pub ival2: timeval, +} + +pub enum OPCODE { + TxSetup = 1, +/* TxDelete, + TxRead, + TxSend, + RxSetup, + RxDelete, + RxRead, + TxStatus, + TxExpired, + RxStatus, + RxTimeout, + RxChanged*/ +} + +pub enum BCMFlags { + SetTimer = 0x0001, + StartTimer = 0x0002, +/* TxCountEvt = 0x0004, + TxAnnounce = 0x0008, + TxCpCanId = 0x0010, + RxFilterId = 0x0020, + RxCheckDlc = 0x0040, + RxNoAutotimer = 0x0080, + RxAnnounceResume = 0x0100, + TxResetMultiIdx = 0x0200, + RxRtrFrame = 0x0400, + CanFdFrame = 0x0800*/ +} diff --git a/opendut-edgar/restbus-simulation/src/restbus_utils.rs b/opendut-edgar/restbus-simulation/src/restbus_utils.rs index d718c176..2e028f25 100755 --- a/opendut-edgar/restbus-simulation/src/restbus_utils.rs +++ b/opendut-edgar/restbus-simulation/src/restbus_utils.rs @@ -2,65 +2,14 @@ HELPER METHODS just for restbus-simulation */ -use nix::libc::{__c_anonymous_sockaddr_can_can_addr, __c_anonymous_sockaddr_can_tp, connect, if_nametoindex, sockaddr, sockaddr_can, socket, timeval, write, AF_CAN, CAN_BCM, CAN_EFF_FLAG, SOCK_DGRAM}; -use std::ffi::CString; -use std::mem; +use crate::restbus_structs::*; + +use std::{mem, slice}; use std::io::Error; -use std::slice; use std::os::raw::c_void; +use std::ffi::CString; -#[repr(C)] -#[derive(Debug)] -pub struct BcmMsgHead { - opcode: u32, - flags: u32, - count: u32, - ival1: timeval, - ival2: timeval, - can_id: u32, - nframes: u32, -} - -#[repr(C, packed)] -#[derive(Debug)] -pub struct CanFrame { - can_id: u32, - can_dlc: u8, - __pad: u8, - __res0: u8, - __res1: u8, - data: [u8; 8], -} - -pub enum OPCODE { - TxSetup = 1, -/* TxDelete, - TxRead, - TxSend, - RxSetup, - RxDelete, - RxRead, - TxStatus, - TxExpired, - RxStatus, - RxTimeout, - RxChanged*/ -} - -pub enum BCMFlags { - SetTimer = 0x0001, - StartTimer = 0x0002, -/* TxCountEvt = 0x0004, - TxAnnounce = 0x0008, - TxCpCanId = 0x0010, - RxFilterId = 0x0020, - RxCheckDlc = 0x0040, - RxNoAutotimer = 0x0080, - RxAnnounceResume = 0x0100, - TxResetMultiIdx = 0x0200, - RxRtrFrame = 0x0400, - CanFdFrame = 0x0800*/ -} +use nix::libc::{__c_anonymous_sockaddr_can_can_addr, __c_anonymous_sockaddr_can_tp, connect, if_nametoindex, sockaddr, sockaddr_can, socket, timeval, write, AF_CAN, CAN_BCM, CAN_EFF_FLAG, SOCK_DGRAM}; fn vec_to_c_void(vec: &Vec) -> *const c_void { @@ -154,9 +103,29 @@ pub fn create_can_frame_structure(can_id: u32, can_dlc: u8, addressing_mode: boo }; } -pub fn create_bcm_structure_bytes(count: u32, ival1_tv_sec: i64, ival1_tv_usec: i64 - , ival2_tv_sec: i64, ival2_tv_usec: i64, can_id: u32, frames: &Vec, write_bytes: &mut Vec) { - let head: BcmMsgHead = BcmMsgHead { +pub fn create_time_can_frame_structure(count: u32, ival1_tv_sec: i64, ival1_tv_usec: i64, ival2_tv_sec: i64, + ival2_tv_usec: i64, can_id: u32, can_dlc: u8, addressing_mode: bool, data_vector: &Vec) -> TimedCanFrame { + let mut copy_data_vector: Vec = Vec::new(); + + for data in data_vector { + copy_data_vector.push(*data); + } + + return TimedCanFrame { + can_id: can_id, + can_dlc: can_dlc, + addressing_mode: addressing_mode, + data_vector: copy_data_vector, + count: count, + ival1: timeval { tv_sec: ival1_tv_sec, tv_usec: ival1_tv_usec }, + ival2: timeval { tv_sec: ival2_tv_sec, tv_usec: ival2_tv_usec }, + } + +} + +pub fn create_bcm_head(count: u32, ival1_tv_sec: i64, ival1_tv_usec: i64 + , ival2_tv_sec: i64, ival2_tv_usec: i64, can_id: u32, frames: &Vec) -> BcmMsgHead{ + return BcmMsgHead { opcode: OPCODE::TxSetup as u32, flags: BCMFlags::SetTimer as u32 | BCMFlags::StartTimer as u32, count: count, @@ -165,7 +134,12 @@ pub fn create_bcm_structure_bytes(count: u32, ival1_tv_sec: i64, ival1_tv_usec: can_id: can_id, nframes: frames.len() as u32, }; - +} + +pub fn create_bcm_structure_bytes(count: u32, ival1_tv_sec: i64, ival1_tv_usec: i64 + , ival2_tv_sec: i64, ival2_tv_usec: i64, can_id: u32, frames: &Vec, write_bytes: &mut Vec) { + let head: BcmMsgHead = create_bcm_head(count, ival1_tv_sec, ival1_tv_usec, ival2_tv_sec, ival2_tv_usec, can_id, frames); + let ptr: *const u8 = &head as *const BcmMsgHead as *const u8; let bytes: &[u8] = unsafe { slice::from_raw_parts(ptr, mem::size_of::()) }; From be0b1dae65ee908b10660d2243cd7ca7eb717c13 Mon Sep 17 00:00:00 2001 From: Martin Heyden Date: Fri, 24 May 2024 15:14:43 -0400 Subject: [PATCH 03/18] added canfd support for restbus simulation --- .../restbus-simulation/src/arxml_parser.rs | 12 ++- .../restbus-simulation/src/arxml_structs.rs | 4 +- .../restbus-simulation/src/arxml_utils.rs | 5 +- .../src/restbus_simulation.rs | 19 ++--- .../restbus-simulation/src/restbus_structs.rs | 28 +++++-- .../restbus-simulation/src/restbus_utils.rs | 75 +++++++++++++------ 6 files changed, 102 insertions(+), 41 deletions(-) diff --git a/opendut-edgar/restbus-simulation/src/arxml_parser.rs b/opendut-edgar/restbus-simulation/src/arxml_parser.rs index 9a6b3952..5deb7971 100755 --- a/opendut-edgar/restbus-simulation/src/arxml_parser.rs +++ b/opendut-edgar/restbus-simulation/src/arxml_parser.rs @@ -324,13 +324,21 @@ impl ArxmlParser { addressing_mode = true; } - let frame_rx_behavior = get_optional_string( + let mut frame_rx_behavior = false; + let frame_rx_behavior_str = get_required_string( can_frame_triggering, ElementName::CanFrameRxBehavior); + if frame_rx_behavior_str.to_uppercase() == String::from("CAN-FD") { + frame_rx_behavior = true; + } - let frame_tx_behavior = get_optional_string( + let mut frame_tx_behavior = false; + let frame_tx_behavior_str = get_required_string( can_frame_triggering, ElementName::CanFrameTxBehavior); + if frame_tx_behavior_str.to_uppercase() == String::from("CAN-FD") { + frame_tx_behavior = true; + } let mut rx_range_lower: i64 = 0; let mut rx_range_upper: i64 = 0; diff --git a/opendut-edgar/restbus-simulation/src/arxml_structs.rs b/opendut-edgar/restbus-simulation/src/arxml_structs.rs index 5a22401d..689e5346 100755 --- a/opendut-edgar/restbus-simulation/src/arxml_structs.rs +++ b/opendut-edgar/restbus-simulation/src/arxml_structs.rs @@ -14,8 +14,8 @@ pub struct CanFrameTriggering { pub frame_name: String, pub can_id: i64, pub addressing_mode: bool, - pub frame_rx_behavior: String, - pub frame_tx_behavior: String, + pub frame_rx_behavior: bool, + pub frame_tx_behavior: bool, pub rx_range_lower: i64, pub rx_range_upper: i64, pub sender_ecus: Vec, diff --git a/opendut-edgar/restbus-simulation/src/arxml_utils.rs b/opendut-edgar/restbus-simulation/src/arxml_utils.rs index 117ccb60..101f89cb 100755 --- a/opendut-edgar/restbus-simulation/src/arxml_utils.rs +++ b/opendut-edgar/restbus-simulation/src/arxml_utils.rs @@ -485,8 +485,9 @@ pub fn process_signal_group(signal_group: &Element, // should normally only add one TimedCanFrame but multiple may be added in case multiple pdu mappings exist pub fn get_timed_can_frame(can_frame_triggering: &CanFrameTriggering, timed_can_frames: &mut Vec) { let can_id: u32 = can_frame_triggering.can_id as u32; - let can_dlc: u8 = can_frame_triggering.frame_length as u8; + let len: u8 = can_frame_triggering.frame_length as u8; let addressing_mode: bool = can_frame_triggering.addressing_mode; + let frame_tx_behavior: bool = can_frame_triggering.frame_tx_behavior; for pdu_mapping in &can_frame_triggering.pdu_mappings { let mut count: u32 = 0; let mut ival1_tv_sec: i64 = 0; @@ -526,7 +527,7 @@ pub fn get_timed_can_frame(can_frame_triggering: &CanFrameTriggering, timed_can_ } } timed_can_frames.push(create_time_can_frame_structure(count, ival1_tv_sec, ival1_tv_usec, ival2_tv_sec, - ival2_tv_usec, can_id, can_dlc, addressing_mode, &init_values)); + ival2_tv_usec, can_id, len, addressing_mode, frame_tx_behavior, &init_values)); } } diff --git a/opendut-edgar/restbus-simulation/src/restbus_simulation.rs b/opendut-edgar/restbus-simulation/src/restbus_simulation.rs index fdb1a4e1..e8ca255b 100755 --- a/opendut-edgar/restbus-simulation/src/restbus_simulation.rs +++ b/opendut-edgar/restbus-simulation/src/restbus_simulation.rs @@ -29,30 +29,31 @@ impl RestbusSimulation { let mut can_frames: Vec = Vec::new(); can_frames.push( - create_can_frame_structure(timed_can_frame.can_id, timed_can_frame.can_dlc, timed_can_frame.addressing_mode, &timed_can_frame.data_vector)); + create_can_frame_structure(timed_can_frame.can_id, timed_can_frame.len, timed_can_frame.addressing_mode, timed_can_frame.frame_tx_behavior, &timed_can_frame.data_vector)); create_bcm_structure_bytes(timed_can_frame.count, timed_can_frame.ival1.tv_sec, timed_can_frame.ival1.tv_usec, - timed_can_frame.ival2.tv_sec, timed_can_frame.ival2.tv_usec, timed_can_frame.can_id, &can_frames, &mut write_bytes); + timed_can_frame.ival2.tv_sec, timed_can_frame.ival2.tv_usec, timed_can_frame.can_id, timed_can_frame.frame_tx_behavior, &can_frames, &mut write_bytes); + write_bytes_global.push(write_bytes); + + } + + for write_bytes in write_bytes_global { println!("write byte is {}", write_bytes.len()); for byte in &write_bytes { print!("{:02x} ", byte); } println!(""); - write_bytes_global.push(write_bytes); - - } - - for write_bytes in write_bytes_global { write_socket(sock, &write_bytes, write_bytes.len())?; + println!("successfully wrote to socket"); } return Ok(true); } /*pub fn play_single_bcm_frame(&self, ifname: &String, count: u32, ival1_tv_sec: i64, ival1_tv_usec: i64, - ival2_tv_sec: i64, ival2_tv_usec: i64, can_id: u32, can_dlc: u8, addressing_mode: bool, data_vector: &Vec) -> Result + ival2_tv_sec: i64, ival2_tv_usec: i64, can_id: u32, len: u8, addressing_mode: bool, data_vector: &Vec) -> Result { let sock = create_socket()?; @@ -63,7 +64,7 @@ impl RestbusSimulation { let mut can_frames: Vec = Vec::new(); - can_frames.push(create_can_frame_structure(can_id, can_dlc, addressing_mode, data_vector)); + can_frames.push(create_can_frame_structure(can_id, len, addressing_mode, data_vector)); create_bcm_structure_bytes(count, ival1_tv_sec, ival1_tv_usec, ival2_tv_sec, ival2_tv_usec, can_id, &can_frames, &mut write_bytes); diff --git a/opendut-edgar/restbus-simulation/src/restbus_structs.rs b/opendut-edgar/restbus-simulation/src/restbus_structs.rs index a6fe9475..751c0360 100755 --- a/opendut-edgar/restbus-simulation/src/restbus_structs.rs +++ b/opendut-edgar/restbus-simulation/src/restbus_structs.rs @@ -12,22 +12,40 @@ pub struct BcmMsgHead { pub nframes: u32, } +#[derive(Debug)] +pub enum CanFrame { + Can20Frame(Can20Frame), + CanFdFrame(CanFdFrame) +} + #[repr(C, packed)] #[derive(Debug)] -pub struct CanFrame { +pub struct Can20Frame { pub can_id: u32, - pub can_dlc: u8, + pub len: u8, pub __pad: u8, pub __res0: u8, pub __res1: u8, pub data: [u8; 8], } +#[repr(C, packed)] +#[derive(Debug)] +pub struct CanFdFrame { + pub can_id: u32, + pub len: u8, + pub flags: u8, + pub __res0: u8, + pub __res1: u8, + pub data: [u8; 64], +} + #[derive(Debug)] pub struct TimedCanFrame { pub can_id: u32, - pub can_dlc: u8, + pub len: u8, pub addressing_mode: bool, + pub frame_tx_behavior: bool, pub data_vector: Vec, pub count: u32, pub ival1: timeval, @@ -60,6 +78,6 @@ pub enum BCMFlags { RxNoAutotimer = 0x0080, RxAnnounceResume = 0x0100, TxResetMultiIdx = 0x0200, - RxRtrFrame = 0x0400, - CanFdFrame = 0x0800*/ + RxRtrFrame = 0x0400,*/ + CanFdFrame = 0x0800 } diff --git a/opendut-edgar/restbus-simulation/src/restbus_utils.rs b/opendut-edgar/restbus-simulation/src/restbus_utils.rs index 2e028f25..28759594 100755 --- a/opendut-edgar/restbus-simulation/src/restbus_utils.rs +++ b/opendut-edgar/restbus-simulation/src/restbus_utils.rs @@ -78,33 +78,52 @@ pub fn write_socket(sock: i32, write_bytes: &Vec, count: usize) -> Result) -> CanFrame { - let mut data: [u8; 8] = [0; 8]; - +fn fill_data_array(data: &mut [u8], data_vector: &Vec) { let mut i = 0; while i < data_vector.len() { data[i] = data_vector[i]; i += 1; } +} +pub fn create_can_frame_structure(can_id: u32, len: u8, addressing_mode: bool, frame_tx_behavior: bool, data_vector: &Vec) -> CanFrame { let mut eflag: u32 = 0x0; if addressing_mode { eflag = CAN_EFF_FLAG; } - return CanFrame { - can_id: can_id | eflag, - can_dlc: can_dlc, - __pad: 0, - __res0: 0, - __res1: 0, - data: data, - }; + if frame_tx_behavior { + let mut data: [u8; 64] = [0; 64]; + + fill_data_array(&mut data, data_vector); + + return CanFrame::CanFdFrame( CanFdFrame { + can_id: can_id | eflag, + len: len, + flags: 0, // are there any relevant flags? + __res0: 0, + __res1: 0, + data: data, + }); + } else { + let mut data: [u8; 8] = [0; 8]; + + fill_data_array(&mut data, data_vector); + + return CanFrame::Can20Frame( Can20Frame { + can_id: can_id | eflag, + len: len, + __pad: 0, + __res0: 0, + __res1: 0, + data: data, + }); + } } pub fn create_time_can_frame_structure(count: u32, ival1_tv_sec: i64, ival1_tv_usec: i64, ival2_tv_sec: i64, - ival2_tv_usec: i64, can_id: u32, can_dlc: u8, addressing_mode: bool, data_vector: &Vec) -> TimedCanFrame { + ival2_tv_usec: i64, can_id: u32, len: u8, addressing_mode: bool, frame_tx_behavior: bool, data_vector: &Vec) -> TimedCanFrame { let mut copy_data_vector: Vec = Vec::new(); for data in data_vector { @@ -113,21 +132,26 @@ pub fn create_time_can_frame_structure(count: u32, ival1_tv_sec: i64, ival1_tv_u return TimedCanFrame { can_id: can_id, - can_dlc: can_dlc, + len: len, addressing_mode: addressing_mode, + frame_tx_behavior: frame_tx_behavior, data_vector: copy_data_vector, count: count, ival1: timeval { tv_sec: ival1_tv_sec, tv_usec: ival1_tv_usec }, ival2: timeval { tv_sec: ival2_tv_sec, tv_usec: ival2_tv_usec }, } - } pub fn create_bcm_head(count: u32, ival1_tv_sec: i64, ival1_tv_usec: i64 - , ival2_tv_sec: i64, ival2_tv_usec: i64, can_id: u32, frames: &Vec) -> BcmMsgHead{ + , ival2_tv_sec: i64, ival2_tv_usec: i64, can_id: u32, frame_tx_behavior: bool, frames: &Vec) -> BcmMsgHead { + let mut canfd_flag: u32 = 0x0; + + if frame_tx_behavior { + canfd_flag = BCMFlags::CanFdFrame as u32; + } return BcmMsgHead { opcode: OPCODE::TxSetup as u32, - flags: BCMFlags::SetTimer as u32 | BCMFlags::StartTimer as u32, + flags: BCMFlags::SetTimer as u32 | BCMFlags::StartTimer as u32 | canfd_flag, count: count, ival1: timeval { tv_sec: ival1_tv_sec, tv_usec: ival1_tv_usec }, ival2: timeval { tv_sec: ival2_tv_sec, tv_usec: ival2_tv_usec }, @@ -137,8 +161,8 @@ pub fn create_bcm_head(count: u32, ival1_tv_sec: i64, ival1_tv_usec: i64 } pub fn create_bcm_structure_bytes(count: u32, ival1_tv_sec: i64, ival1_tv_usec: i64 - , ival2_tv_sec: i64, ival2_tv_usec: i64, can_id: u32, frames: &Vec, write_bytes: &mut Vec) { - let head: BcmMsgHead = create_bcm_head(count, ival1_tv_sec, ival1_tv_usec, ival2_tv_sec, ival2_tv_usec, can_id, frames); + , ival2_tv_sec: i64, ival2_tv_usec: i64, can_id: u32, frame_tx_behavior: bool, frames: &Vec, write_bytes: &mut Vec) { + let head: BcmMsgHead = create_bcm_head(count, ival1_tv_sec, ival1_tv_usec, ival2_tv_sec, ival2_tv_usec, can_id, frame_tx_behavior, frames); let ptr: *const u8 = &head as *const BcmMsgHead as *const u8; let bytes: &[u8] = unsafe { slice::from_raw_parts(ptr, mem::size_of::()) }; @@ -146,8 +170,17 @@ pub fn create_bcm_structure_bytes(count: u32, ival1_tv_sec: i64, ival1_tv_usec: write_bytes.extend_from_slice(bytes); for frame in frames { - let ptr: *const u8 = frame as *const CanFrame as *const u8; - let bytes: &[u8] = unsafe { slice::from_raw_parts(ptr, mem::size_of::()) }; - write_bytes.extend_from_slice(bytes); + match frame { + CanFrame::Can20Frame(can20_frame) => { + let ptr: *const u8 = can20_frame as *const Can20Frame as *const u8; + let bytes: &[u8] = unsafe { slice::from_raw_parts(ptr, mem::size_of::()) }; + write_bytes.extend_from_slice(bytes); + } + CanFrame::CanFdFrame(canfd_frame) => { + let ptr: *const u8 = canfd_frame as *const CanFdFrame as *const u8; + let bytes: &[u8] = unsafe { slice::from_raw_parts(ptr, mem::size_of::()) }; + write_bytes.extend_from_slice(bytes); + } + } } } \ No newline at end of file From efa676175baee9da46542118ca2b83a4c1206991 Mon Sep 17 00:00:00 2001 From: Martin Heyden Date: Mon, 27 May 2024 07:17:05 -0400 Subject: [PATCH 04/18] added proper comments --- .../restbus-simulation/src/arxml_parser.rs | 104 ++++++++---------- .../restbus-simulation/src/arxml_structs.rs | 35 +++++- .../restbus-simulation/src/arxml_utils.rs | 88 ++++++++++++++- .../src/restbus_simulation.rs | 25 +++-- .../restbus-simulation/src/restbus_structs.rs | 23 ++++ .../restbus-simulation/src/restbus_utils.rs | 32 +++++- 6 files changed, 225 insertions(+), 82 deletions(-) diff --git a/opendut-edgar/restbus-simulation/src/arxml_parser.rs b/opendut-edgar/restbus-simulation/src/arxml_parser.rs index 5deb7971..5c2be504 100755 --- a/opendut-edgar/restbus-simulation/src/arxml_parser.rs +++ b/opendut-edgar/restbus-simulation/src/arxml_parser.rs @@ -1,3 +1,11 @@ +/* + Arxml parser that is able to extract all values necessary for a restbus simulation. + Uses autosar-data library to parse data like in this example: + https://github.com/DanielT/autosar-data/blob/main/autosar-data/examples/businfo/main.rs + Ideas for improvement: + - Provide options to store parsed data for quicker restart +*/ + use crate::arxml_structs::*; use crate::arxml_utils::*; @@ -9,30 +17,15 @@ use core::panic; use autosar_data::{AutosarModel, CharacterData, Element, ElementName, EnumItem}; -/* - Arxml parser that is able to extract all values necessary for a restbus simulation -*/ - -/* -- TODO: - - include signal desc - -- Improvements at some stage: - - Provide options to store parsed data for quicker restart - - be able to manually add stuff to restbus -> provide interface - -- Code inside DEBUG comments will be removed at a later stage -*/ - - -// Parser structure pub struct ArxmlParser { } -// Use autosar-data library to parse data like in this example: -// https://github.com/DanielT/autosar-data/blob/main/autosar-data/examples/businfo/main.rs -// Do I have to add license to this file or is project license enough? impl ArxmlParser { + /* + 1. Parses an Autosar ISignalToIPduMapping. + 2. Extracts Autosar ISignal and ISignalGroup elements. + 2. Fills the important extracted data into the signals HashMap and signal_groups vectors. + */ fn handle_isignal_to_pdu_mappings(&self, mapping: &Element, signals: &mut HashMap, signal_groups: &mut Vec) @@ -69,6 +62,10 @@ impl ArxmlParser { } } + /* + 1. Parses and processes all the ISignals defined in the parent ISignalIPdu. + 2. Fills the important extracted data into the grouped_signals and ungrouped_signals vectors of structures. + */ fn handle_isignals(&self, pdu: &Element, grouped_signals: &mut Vec, ungrouped_signals: &mut Vec) -> Option<()> { //let mut signals: HashMap, Option)> = HashMap::new(); let mut signals: HashMap = HashMap::new(); @@ -105,6 +102,10 @@ impl ArxmlParser { Some(()) } + /* + 1. Parses an Autosar ISignalIPdu element. + 2. Returns important data in a self-defined ISignalIPDU structure. + */ fn handle_isignal_ipdu(&self, pdu: &Element) -> Option { // Find out these values: ... let mut cyclic_timing_period_value: f64 = 0_f64; @@ -164,6 +165,10 @@ impl ArxmlParser { return Some(isginal_ipdu); } + /* + 1. Parses an Autosar NmPdu element + 2. Returns important data in a self-defined NMPDU structure. + */ fn handle_nm_pdu(&self, pdu: &Element) -> Option { let unused_bit_pattern = get_unused_bit_pattern(&pdu); @@ -182,43 +187,11 @@ impl ArxmlParser { return Some(nm_pdu); } - /*// Add support in future in case it is needed - fn handle_container_ipdu(&self, pdu: &Element){ - let mut container_timeout: f64 = 0.0; - - let header_type = self.get_optional_string(pdu, ElementName::HeaderType); - - if let Some(container_timeout_tmp) = pdu - .get_sub_element(ElementName::ContainerTimeout) - .and_then(|elem| elem.character_data()) - .and_then(|cdata| cdata.double_value()) - { - container_timeout = container_timeout_tmp; - } - - let container_trigger = self.get_optional_string(pdu, ElementName::ContainerTrigger); - - if let Some(contained_pdu_refs) = pdu.get_sub_element(ElementName::ContainedPduTriggeringRefs) { - for contained_ref in contained_pdu_refs.sub_elements() { - if let Some(contained_pdu) = contained_ref - .get_reference_target() - .ok() - .and_then(|elem| elem.get_sub_element(ElementName::IPduRef)) - .and_then(|elem| elem.get_reference_target().ok()) - { - let pdu_name = self.get_required_item_name(&contained_pdu, "ContainedPDU"); - display_pdu(&contained_pdu, indent + 1); - } - } - } - //... - }*/ - - /*// Add support in future in case it is needed - fn handle_secured_ipdu(&self, pdu: &Element){ - - }*/ - + /* + 1. Resolves the reference inside a PduToFrameMapping to get the PDU element. + 2. Parses the Autosar PDU element + 3. Returns important data in a self-defined PDU mapping structure. + */ fn handle_pdu_mapping(&self, pdu_mapping: &Element) -> Result { let pdu = get_required_reference( pdu_mapping, @@ -295,6 +268,10 @@ impl ArxmlParser { return Ok(pdu_mapping); } + /* + 1. Parses an Autosar CanFrameTriggering element. + 2. Returns important data in a self-defined CanFrameTriggering structure. + */ fn handle_can_frame_triggering(&self, can_frame_triggering: &Element) -> Result { let can_frame_triggering_name= get_required_item_name( can_frame_triggering, "CanFrameTriggering"); @@ -389,6 +366,10 @@ impl ArxmlParser { return Ok(can_frame_triggering_struct); } + /* + 1. Parses an Autosar CanCluster element + 2. Returns important data in a self-defined CanCluster structure. + */ fn handle_can_cluster(&self, can_cluster: &Element) -> Result { let can_cluster_name = get_required_item_name( can_cluster, "CanCluster"); @@ -449,9 +430,12 @@ impl ArxmlParser { return Ok(can_cluster_struct); } - // Main parsing method. Uses autosar-data libray for parsing ARXML - // In the future, it might be extended to support Etherneth, Flexray, ... - // Returns now a vector of CanCluster + /* + Main parsing method. Uses autosar-data libray for parsing ARXML. + In the future, it might be extended to support Ethernet, Flexray, ... + The resources to develop that should not be thaat high, since it is basically just extending the current parser. + Returns a vector of CanCluster structures. + */ pub fn parse_file(&self, file_name: String) -> Option> { let start = Instant::now(); diff --git a/opendut-edgar/restbus-simulation/src/arxml_structs.rs b/opendut-edgar/restbus-simulation/src/arxml_structs.rs index 689e5346..e68c99c1 100755 --- a/opendut-edgar/restbus-simulation/src/arxml_structs.rs +++ b/opendut-edgar/restbus-simulation/src/arxml_structs.rs @@ -1,5 +1,13 @@ +/* + Also see https://www.autosar.org/fileadmin/standards/R22-11/CP/AUTOSAR_TPS_SystemTemplate.pdf. +*/ + + use std::collections::HashMap; +/* + Represents important data from Autosar CanCluster element. +*/ #[derive(Debug)] pub struct CanCluster { pub name: String, @@ -8,6 +16,9 @@ pub struct CanCluster { pub can_frame_triggerings: HashMap } +/* + Represents important data from Autosar CanFrameTriggering element. +*/ #[derive(Debug)] pub struct CanFrameTriggering { pub frame_triggering_name: String, @@ -24,6 +35,9 @@ pub struct CanFrameTriggering { pub pdu_mappings: Vec } +/* + Represents important parent data from an Autosar *PDU element. +*/ #[derive(Debug)] pub struct PDUMapping { pub name: String, @@ -37,6 +51,9 @@ pub struct PDUMapping { pub pdu: PDU } +/* + Enum of all supported PDU types. +*/ #[derive(Debug)] pub enum PDU { ISignalIPDU(ISignalIPDU), @@ -47,10 +64,10 @@ pub enum PDU { // Temp(i64) } -/*pub struct DCMIPDU { // Seems to be only DoIP relevant - diag_pdu_type: String -}*/ +/* + Represents important data from an Autosar ISignalIPDU element. +*/ #[derive(Debug)] pub struct ISignalIPDU { pub cyclic_timing_period_value: f64, @@ -65,6 +82,9 @@ pub struct ISignalIPDU { pub grouped_signals: Vec, } +/* + Represents important data from an Autosar NmPdu element. +*/ #[derive(Debug)] pub struct NMPDU { pub unused_bit_pattern: bool, @@ -72,6 +92,9 @@ pub struct NMPDU { pub grouped_signals: Vec, } +/* + Represents important data from an Autosar ISignal element. +*/ #[derive(Debug)] pub struct ISignal { pub name: String, @@ -81,6 +104,9 @@ pub struct ISignal { pub init_values: InitValues } +/* + Enum of the initial values of an ISignal. +*/ #[derive(Debug)] #[derive(Clone)] pub enum InitValues { @@ -89,6 +115,9 @@ pub enum InitValues { NotExist(bool), } +/* + Represents important data from an Autosar ISignal element. +*/ #[derive(Debug)] pub struct E2EDataTransformationProps { pub transformer_name: String, diff --git a/opendut-edgar/restbus-simulation/src/arxml_utils.rs b/opendut-edgar/restbus-simulation/src/arxml_utils.rs index 101f89cb..c5ba3cce 100755 --- a/opendut-edgar/restbus-simulation/src/arxml_utils.rs +++ b/opendut-edgar/restbus-simulation/src/arxml_utils.rs @@ -1,5 +1,6 @@ /* - HELPER METHODS + HELPER METHODS. + Some are oriented on https://github.com/DanielT/autosar-data/blob/main/autosar-data/examples/businfo/main.rs. */ use crate::arxml_structs::*; use crate::restbus_structs::*; @@ -10,6 +11,10 @@ use std::collections::HashMap; use autosar_data::{CharacterData, Element, ElementName, EnumItem}; +/* + Converts a CharacterData type from the autosar_data library + Directly taken from https://github.com/DanielT/autosar-data/blob/main/autosar-data/examples/businfo/main.rs. +*/ pub fn decode_integer(cdata: &CharacterData) -> Option { if let CharacterData::String(text) = cdata { if text == "0" { @@ -37,6 +42,9 @@ pub fn decode_integer(cdata: &CharacterData) -> Option { } } +/* + Processes time-related element data (intended from a ISignalIPdu element) and returns a self-defined TimeRange struct. +*/ pub fn get_time_range(base: &Element) -> Option { let value = base .get_sub_element(ElementName::Value) @@ -61,6 +69,9 @@ pub fn get_time_range(base: &Element) -> Option { Some(TimeRange { tolerance, value }) } +/* + Gets a sub-element and tries to extract time-related information. +*/ pub fn get_sub_element_and_time_range(base: &Element, sub_elem_name: ElementName, value: &mut f64, tolerance: &mut Option) { if let Some(time_range) = base .get_sub_element(sub_elem_name) @@ -71,6 +82,9 @@ pub fn get_sub_element_and_time_range(base: &Element, sub_elem_name: ElementName } } +/* + Gets a required item name from the element. This has to be possible. +*/ pub fn get_required_item_name(element: &Element, element_name: &str) -> String { if let Some(item_name) = element.item_name() { return item_name; @@ -79,6 +93,9 @@ pub fn get_required_item_name(element: &Element, element_name: &str) -> String { } } +/* + Gets a required subsubelement from the element. This needs to succeed. +*/ pub fn get_required_sub_subelement(element: &Element, subelement_name: ElementName, sub_subelement_name: ElementName) -> Element { if let Some(sub_subelement) = element .get_sub_element(subelement_name) @@ -92,6 +109,9 @@ pub fn get_required_sub_subelement(element: &Element, subelement_name: ElementNa } } +/* + Tries to get a subelement and convert it's value to i64. +*/ pub fn get_subelement_int_value(element: &Element, subelement_name: ElementName) -> Option { return element .get_sub_element(subelement_name) @@ -99,6 +119,9 @@ pub fn get_subelement_int_value(element: &Element, subelement_name: ElementName) .and_then(|cdata| decode_integer(&cdata)); } +/* + Gets the i64 value for a element. This has to succeed. +*/ pub fn get_required_int_value(element: &Element, subelement_name: ElementName) -> i64 { if let Some(int_value) = get_subelement_int_value(element, subelement_name) { return int_value; @@ -107,6 +130,9 @@ pub fn get_required_int_value(element: &Element, subelement_name: ElementName) - } } +/* + Gets the i64 value for a element. This is optional. So, if the subelement does not exist, then 0 is returned. +*/ pub fn get_optional_int_value(element: &Element, subelement_name: ElementName) -> i64 { if let Some(int_value) = get_subelement_int_value(element, subelement_name) { return int_value; @@ -115,6 +141,9 @@ pub fn get_optional_int_value(element: &Element, subelement_name: ElementName) - } } +/* + Resolves a reference and returns the target Element. This has to succeed. +*/ pub fn get_required_reference(element: &Element, subelement_name: ElementName) -> Element { if let Some(subelement) = element.get_sub_element(subelement_name) { match subelement.get_reference_target() { @@ -126,6 +155,9 @@ pub fn get_required_reference(element: &Element, subelement_name: ElementName) - panic!("Error getting required reference for {}", subelement_name); } +/* + Tries to get a subelement and return it's String value. +*/ pub fn get_subelement_string_value(element: &Element, subelement_name: ElementName) -> Option { return element .get_sub_element(subelement_name) @@ -133,6 +165,9 @@ pub fn get_subelement_string_value(element: &Element, subelement_name: ElementNa .map(|cdata| cdata.to_string()); } +/* + Gets the String value for a element. This has to succeed. +*/ pub fn get_required_string(element: &Element, subelement_name: ElementName) -> String { if let Some(value) = get_subelement_string_value(element, subelement_name) { return value; @@ -141,6 +176,9 @@ pub fn get_required_string(element: &Element, subelement_name: ElementName) -> S } } +/* + Gets the String value for a element. This is optional. So, if the subelement does not exist, then "" is returned. +*/ pub fn get_optional_string(element: &Element, subelement_name: ElementName) -> String { if let Some(value) = get_subelement_string_value(element, subelement_name) { return value; @@ -149,6 +187,9 @@ pub fn get_optional_string(element: &Element, subelement_name: ElementName) -> S } } +/* + Gets the String value of a subsubelement. In case the subelement or subsubelement do not exist, then "" is returned. +*/ pub fn get_subelement_optional_string(element: &Element, subelement_name: ElementName, sub_subelement_name: ElementName) -> String { if let Some(value) = element.get_sub_element(subelement_name) .and_then(|elem| elem.get_sub_element(sub_subelement_name)) @@ -161,6 +202,9 @@ pub fn get_subelement_optional_string(element: &Element, subelement_name: Elemen } } +/* + Retrieves the ECU name by resolving multiple references. +*/ pub fn ecu_of_frame_port(frame_port: &Element) -> Option { let ecu_comm_port_instance = frame_port.parent().ok()??; let comm_connector = ecu_comm_port_instance.parent().ok()??; @@ -169,7 +213,10 @@ pub fn ecu_of_frame_port(frame_port: &Element) -> Option { ecu_instance.item_name() } -// 1: Big Endian, 0: Little Endian +/* + Helper method comparing a given String with a byte order indicator. + Returns true for Big Endian, false for Little Endian +*/ pub fn get_byte_order(byte_order: &String) -> bool { if byte_order.eq("MOST-SIGNIFICANT-BYTE-LAST") { return false; @@ -177,9 +224,12 @@ pub fn get_byte_order(byte_order: &String) -> bool { return true; } -// See how endianess affects PDU in 6.2.2 https://www.autosar.org/fileadmin/standards/R22-11/CP/AUTOSAR_TPS_SystemTemplate.pdf -// Currenlty assumes Little Endian byte ordering and has support for signals that are Little Endian or Big Endian -// Bit positions in undefined ranges are set to 1 +/* + Extracts the initial values for a PDU by processing contained ISignal and ISignalGroup elements related to that PDU. + See how endianess affects PDU in 6.2.2 https://www.autosar.org/fileadmin/standards/R22-11/CP/AUTOSAR_TPS_SystemTemplate.pdf + Currenlty assumes Little Endian byte ordering and has support for signals that are Little Endian or Big Endian. + Bit positions in undefined ranges are set to unused_bit_pattern. +*/ pub fn extract_init_values(unused_bit_pattern: bool, ungrouped_signals: &Vec, grouped_signals: &Vec, length: i64, byte_order: &bool) -> Vec { // pre checks if grouped_signals.len() > 0 && ungrouped_signals.len() > 0 { @@ -303,6 +353,9 @@ pub fn extract_init_values(unused_bit_pattern: bool, ungrouped_signals: &Vec bool { let unused_bit_pattern_int = get_required_int_value(&pdu, ElementName::UnusedBitPattern); let unused_bit_pattern: bool; @@ -318,6 +371,9 @@ pub fn get_unused_bit_pattern(pdu: &Element) -> bool { return unused_bit_pattern; } +/* + Processes the Autosar FramePortRefs elements inside a CanFrameTriggering to find out the ECUs (names) that send and receive this underlying CAN frame. +*/ pub fn process_frame_ports(can_frame_triggering: &Element, can_frame_triggering_name: &String, rx_ecus: &mut Vec, tx_ecus: &mut Vec) -> Result<(), String> { if let Some(frame_ports) = can_frame_triggering.get_sub_element(ElementName::FramePortRefs) { let frame_ports: Vec = frame_ports.sub_elements() @@ -350,6 +406,9 @@ pub fn process_frame_ports(can_frame_triggering: &Element, can_frame_triggering_ Ok(()) } +/* + Processes the Autosar InitValue element of an ISignal. Extracts one or more of them an put them into passed init_values argument. +*/ pub fn process_init_value(init_value_elem: &Element, init_values: &mut InitValues, signal_name: &String) { let init_value_single: bool; @@ -385,6 +444,11 @@ pub fn process_init_value(init_value_elem: &Element, init_values: &mut InitValue } } +/* + -Processes an ISignalGroup element and extracts important data. + -Removes signals defined in ISignalGroup from signals HashMap (passed argument). + -Pushes the resulting self-defined ISignalGroup structure containing important data into the grouped_signals argument. +*/ pub fn process_signal_group(signal_group: &Element, signals: &mut HashMap, grouped_signals: &mut Vec) -> Option<()> @@ -482,7 +546,11 @@ pub fn process_signal_group(signal_group: &Element, Some(()) } -// should normally only add one TimedCanFrame but multiple may be added in case multiple pdu mappings exist +/* + 1. Extract data from CanFrameTriggering structure that is later needed by restbus-simulation. + 2. Create TimedCanFrame sructure out of data and put the structure into timed_can_frames vector. + Note: Should normally only add one TimedCanFrame but multiple may be added in case multiple PDU Mappings exist for a Can frame. +*/ pub fn get_timed_can_frame(can_frame_triggering: &CanFrameTriggering, timed_can_frames: &mut Vec) { let can_id: u32 = can_frame_triggering.can_id as u32; let len: u8 = can_frame_triggering.frame_length as u8; @@ -531,6 +599,10 @@ pub fn get_timed_can_frame(can_frame_triggering: &CanFrameTriggering, timed_can_ } } +/* + 1. Find CanFrameTriggering structure based on CAN id. + 2. Put its important data as TimedCanFrame structure into timed_can_frames vector. +*/ pub fn get_timed_can_frame_from_id(can_clusters: &HashMap, bus_name: String, can_id: i64) -> Vec { let mut timed_can_frames: Vec = Vec::new(); @@ -543,6 +615,10 @@ pub fn get_timed_can_frame_from_id(can_clusters: &HashMap, b return timed_can_frames } +/* + 1. Iterate over all CanFrameTriggerings belonging to a CanCluster structure. + 2. Put all CanFrameTriggering important data as TimedCanFrame structures into timed_can_frames vector. +*/ pub fn get_timed_can_frames_from_bus(can_clusters: &HashMap, bus_name: String) -> Vec { let mut timed_can_frames: Vec = Vec::new(); diff --git a/opendut-edgar/restbus-simulation/src/restbus_simulation.rs b/opendut-edgar/restbus-simulation/src/restbus_simulation.rs index e8ca255b..d0a65423 100755 --- a/opendut-edgar/restbus-simulation/src/restbus_simulation.rs +++ b/opendut-edgar/restbus-simulation/src/restbus_simulation.rs @@ -1,21 +1,22 @@ -use crate::restbus_utils::*; -use crate::restbus_structs::*; - /* - Restbus simulation that makes use of the structures parsed by the ARXML parser. Makes use of the Linux Kernel CAN Broadcast Manager + Restbus simulation that makes use of the structures parsed by the ARXML parser. Makes use of the Linux Kernel CAN Broadcast Manager. + Ideas for improvement: + - Be able to manually add stuff to restbus -> provide interface */ -/* -- TODO: - - create restbus simulation based on parsed data in a different source code file - - use 100ms for NM pdus -*/ +use crate::restbus_utils::*; +use crate::restbus_structs::*; pub struct RestbusSimulation { } impl RestbusSimulation { + /* + 1. Creates a BCM socket. + 2. Converts all TimedCanFrames into regular CAN frames and puts each CAN frame into a BCM struct. + 3. All created BCM structs are written to the BCM socket (sent to the Broadcast Manager). + */ pub fn play_all(&self, timed_can_frames: &Vec, ifname: &String) -> Result { let sock = create_socket()?; @@ -39,14 +40,14 @@ impl RestbusSimulation { } for write_bytes in write_bytes_global { - println!("write byte is {}", write_bytes.len()); + /*println!("write byte is {}", write_bytes.len()); for byte in &write_bytes { print!("{:02x} ", byte); } - println!(""); + println!("");*/ write_socket(sock, &write_bytes, write_bytes.len())?; - println!("successfully wrote to socket"); + //println!("successfully wrote to socket"); } return Ok(true); diff --git a/opendut-edgar/restbus-simulation/src/restbus_structs.rs b/opendut-edgar/restbus-simulation/src/restbus_structs.rs index 751c0360..845a10f9 100755 --- a/opendut-edgar/restbus-simulation/src/restbus_structs.rs +++ b/opendut-edgar/restbus-simulation/src/restbus_structs.rs @@ -1,5 +1,10 @@ use nix::libc::timeval; + +/* + Header of messages sent to or received from the BCM.. + Same as in https://github.com/linux-can/can-utils/blob/master/include/linux/can/bcm.h. +*/ #[repr(C)] #[derive(Debug)] pub struct BcmMsgHead { @@ -12,12 +17,18 @@ pub struct BcmMsgHead { pub nframes: u32, } +/* + Enum of all supported Can frames. Currently, only Can-20 and Can-FD are supported. + */ #[derive(Debug)] pub enum CanFrame { Can20Frame(Can20Frame), CanFdFrame(CanFdFrame) } +/* + A Can-20 Frame. Same as in https://github.com/linux-can/can-utils/blob/master/include/linux/can.h. +*/ #[repr(C, packed)] #[derive(Debug)] pub struct Can20Frame { @@ -29,6 +40,9 @@ pub struct Can20Frame { pub data: [u8; 8], } +/* + A Can-Fd Frame. Same as in https://github.com/linux-can/can-utils/blob/master/include/linux/can.h. +*/ #[repr(C, packed)] #[derive(Debug)] pub struct CanFdFrame { @@ -40,6 +54,9 @@ pub struct CanFdFrame { pub data: [u8; 64], } +/* + A structure holding can frame information including timing, which is later used to create Can frames sent to the BCM. +*/ #[derive(Debug)] pub struct TimedCanFrame { pub can_id: u32, @@ -52,6 +69,9 @@ pub struct TimedCanFrame { pub ival2: timeval, } +/* + Opcodes defining the operation that the BCM should do. Same as in https://github.com/linux-can/can-utils/blob/master/include/linux/can/bcm.h. +*/ pub enum OPCODE { TxSetup = 1, /* TxDelete, @@ -67,6 +87,9 @@ pub enum OPCODE { RxChanged*/ } +/* + BCM flags used in messages sent to the BCM. Same as in https://github.com/linux-can/can-utils/blob/master/include/linux/can/bcm.h. +*/ pub enum BCMFlags { SetTimer = 0x0001, StartTimer = 0x0002, diff --git a/opendut-edgar/restbus-simulation/src/restbus_utils.rs b/opendut-edgar/restbus-simulation/src/restbus_utils.rs index 28759594..6904ebc7 100755 --- a/opendut-edgar/restbus-simulation/src/restbus_utils.rs +++ b/opendut-edgar/restbus-simulation/src/restbus_utils.rs @@ -11,11 +11,16 @@ use std::ffi::CString; use nix::libc::{__c_anonymous_sockaddr_can_can_addr, __c_anonymous_sockaddr_can_tp, connect, if_nametoindex, sockaddr, sockaddr_can, socket, timeval, write, AF_CAN, CAN_BCM, CAN_EFF_FLAG, SOCK_DGRAM}; - +/* + Convert Rust vector to C like pointer that is required when using C derived method +*/ fn vec_to_c_void(vec: &Vec) -> *const c_void { vec.as_ptr() as *const c_void } +/* + Create a socket using libc's socket function. This is required since not Rust equivalent library or method exists for establishing a BCM CAN socket +*/ pub fn create_socket() -> Result { let sock = unsafe { socket(AF_CAN, SOCK_DGRAM, CAN_BCM) @@ -28,6 +33,11 @@ pub fn create_socket() -> Result { return Ok(sock); } +/* + 1. Get the interface index from the interface string + 2. Setup libc sockaddr_can structure + 3. Connect the existing socket +*/ pub fn connect_socket(sock: i32, ifname: &String) -> Result { let ifindex = unsafe { if let Ok(c_ifname) = CString::new(ifname.as_str()) { @@ -67,6 +77,9 @@ pub fn connect_socket(sock: i32, ifname: &String) -> Result { return Ok(connect_res); } +/* + Writes to existing socket +*/ pub fn write_socket(sock: i32, write_bytes: &Vec, count: usize) -> Result { let wres = unsafe { write(sock, vec_to_c_void(&write_bytes), count) @@ -78,6 +91,9 @@ pub fn write_socket(sock: i32, write_bytes: &Vec, count: usize) -> Result) { let mut i = 0; while i < data_vector.len() { @@ -86,6 +102,9 @@ fn fill_data_array(data: &mut [u8], data_vector: &Vec) { } } +/* + Creates a self-defined CanFrame structure that is either a CanFdFrame or a Can20Frame + */ pub fn create_can_frame_structure(can_id: u32, len: u8, addressing_mode: bool, frame_tx_behavior: bool, data_vector: &Vec) -> CanFrame { let mut eflag: u32 = 0x0; @@ -122,6 +141,9 @@ pub fn create_can_frame_structure(can_id: u32, len: u8, addressing_mode: bool, f } } +/* + Creates a self-defined TimedCanFrame structure that holds the necessary data used for creating a CanFrame later +*/ pub fn create_time_can_frame_structure(count: u32, ival1_tv_sec: i64, ival1_tv_usec: i64, ival2_tv_sec: i64, ival2_tv_usec: i64, can_id: u32, len: u8, addressing_mode: bool, frame_tx_behavior: bool, data_vector: &Vec) -> TimedCanFrame { let mut copy_data_vector: Vec = Vec::new(); @@ -142,6 +164,10 @@ pub fn create_time_can_frame_structure(count: u32, ival1_tv_sec: i64, ival1_tv_u } } +/* + Creates a BcmMsgHead structure, which is a header of messages send to/from the CAN Broadcast Manager + See also https://github.com/linux-can/can-utils/blob/master/include/linux/can/bcm.h +*/ pub fn create_bcm_head(count: u32, ival1_tv_sec: i64, ival1_tv_usec: i64 , ival2_tv_sec: i64, ival2_tv_usec: i64, can_id: u32, frame_tx_behavior: bool, frames: &Vec) -> BcmMsgHead { let mut canfd_flag: u32 = 0x0; @@ -160,6 +186,10 @@ pub fn create_bcm_head(count: u32, ival1_tv_sec: i64, ival1_tv_usec: i64 }; } +/* + Converts a BcmMsgHead structure and the payload (which are CanFrames) to a byte representation. + The write_bytes vector is filled with the bytes and can be then later be used by the caller. +*/ pub fn create_bcm_structure_bytes(count: u32, ival1_tv_sec: i64, ival1_tv_usec: i64 , ival2_tv_sec: i64, ival2_tv_usec: i64, can_id: u32, frame_tx_behavior: bool, frames: &Vec, write_bytes: &mut Vec) { let head: BcmMsgHead = create_bcm_head(count, ival1_tv_sec, ival1_tv_usec, ival2_tv_sec, ival2_tv_usec, can_id, frame_tx_behavior, frames); From e3334a53868becd48b103686c54d24a94ff5138c Mon Sep 17 00:00:00 2001 From: Martin Heyden Date: Mon, 27 May 2024 08:25:09 -0400 Subject: [PATCH 05/18] allow optional unusedbitpattern value for nmpdus --- .../restbus-simulation/src/arxml_parser.rs | 4 ++-- .../restbus-simulation/src/arxml_utils.rs | 14 ++++++++++++-- 2 files changed, 14 insertions(+), 4 deletions(-) diff --git a/opendut-edgar/restbus-simulation/src/arxml_parser.rs b/opendut-edgar/restbus-simulation/src/arxml_parser.rs index 5c2be504..0e45ca59 100755 --- a/opendut-edgar/restbus-simulation/src/arxml_parser.rs +++ b/opendut-edgar/restbus-simulation/src/arxml_parser.rs @@ -141,7 +141,7 @@ impl ArxmlParser { } } - let unused_bit_pattern = get_unused_bit_pattern(&pdu); + let unused_bit_pattern = get_unused_bit_pattern(&pdu, true); let mut grouped_signals: Vec = Vec::new(); @@ -170,7 +170,7 @@ impl ArxmlParser { 2. Returns important data in a self-defined NMPDU structure. */ fn handle_nm_pdu(&self, pdu: &Element) -> Option { - let unused_bit_pattern = get_unused_bit_pattern(&pdu); + let unused_bit_pattern = get_unused_bit_pattern(&pdu, false); let mut grouped_signals: Vec = Vec::new(); diff --git a/opendut-edgar/restbus-simulation/src/arxml_utils.rs b/opendut-edgar/restbus-simulation/src/arxml_utils.rs index c5ba3cce..fa3f2576 100755 --- a/opendut-edgar/restbus-simulation/src/arxml_utils.rs +++ b/opendut-edgar/restbus-simulation/src/arxml_utils.rs @@ -356,8 +356,18 @@ pub fn extract_init_values(unused_bit_pattern: bool, ungrouped_signals: &Vec bool { - let unused_bit_pattern_int = get_required_int_value(&pdu, ElementName::UnusedBitPattern); +pub fn get_unused_bit_pattern(pdu: &Element, required: bool) -> bool { + let unused_bit_pattern_int; + if required { + unused_bit_pattern_int = get_required_int_value(&pdu, ElementName::UnusedBitPattern); + } else { + // in case the element is optional (as for NMPdu), then use 0 when it does not exist + unused_bit_pattern_int = get_optional_int_value(&pdu, ElementName::UnusedBitPattern); + /*match get_subelement_int_value(&pdu, ElementName::UnusedBitPattern) { + Some(value) => unused_bit_pattern_int = value, + _ => unused_bit_pattern_int = 1, // in case the element is optional (as for NMPdu) and not existing, then use 0 as default value + }*/ + } let unused_bit_pattern: bool; if unused_bit_pattern_int == 0 { From 4fd74e51bd61b558060682caf1bf00b2cfe9a49b Mon Sep 17 00:00:00 2001 From: Martin Heyden Date: Wed, 29 May 2024 12:30:00 -0400 Subject: [PATCH 06/18] added main method that can be used to test everything using example file or own file --- .../restbus-simulation/src/arxml_parser.rs | 48 +- .../restbus-simulation/src/arxml_structs.rs | 34 +- .../restbus-simulation/src/arxml_utils.rs | 262 +-- opendut-edgar/restbus-simulation/src/main.rs | 123 ++ .../src/restbus_simulation.rs | 4 +- .../restbus-simulation/src/restbus_utils.rs | 20 +- .../restbus-simulation/src/system-4.2.arxml | 1483 +++++++++++++++++ 7 files changed, 1804 insertions(+), 170 deletions(-) create mode 100755 opendut-edgar/restbus-simulation/src/main.rs create mode 100755 opendut-edgar/restbus-simulation/src/system-4.2.arxml diff --git a/opendut-edgar/restbus-simulation/src/arxml_parser.rs b/opendut-edgar/restbus-simulation/src/arxml_parser.rs index 0e45ca59..f27f6625 100755 --- a/opendut-edgar/restbus-simulation/src/arxml_parser.rs +++ b/opendut-edgar/restbus-simulation/src/arxml_parser.rs @@ -27,7 +27,7 @@ impl ArxmlParser { 2. Fills the important extracted data into the signals HashMap and signal_groups vectors. */ fn handle_isignal_to_pdu_mappings(&self, mapping: &Element, - signals: &mut HashMap, + signals: &mut HashMap, signal_groups: &mut Vec) { if let Some(signal) = mapping @@ -48,9 +48,9 @@ impl ArxmlParser { ElementName::Length); let mut init_values: InitValues = InitValues::NotExist(true); - - if let Some(init_value_elem) = signal.get_sub_element(ElementName::InitValue) { - process_init_value(&init_value_elem, &mut init_values, &name); + + if let Some(mut init_value_elem) = signal.get_sub_element(ElementName::InitValue) { + process_init_value(&mut init_value_elem, &mut init_values, &name); } signals.insert(refpath, (name, byte_order, start_pos, length, init_values)); } else if let Some(signal_group) = mapping @@ -68,7 +68,7 @@ impl ArxmlParser { */ fn handle_isignals(&self, pdu: &Element, grouped_signals: &mut Vec, ungrouped_signals: &mut Vec) -> Option<()> { //let mut signals: HashMap, Option)> = HashMap::new(); - let mut signals: HashMap = HashMap::new(); + let mut signals: HashMap = HashMap::new(); let mut signal_groups = Vec::new(); @@ -83,7 +83,7 @@ impl ArxmlParser { process_signal_group(signal_group, &mut signals, grouped_signals); } - let remaining_signals: Vec<(String, String, i64, i64, InitValues)> = signals.values().cloned().collect(); + let remaining_signals: Vec<(String, String, u64, u64, InitValues)> = signals.values().cloned().collect(); if remaining_signals.len() > 0 { for (name, byte_order, start_pos, length, init_values) in remaining_signals { let isignal_struct: ISignal = ISignal { @@ -114,7 +114,7 @@ impl ArxmlParser { let mut cyclic_timing_offset_value: f64 = 0_f64; let mut cyclic_timing_offset_tolerance: Option = None; - let mut number_of_repetitions: i64 = 0; + let mut number_of_repetitions: u64 = 0; let mut repetition_period_value: f64 = 0_f64; let mut repetition_period_tolerance: Option = None; @@ -141,7 +141,7 @@ impl ArxmlParser { } } - let unused_bit_pattern = get_unused_bit_pattern(&pdu, true); + let unused_bit_pattern = get_unused_bit_pattern(&pdu); let mut grouped_signals: Vec = Vec::new(); @@ -170,7 +170,7 @@ impl ArxmlParser { 2. Returns important data in a self-defined NMPDU structure. */ fn handle_nm_pdu(&self, pdu: &Element) -> Option { - let unused_bit_pattern = get_unused_bit_pattern(&pdu, false); + let unused_bit_pattern = get_unused_bit_pattern(&pdu); let mut grouped_signals: Vec = Vec::new(); @@ -200,12 +200,10 @@ impl ArxmlParser { let pdu_name = get_required_item_name( &pdu, "Pdu"); - let byte_order = get_required_string(pdu_mapping, + //let byte_order = get_required_string(pdu_mapping, + let byte_order = get_optional_string(pdu_mapping, ElementName::PackingByteOrder); - let start_position = get_required_int_value(pdu_mapping, - ElementName::StartPosition); - let pdu_length = get_required_int_value(&pdu, ElementName::Length); @@ -256,7 +254,7 @@ impl ArxmlParser { let pdu_mapping: PDUMapping = PDUMapping { name: pdu_name, byte_order: get_byte_order(&byte_order), - start_position: start_position, + // start_position: start_position, length: pdu_length, dynamic_length: pdu_dynamic_length, category: pdu_category, @@ -272,7 +270,7 @@ impl ArxmlParser { 1. Parses an Autosar CanFrameTriggering element. 2. Returns important data in a self-defined CanFrameTriggering structure. */ - fn handle_can_frame_triggering(&self, can_frame_triggering: &Element) -> Result { + fn handle_can_frame_triggering(&self, can_frame_triggering: &Element, has_fd_baudrate: bool) -> Result { let can_frame_triggering_name= get_required_item_name( can_frame_triggering, "CanFrameTriggering"); @@ -301,24 +299,30 @@ impl ArxmlParser { addressing_mode = true; } + // allow it to be missing. When missing, then derive value from CanCluster let mut frame_rx_behavior = false; - let frame_rx_behavior_str = get_required_string( + let frame_rx_behavior_str = get_optional_string( can_frame_triggering, ElementName::CanFrameRxBehavior); if frame_rx_behavior_str.to_uppercase() == String::from("CAN-FD") { frame_rx_behavior = true; + } else if frame_rx_behavior_str == "" && has_fd_baudrate { + frame_rx_behavior = true; } + // allow it to be missing. When missing, then derive value from CanCluster let mut frame_tx_behavior = false; - let frame_tx_behavior_str = get_required_string( + let frame_tx_behavior_str = get_optional_string( can_frame_triggering, ElementName::CanFrameTxBehavior); if frame_tx_behavior_str.to_uppercase() == String::from("CAN-FD") { frame_tx_behavior = true; + } else if frame_tx_behavior_str == "" && has_fd_baudrate { + frame_tx_behavior = true; } - let mut rx_range_lower: i64 = 0; - let mut rx_range_upper: i64 = 0; + let mut rx_range_lower: u64 = 0; + let mut rx_range_upper: u64 = 0; if let Some(range_elem) = can_frame_triggering.get_sub_element(ElementName::RxIdentifierRange) { rx_range_lower = get_required_int_value(&range_elem, ElementName::LowerCanId); rx_range_upper = get_required_int_value(&range_elem, ElementName::UpperCanId); @@ -388,6 +392,8 @@ impl ArxmlParser { &can_cluster_conditional, ElementName::CanFdBaudrate); + let has_fd_baudrate = can_cluster_baudrate > 0; + if can_cluster_baudrate == 0 && can_cluster_fd_baudrate == 0 { let msg = format!("Baudrate and FD Baudrate of CanCluster {} do not exist or are 0. Skipping this CanCluster.", can_cluster_name); return Err(msg.to_string()); @@ -406,11 +412,11 @@ impl ArxmlParser { return Err(msg.to_string()); } - let mut can_frame_triggerings: HashMap = HashMap::new(); + let mut can_frame_triggerings: HashMap = HashMap::new(); for physical_channel in physical_channels { if let Some(frame_triggerings) = physical_channel.get_sub_element(ElementName::FrameTriggerings) { for can_frame_triggering in frame_triggerings.sub_elements() { - match self.handle_can_frame_triggering(&can_frame_triggering) { + match self.handle_can_frame_triggering(&can_frame_triggering, has_fd_baudrate) { Ok(value) => { can_frame_triggerings.insert(value.can_id.clone(), value); } diff --git a/opendut-edgar/restbus-simulation/src/arxml_structs.rs b/opendut-edgar/restbus-simulation/src/arxml_structs.rs index e68c99c1..9bb002d4 100755 --- a/opendut-edgar/restbus-simulation/src/arxml_structs.rs +++ b/opendut-edgar/restbus-simulation/src/arxml_structs.rs @@ -11,9 +11,9 @@ use std::collections::HashMap; #[derive(Debug)] pub struct CanCluster { pub name: String, - pub baudrate: i64, - pub canfd_baudrate: i64, - pub can_frame_triggerings: HashMap + pub baudrate: u64, + pub canfd_baudrate: u64, + pub can_frame_triggerings: HashMap } /* @@ -23,15 +23,15 @@ pub struct CanCluster { pub struct CanFrameTriggering { pub frame_triggering_name: String, pub frame_name: String, - pub can_id: i64, + pub can_id: u64, pub addressing_mode: bool, pub frame_rx_behavior: bool, pub frame_tx_behavior: bool, - pub rx_range_lower: i64, - pub rx_range_upper: i64, + pub rx_range_lower: u64, + pub rx_range_upper: u64, pub sender_ecus: Vec, pub receiver_ecus: Vec, - pub frame_length: i64, + pub frame_length: u64, pub pdu_mappings: Vec } @@ -42,8 +42,8 @@ pub struct CanFrameTriggering { pub struct PDUMapping { pub name: String, pub byte_order: bool, - pub start_position: i64, - pub length: i64, +// pub start_position: u64, + pub length: u64, pub dynamic_length: String, pub category: String, pub contained_header_id_short: String, @@ -74,7 +74,7 @@ pub struct ISignalIPDU { pub cyclic_timing_period_tolerance: Option, pub cyclic_timing_offset_value: f64, pub cyclic_timing_offset_tolerance: Option, - pub number_of_repetitions: i64, + pub number_of_repetitions: u64, pub repetition_period_value: f64, pub repetition_period_tolerance: Option, pub unused_bit_pattern: bool, @@ -99,8 +99,8 @@ pub struct NMPDU { pub struct ISignal { pub name: String, pub byte_order: bool, - pub start_pos: i64, - pub length: i64, + pub start_pos: u64, + pub length: u64, pub init_values: InitValues } @@ -110,8 +110,8 @@ pub struct ISignal { #[derive(Debug)] #[derive(Clone)] pub enum InitValues { - Single(i64), - Array(Vec), + Single(u64), + Array(Vec), NotExist(bool), } @@ -121,8 +121,8 @@ pub enum InitValues { #[derive(Debug)] pub struct E2EDataTransformationProps { pub transformer_name: String, - pub data_id: i64, - pub data_length: i64 + pub data_id: u64, + pub data_length: u64 } #[derive(Debug)] @@ -135,7 +135,7 @@ pub struct ISignalGroup { #[derive(Debug)] pub enum TimeRangeTolerance { - Relative(i64), + Relative(u64), Absolute(f64), } diff --git a/opendut-edgar/restbus-simulation/src/arxml_utils.rs b/opendut-edgar/restbus-simulation/src/arxml_utils.rs index fa3f2576..5fc21b08 100755 --- a/opendut-edgar/restbus-simulation/src/arxml_utils.rs +++ b/opendut-edgar/restbus-simulation/src/arxml_utils.rs @@ -15,25 +15,29 @@ use autosar_data::{CharacterData, Element, ElementName, EnumItem}; Converts a CharacterData type from the autosar_data library Directly taken from https://github.com/DanielT/autosar-data/blob/main/autosar-data/examples/businfo/main.rs. */ -pub fn decode_integer(cdata: &CharacterData) -> Option { +pub fn decode_integer(cdata: &CharacterData) -> Option { if let CharacterData::String(text) = cdata { if text == "0" { Some(0) } else if text.starts_with("0x") { let hexstr = text.strip_prefix("0x").unwrap(); - Some(i64::from_str_radix(hexstr, 16).ok()?) + Some(u64::from_str_radix(hexstr, 16).ok()?) } else if text.starts_with("0X") { let hexstr = text.strip_prefix("0X").unwrap(); - Some(i64::from_str_radix(hexstr, 16).ok()?) + Some(u64::from_str_radix(hexstr, 16).ok()?) } else if text.starts_with("0b") { let binstr = text.strip_prefix("0b").unwrap(); - Some(i64::from_str_radix(binstr, 2).ok()?) + Some(u64::from_str_radix(binstr, 2).ok()?) } else if text.starts_with("0B") { let binstr = text.strip_prefix("0B").unwrap(); - Some(i64::from_str_radix(binstr, 2).ok()?) + Some(u64::from_str_radix(binstr, 2).ok()?) } else if text.starts_with('0') { let octstr = text.strip_prefix('0').unwrap(); - Some(i64::from_str_radix(octstr, 8).ok()?) + Some(u64::from_str_radix(octstr, 8).ok()?) + } else if text.to_ascii_lowercase() == "false" { + Some(0) + } else if text.to_ascii_lowercase() == "true" { + Some(1) } else { Some(text.parse().ok()?) } @@ -110,9 +114,9 @@ pub fn get_required_sub_subelement(element: &Element, subelement_name: ElementNa } /* - Tries to get a subelement and convert it's value to i64. + Tries to get a subelement and convert it's value to u64. */ -pub fn get_subelement_int_value(element: &Element, subelement_name: ElementName) -> Option { +pub fn get_subelement_int_value(element: &Element, subelement_name: ElementName) -> Option { return element .get_sub_element(subelement_name) .and_then(|elem| elem.character_data()) @@ -120,9 +124,9 @@ pub fn get_subelement_int_value(element: &Element, subelement_name: ElementName) } /* - Gets the i64 value for a element. This has to succeed. + Gets the u64 value for a element. This has to succeed. */ -pub fn get_required_int_value(element: &Element, subelement_name: ElementName) -> i64 { +pub fn get_required_int_value(element: &Element, subelement_name: ElementName) -> u64 { if let Some(int_value) = get_subelement_int_value(element, subelement_name) { return int_value; } else { @@ -131,9 +135,9 @@ pub fn get_required_int_value(element: &Element, subelement_name: ElementName) - } /* - Gets the i64 value for a element. This is optional. So, if the subelement does not exist, then 0 is returned. + Gets the u64 value for a element. This is optional. So, if the subelement does not exist, then 0 is returned. */ -pub fn get_optional_int_value(element: &Element, subelement_name: ElementName) -> i64 { +pub fn get_optional_int_value(element: &Element, subelement_name: ElementName) -> u64 { if let Some(int_value) = get_subelement_int_value(element, subelement_name) { return int_value; } else { @@ -148,7 +152,23 @@ pub fn get_required_reference(element: &Element, subelement_name: ElementName) - if let Some(subelement) = element.get_sub_element(subelement_name) { match subelement.get_reference_target() { Ok(reference) => return reference, - Err(_) => {} + Err(err) => { + println!("[-] Warning: Constant ref error: {}. Will try modification of target name and reference again.", err); + match subelement.character_data() { + Some(val) => { + let new_dest = "/Constants/".to_string() + val.to_string().as_str(); + match subelement.set_character_data(CharacterData::String(new_dest)) { + Ok(()) => {} + Err(err) => println!("[-] Warning: Error setting new dest: {}", err) + } + match subelement.get_reference_target() { + Ok(reference) => return reference, + Err(err) => println!("[-] Warning: Constant ref retry error: {}.", err), + } + } + _ => println!("[-] Warning: No success in retry because Element CharacterData is not set."), + } + } } } @@ -224,94 +244,88 @@ pub fn get_byte_order(byte_order: &String) -> bool { return true; } -/* - Extracts the initial values for a PDU by processing contained ISignal and ISignalGroup elements related to that PDU. - See how endianess affects PDU in 6.2.2 https://www.autosar.org/fileadmin/standards/R22-11/CP/AUTOSAR_TPS_SystemTemplate.pdf - Currenlty assumes Little Endian byte ordering and has support for signals that are Little Endian or Big Endian. - Bit positions in undefined ranges are set to unused_bit_pattern. -*/ -pub fn extract_init_values(unused_bit_pattern: bool, ungrouped_signals: &Vec, grouped_signals: &Vec, length: i64, byte_order: &bool) -> Vec { - // pre checks - if grouped_signals.len() > 0 && ungrouped_signals.len() > 0 { - panic!("both signal vectors are > 0"); - } - - let isignals: &Vec; - - if grouped_signals.len() > 0 { - if grouped_signals.len() > 1 { - panic!("Grouped signals > 0"); - } - isignals = &grouped_signals[0].isignals; - } else { - isignals = ungrouped_signals; - } +fn process_isignal_init_value(isignal: &ISignal, bits: &mut Vec) { + let mut tmp_bit_array: Vec = Vec::new(); + let init_values = &isignal.init_values; + let isignal_byte_order = isignal.byte_order; + let isignal_length: usize = isignal.length.try_into().unwrap(); + let isignal_start: usize = isignal.start_pos.try_into().unwrap(); - let dlc: usize = length.try_into().unwrap(); + match init_values { + InitValues::Single(value) => { + let mut n = value.clone(); - let mut bits = vec![unused_bit_pattern; dlc * 8]; // Using unusued_bit_pattern for undefined bits + while n != 0 { + tmp_bit_array.push(n & 1 != 0); + n >>= 1; + } - for isignal in isignals { - let mut tmp_bit_array: Vec = Vec::new(); - let init_values = &isignal.init_values; - let isignal_byte_order = isignal.byte_order; - let isignal_length: usize = isignal.length.try_into().unwrap(); - let isignal_start: usize = isignal.start_pos.try_into().unwrap(); + while tmp_bit_array.len() < isignal_length { + tmp_bit_array.push(false); + } + + if isignal_byte_order { + tmp_bit_array.reverse(); + } + } + InitValues::Array(values) => { + if isignal_length % 8 != 0 { + panic!("ISignal length for array is not divisable through 8. Length is {}", isignal_length); + } - match init_values { - InitValues::Single(value) => { - let mut n = value.clone(); + for isignal_value in values { + let byte_len: usize = 8; + let mut n = isignal_value.clone(); + let mut tmp_tmp_bit_array: Vec = Vec::new(); while n != 0 { - tmp_bit_array.push(n & 1 != 0); + tmp_tmp_bit_array.push(n & 1 != 0); n >>= 1; } - while tmp_bit_array.len() < isignal_length { - tmp_bit_array.push(false); - } - - if isignal_byte_order { - tmp_bit_array.reverse(); - } - } - InitValues::Array(values) => { - if isignal_length % 8 != 0 { - panic!("ISignal length for array is not divisable through 8. Length is {}", isignal_length); + while tmp_tmp_bit_array.len() < byte_len { + tmp_tmp_bit_array.push(false); } + + tmp_tmp_bit_array.reverse(); - for isignal_value in values { - let byte_len: usize = 8; - let mut n = isignal_value.clone(); - let mut tmp_tmp_bit_array: Vec = Vec::new(); + tmp_bit_array.extend(tmp_tmp_bit_array); + } + } + _ => return + } - while n != 0 { - tmp_tmp_bit_array.push(n & 1 != 0); - n >>= 1; - } + if tmp_bit_array.len() != isignal.length.try_into().unwrap() { + panic!("Miscalculation for tmp_bit_array"); + } - while tmp_tmp_bit_array.len() < byte_len { - tmp_tmp_bit_array.push(false); - } - - tmp_tmp_bit_array.reverse(); + let mut index: usize = 0; - tmp_bit_array.extend(tmp_tmp_bit_array); - } - } - _ => continue - } + while index < isignal_length { + bits[isignal_start + index] = tmp_bit_array[index]; + index += 1; + } +} - if tmp_bit_array.len() != isignal.length.try_into().unwrap() { - panic!("Miscalculation for tmp_bit_array"); - } +/* + Extracts the initial values for a PDU by processing contained ISignal and ISignalGroup elements related to that PDU. + See how endianess affects PDU in 6.2.2 https://www.autosar.org/fileadmin/standards/R22-11/CP/AUTOSAR_TPS_SystemTemplate.pdf + Currenlty assumes Little Endian byte ordering and has support for signals that are Little Endian or Big Endian. + Bit positions in undefined ranges are set to unused_bit_pattern. +*/ +pub fn extract_init_values(unused_bit_pattern: bool, ungrouped_signals: &Vec, grouped_signals: &Vec, length: u64, byte_order: &bool) -> Vec { + let dlc: usize = length.try_into().unwrap(); - let mut index: usize = 0; + let mut bits = vec![unused_bit_pattern; dlc * 8]; // Using unusued_bit_pattern for undefined bits - while index < isignal_length { - bits[isignal_start + index] = tmp_bit_array[index]; - index += 1; - } + for isignal in ungrouped_signals { + process_isignal_init_value(isignal, &mut bits); + } + + for isignal_group in grouped_signals { + for isignal in &isignal_group.isignals { + process_isignal_init_value(isignal, &mut bits); + } } let mut init_values: Vec = Vec::new(); @@ -356,20 +370,16 @@ pub fn extract_init_values(unused_bit_pattern: bool, ungrouped_signals: &Vec bool { - let unused_bit_pattern_int; - if required { - unused_bit_pattern_int = get_required_int_value(&pdu, ElementName::UnusedBitPattern); - } else { - // in case the element is optional (as for NMPdu), then use 0 when it does not exist - unused_bit_pattern_int = get_optional_int_value(&pdu, ElementName::UnusedBitPattern); - /*match get_subelement_int_value(&pdu, ElementName::UnusedBitPattern) { - Some(value) => unused_bit_pattern_int = value, - _ => unused_bit_pattern_int = 1, // in case the element is optional (as for NMPdu) and not existing, then use 0 as default value - }*/ - } +pub fn get_unused_bit_pattern(pdu: &Element) -> bool { + // even though it needs to exist at least for ISignalIPdus, we keep it as optional, since at least one encounter shows that it might be missing. + // then use 0 as default value + let mut unused_bit_pattern_int = get_optional_int_value(&pdu, ElementName::UnusedBitPattern); + let unused_bit_pattern: bool; + // supports values > 1. Just look at least significant bit + unused_bit_pattern_int = unused_bit_pattern_int & 1; + if unused_bit_pattern_int == 0 { unused_bit_pattern = false; } else if unused_bit_pattern_int == 1 { @@ -409,9 +419,9 @@ pub fn process_frame_ports(can_frame_triggering: &Element, can_frame_triggering_ return Err(format!("Could not extract ECUName in FramePort. Skipping CanFrameTriggering {}", can_frame_triggering_name)) ; } } - } else { + }/* else { return Err(format!("FramePortRefs in CanFrameTriggering not found. Skipping CanFrameTriggering {}", can_frame_triggering_name)); - } + }*/ Ok(()) } @@ -419,20 +429,30 @@ pub fn process_frame_ports(can_frame_triggering: &Element, can_frame_triggering_ /* Processes the Autosar InitValue element of an ISignal. Extracts one or more of them an put them into passed init_values argument. */ -pub fn process_init_value(init_value_elem: &Element, init_values: &mut InitValues, signal_name: &String) { - let init_value_single: bool; +pub fn process_init_value(init_value_elem: &mut Element, init_values: &mut InitValues, signal_name: &String) { + let init_value_type: u8; + + let mut subelement = init_value_elem.get_sub_element_at(0).unwrap(); + + if subelement.element_name().eq(&ElementName::ConstantReference) { + let constant = get_required_reference( + &subelement, + ElementName::ConstantRef); - let subelement_name = init_value_elem.get_sub_element_at(0).unwrap(); + *init_value_elem = constant.get_sub_element(ElementName::ValueSpec).unwrap(); - if subelement_name.element_name().eq(&ElementName::NumericalValueSpecification) { - init_value_single = true; - } else if subelement_name.element_name().eq(&ElementName::ArrayValueSpecification) { - init_value_single = false; + subelement = init_value_elem.get_sub_element_at(0).unwrap(); + } + + if subelement.element_name().eq(&ElementName::NumericalValueSpecification) { + init_value_type = 0; + } else if subelement.element_name().eq(&ElementName::ArrayValueSpecification) { + init_value_type = 1; } else { - panic!("Unrecognized sublement {} for init-value", subelement_name.element_name()); + panic!("Unrecognized sublement {} for init-value", subelement.element_name()); } - if init_value_single { + if init_value_type == 0 { if let Some(num_val) = init_value_elem.get_sub_element(ElementName::NumericalValueSpecification) { let init_value = get_required_int_value(&num_val, ElementName::Value); *init_values = InitValues::Single(init_value); @@ -441,7 +461,7 @@ pub fn process_init_value(init_value_elem: &Element, init_values: &mut InitValue } } else { - let mut init_value_array: Vec = Vec::new(); + let mut init_value_array: Vec = Vec::new(); let num_val_elements = get_required_sub_subelement(&init_value_elem, ElementName::ArrayValueSpecification, ElementName::Elements); @@ -460,7 +480,7 @@ pub fn process_init_value(init_value_elem: &Element, init_values: &mut InitValue -Pushes the resulting self-defined ISignalGroup structure containing important data into the grouped_signals argument. */ pub fn process_signal_group(signal_group: &Element, - signals: &mut HashMap, + signals: &mut HashMap, grouped_signals: &mut Vec) -> Option<()> { let group_name = get_required_item_name(&signal_group, "ISignalGroupRef"); @@ -528,8 +548,10 @@ pub fn process_signal_group(signal_group: &Element, let data_id = get_required_int_value(&data_ids, ElementName::DataId); - - let data_length = get_required_int_value(&e2exf_props_cond, + + // allow optional for now + //let data_length = get_required_int_value(&e2exf_props_cond, + let data_length = get_optional_int_value(&e2exf_props_cond, ElementName::DataLength); @@ -568,25 +590,25 @@ pub fn get_timed_can_frame(can_frame_triggering: &CanFrameTriggering, timed_can_ let frame_tx_behavior: bool = can_frame_triggering.frame_tx_behavior; for pdu_mapping in &can_frame_triggering.pdu_mappings { let mut count: u32 = 0; - let mut ival1_tv_sec: i64 = 0; - let mut ival1_tv_usec: i64 = 0; - let mut ival2_tv_sec: i64 = 0; - let mut ival2_tv_usec: i64 = 0; + let mut ival1_tv_sec: u64 = 0; + let mut ival1_tv_usec: u64 = 0; + let mut ival2_tv_sec: u64 = 0; + let mut ival2_tv_usec: u64 = 0; let init_values: Vec; match &pdu_mapping.pdu { PDU::ISignalIPDU(pdu) => { count = pdu.number_of_repetitions as u32; if pdu.repetition_period_value != 0.0 { - ival1_tv_sec = pdu.repetition_period_value.trunc() as i64; + ival1_tv_sec = pdu.repetition_period_value.trunc() as u64; let fraction: f64 = pdu.repetition_period_value % 1.0; - ival1_tv_usec = (fraction * 1_000_000.0).trunc() as i64; + ival1_tv_usec = (fraction * 1_000_000.0).trunc() as u64; } if pdu.cyclic_timing_period_value != 0.0 { - ival2_tv_sec = pdu.cyclic_timing_period_value.trunc() as i64; + ival2_tv_sec = pdu.cyclic_timing_period_value.trunc() as u64; let fraction: f64 = pdu.cyclic_timing_period_value % 1.0; - ival2_tv_usec = (fraction * 1_000_000.0).trunc() as i64; + ival2_tv_usec = (fraction * 1_000_000.0).trunc() as u64; } init_values = extract_init_values(pdu.unused_bit_pattern, @@ -613,7 +635,7 @@ pub fn get_timed_can_frame(can_frame_triggering: &CanFrameTriggering, timed_can_ 1. Find CanFrameTriggering structure based on CAN id. 2. Put its important data as TimedCanFrame structure into timed_can_frames vector. */ -pub fn get_timed_can_frame_from_id(can_clusters: &HashMap, bus_name: String, can_id: i64) -> Vec { +pub fn get_timed_can_frame_from_id(can_clusters: &HashMap, bus_name: String, can_id: u64) -> Vec { let mut timed_can_frames: Vec = Vec::new(); if let Some(can_cluster) = can_clusters.get(&bus_name) { diff --git a/opendut-edgar/restbus-simulation/src/main.rs b/opendut-edgar/restbus-simulation/src/main.rs new file mode 100755 index 00000000..f89bd9e7 --- /dev/null +++ b/opendut-edgar/restbus-simulation/src/main.rs @@ -0,0 +1,123 @@ +use std::collections::HashMap; +use std::fs::File; +use std::io::prelude::*; +use std::thread; +use std::time::Duration; + +mod arxml_parser; +mod arxml_structs; +mod arxml_utils; +mod restbus_simulation; +mod restbus_utils; +mod restbus_structs; + +use arxml_parser::*; +use arxml_structs::*; +use arxml_utils::*; +use restbus_simulation::*; +use restbus_structs::TimedCanFrame; + + +fn get_pdu_hex(can_id: &u64, init_values: &Vec) -> String { + let mut hex_string = String::new(); + hex_string.push_str(&format!("{}:", can_id)); + for element in init_values { + hex_string.push_str(&format!("{:02X}", element)); + } + println!("Values: {}", hex_string); + + return hex_string; +} + +fn collect_pdus(can_clusters: &HashMap, bus_name: String) -> Vec { + let mut init_values_strings: Vec = Vec::new(); + if let Some(can_cluster) = can_clusters.get(&bus_name) { + for can_frame_triggering in can_cluster.can_frame_triggerings.values() { + for pdu_mapping in &can_frame_triggering.pdu_mappings { + match &pdu_mapping.pdu { + PDU::ISignalIPDU(pdu) => { + let init_values = extract_init_values(pdu.unused_bit_pattern, + &pdu.ungrouped_signals, + &pdu.grouped_signals, + pdu_mapping.length, + &pdu_mapping.byte_order); + init_values_strings.push(get_pdu_hex(&can_frame_triggering.can_id, &init_values)) + } + PDU::NMPDU(pdu) => { + let init_values = extract_init_values(pdu.unused_bit_pattern, + &pdu.ungrouped_signals, + &pdu.grouped_signals, + pdu_mapping.length, + &pdu_mapping.byte_order); + init_values_strings.push(get_pdu_hex(&can_frame_triggering.can_id, &init_values)) + } + } + } + } + } + return init_values_strings; +} + +fn test_find_frame_and_play(restbus_simulation: &RestbusSimulation, ifname: &String, can_clusters: &HashMap, bus_name: String, can_id: u64) { + let timed_can_frames: Vec = get_timed_can_frame_from_id(can_clusters, bus_name, can_id); + + match restbus_simulation.play_all(&timed_can_frames, ifname) { + Ok(_val) => println!("Successfully sent message with can id {}", can_id), + Err(error) => println!("Could not send message with can id {} because: {}", can_id, error), + } + +} + +fn test_bus_play_all(restbus_simulation: &RestbusSimulation, ifname: &String, can_clusters: &HashMap, bus_name: String) { + let timed_can_frames: Vec = get_timed_can_frames_from_bus(can_clusters, bus_name); + + match restbus_simulation.play_all(&timed_can_frames, ifname) { + Ok(_val) => println!("Successfully established restbus simulation"), + Err(error) => println!("Could establish restbus simulation because: {}", error) + } +} + +fn main() -> std::io::Result<()> { + println!("[+] Starting openDuT ARXML parser over main method."); + + let file_name = "system-4.2.arxml"; // from https://github.com/cantools/cantools/blob/master/tests/files/arxml/system-4.2.arxml + + let arxml_parser: ArxmlParser = ArxmlParser {}; + + if let Some(can_clusters) = arxml_parser + .parse_file(file_name.to_string()) + { + let bus_name = "Cluster0"; + let target_file = "system.txt"; + let play_single_or_all = true; + let ifname = String::from("vcan0"); + + for cluster in can_clusters.values() { + println!("CanCluster: {cluster:?}"); + } + + let mut frames = String::new(); + for frame in collect_pdus(&can_clusters, String::from(bus_name)) { + frames.push_str(frame.as_str()); + frames.push_str("\n"); + } + + let mut f = File::create(target_file)?; + f.write_all(&frames.as_bytes())?; + + println!("Trying to setup up restbus simulation"); + + let restbus_simulation: RestbusSimulation = RestbusSimulation {}; + + if !play_single_or_all { + test_find_frame_and_play(&restbus_simulation, &ifname, &can_clusters, String::from(bus_name), 0x3E9); + + } else { + test_bus_play_all(&restbus_simulation, &ifname, &can_clusters, String::from(bus_name)); + } + + thread::sleep(Duration::from_secs(30)); + } + Ok(()) +} + diff --git a/opendut-edgar/restbus-simulation/src/restbus_simulation.rs b/opendut-edgar/restbus-simulation/src/restbus_simulation.rs index d0a65423..83b2624f 100755 --- a/opendut-edgar/restbus-simulation/src/restbus_simulation.rs +++ b/opendut-edgar/restbus-simulation/src/restbus_simulation.rs @@ -32,8 +32,8 @@ impl RestbusSimulation { can_frames.push( create_can_frame_structure(timed_can_frame.can_id, timed_can_frame.len, timed_can_frame.addressing_mode, timed_can_frame.frame_tx_behavior, &timed_can_frame.data_vector)); - create_bcm_structure_bytes(timed_can_frame.count, timed_can_frame.ival1.tv_sec, timed_can_frame.ival1.tv_usec, - timed_can_frame.ival2.tv_sec, timed_can_frame.ival2.tv_usec, timed_can_frame.can_id, timed_can_frame.frame_tx_behavior, &can_frames, &mut write_bytes); + create_bcm_structure_bytes(timed_can_frame.count, timed_can_frame.ival1.tv_sec as u64, timed_can_frame.ival1.tv_usec as u64, + timed_can_frame.ival2.tv_sec as u64, timed_can_frame.ival2.tv_usec as u64, timed_can_frame.can_id, timed_can_frame.frame_tx_behavior, &can_frames, &mut write_bytes); write_bytes_global.push(write_bytes); diff --git a/opendut-edgar/restbus-simulation/src/restbus_utils.rs b/opendut-edgar/restbus-simulation/src/restbus_utils.rs index 6904ebc7..fe745733 100755 --- a/opendut-edgar/restbus-simulation/src/restbus_utils.rs +++ b/opendut-edgar/restbus-simulation/src/restbus_utils.rs @@ -144,8 +144,8 @@ pub fn create_can_frame_structure(can_id: u32, len: u8, addressing_mode: bool, f /* Creates a self-defined TimedCanFrame structure that holds the necessary data used for creating a CanFrame later */ -pub fn create_time_can_frame_structure(count: u32, ival1_tv_sec: i64, ival1_tv_usec: i64, ival2_tv_sec: i64, - ival2_tv_usec: i64, can_id: u32, len: u8, addressing_mode: bool, frame_tx_behavior: bool, data_vector: &Vec) -> TimedCanFrame { +pub fn create_time_can_frame_structure(count: u32, ival1_tv_sec: u64, ival1_tv_usec: u64, ival2_tv_sec: u64, + ival2_tv_usec: u64, can_id: u32, len: u8, addressing_mode: bool, frame_tx_behavior: bool, data_vector: &Vec) -> TimedCanFrame { let mut copy_data_vector: Vec = Vec::new(); for data in data_vector { @@ -159,8 +159,8 @@ pub fn create_time_can_frame_structure(count: u32, ival1_tv_sec: i64, ival1_tv_u frame_tx_behavior: frame_tx_behavior, data_vector: copy_data_vector, count: count, - ival1: timeval { tv_sec: ival1_tv_sec, tv_usec: ival1_tv_usec }, - ival2: timeval { tv_sec: ival2_tv_sec, tv_usec: ival2_tv_usec }, + ival1: timeval { tv_sec: ival1_tv_sec as i64, tv_usec: ival1_tv_usec as i64 }, + ival2: timeval { tv_sec: ival2_tv_sec as i64, tv_usec: ival2_tv_usec as i64 }, } } @@ -168,8 +168,8 @@ pub fn create_time_can_frame_structure(count: u32, ival1_tv_sec: i64, ival1_tv_u Creates a BcmMsgHead structure, which is a header of messages send to/from the CAN Broadcast Manager See also https://github.com/linux-can/can-utils/blob/master/include/linux/can/bcm.h */ -pub fn create_bcm_head(count: u32, ival1_tv_sec: i64, ival1_tv_usec: i64 - , ival2_tv_sec: i64, ival2_tv_usec: i64, can_id: u32, frame_tx_behavior: bool, frames: &Vec) -> BcmMsgHead { +pub fn create_bcm_head(count: u32, ival1_tv_sec: u64, ival1_tv_usec: u64 + , ival2_tv_sec: u64, ival2_tv_usec: u64, can_id: u32, frame_tx_behavior: bool, frames: &Vec) -> BcmMsgHead { let mut canfd_flag: u32 = 0x0; if frame_tx_behavior { @@ -179,8 +179,8 @@ pub fn create_bcm_head(count: u32, ival1_tv_sec: i64, ival1_tv_usec: i64 opcode: OPCODE::TxSetup as u32, flags: BCMFlags::SetTimer as u32 | BCMFlags::StartTimer as u32 | canfd_flag, count: count, - ival1: timeval { tv_sec: ival1_tv_sec, tv_usec: ival1_tv_usec }, - ival2: timeval { tv_sec: ival2_tv_sec, tv_usec: ival2_tv_usec }, + ival1: timeval { tv_sec: ival1_tv_sec as i64, tv_usec: ival1_tv_usec as i64 }, + ival2: timeval { tv_sec: ival2_tv_sec as i64, tv_usec: ival2_tv_usec as i64 }, can_id: can_id, nframes: frames.len() as u32, }; @@ -190,8 +190,8 @@ pub fn create_bcm_head(count: u32, ival1_tv_sec: i64, ival1_tv_usec: i64 Converts a BcmMsgHead structure and the payload (which are CanFrames) to a byte representation. The write_bytes vector is filled with the bytes and can be then later be used by the caller. */ -pub fn create_bcm_structure_bytes(count: u32, ival1_tv_sec: i64, ival1_tv_usec: i64 - , ival2_tv_sec: i64, ival2_tv_usec: i64, can_id: u32, frame_tx_behavior: bool, frames: &Vec, write_bytes: &mut Vec) { +pub fn create_bcm_structure_bytes(count: u32, ival1_tv_sec: u64, ival1_tv_usec: u64 + , ival2_tv_sec: u64, ival2_tv_usec: u64, can_id: u32, frame_tx_behavior: bool, frames: &Vec, write_bytes: &mut Vec) { let head: BcmMsgHead = create_bcm_head(count, ival1_tv_sec, ival1_tv_usec, ival2_tv_sec, ival2_tv_usec, can_id, frame_tx_behavior, frames); let ptr: *const u8 = &head as *const BcmMsgHead as *const u8; diff --git a/opendut-edgar/restbus-simulation/src/system-4.2.arxml b/opendut-edgar/restbus-simulation/src/system-4.2.arxml new file mode 100755 index 00000000..f9a5c0ac --- /dev/null +++ b/opendut-edgar/restbus-simulation/src/system-4.2.arxml @@ -0,0 +1,1483 @@ + + + + + + ECU + + + + DJ + + /PDU_GROUPS/RX/DJ + /PDU_GROUPS/TX/DJ + + + + + Observe + + + + + + Dancer + + Rhythm is a Dancer! + + + /PDU_GROUPS/RX/Dancer + /PDU_GROUPS/TX/Dancer + + + + + Observe + + + + + + Guard + + + + Patrol + + + + + + + + PDU_GROUPS + + + + RX + + + + Dancer + IN + + + /ISignalIPdu/message1 + + + /ISignalIPdu/message4 + + + + + + DJ + IN + + + /ISignalIPdu/message2 + + + /ISignalIPdu/message3 + + + /ISignalIPdu/multiplexed_message_static + + + /ISignalIPdu/multiplexed_message_0 + + + /ISignalIPdu/multiplexed_message_1 + + + + + + + + TX + + + + Dancer + OUT + + + /ISignalIPdu/message2 + + + /ISignalIPdu/message3 + + + /ISignalIPdu/multiplexed_message_static + + + /ISignalIPdu/multiplexed_message_0 + + + /ISignalIPdu/multiplexed_message_1 + + + + + + DJ + OUT + + + /ISignalIPdu/message1 + + + /ISignalIPdu/message4 + + + + + + + + + + NMConfig + + + + A + + + + NightClub + /Cluster/Cluster0 + + + + DJ + /ECU/DJ/Observe + + /NMPdu/alarm_status + + + + + Dancer + /ECU/Dancer/Observe + + /NMPdu/alarm_status + + + + + Guard + /ECU/Guard/Patrol + + /NMPdu/alarm_status + + + + + + + + + + + Cluster + + + + Cluster0 + + The great CAN cluster + + + + 500000 + + + + Pch0 + + + + multiplexed_message + /CanFrame/MultiplexedMessage + STANDARD + CAN-FD + CAN-FD + 4 + + + + message1 + /CanFrame/Message1 + STANDARD + CAN-FD + 5 + + + + message2 + /CanFrame/Message2 + EXTENDED + CAN-FD + 6 + + + + message3 + /CanFrame/Message3 + STANDARD + 100 + + + + message4 + /CanFrame/Message4 + STANDARD + 101 + + + + OneToContainThemAll + /CanFrame/OneToContainThemAll + STANDARD + 102 + + + + AlarmStatus + /CanFrame/AlarmStatus + STANDARD + 1001 + + + MessageWithoutPDU + /CanFrame/MessageWithoutPDU + STANDARD + 1002 + + + + + + message1_triggering + /ISignalIPdu/message1 + + + + message2_triggering + /ISignalIPdu/message2 + + + + multiplexed_triggering + /MultiplexedIPdu/multiplexed_message + + + + message3_triggering + /ISignalIPdu/message3 + + + + message3_secured_triggering + /SecuredIPdu/message3_secured + + + + + can2.0b + 2000000 + + + + + + + + CanFrame + + + + MultiplexedMessage + 2 + + + + multiplexed_message + /MultiplexedIPdu/multiplexed_message + 0 + + + + + + Message1 + + Comment1 + Kommentar1 + + 9 + + + + message1 + /ISignalIPdu/message1 + + + + + + Message2 + 7 + + + + message2 + /ISignalIPdu/message2 + + + + + + Message3 + 6 + + + + message3 + /SecuredIPdu/message3_secured + + + + + + Message4 + 6 + + + + message4 + /ISignalIPdu/message4 + + + + + + OneToContainThemAll + 64 + + + + OneToContainThemAll_mapping + /ContainerIPdu/OneToContainThemAll + + + + + + AlarmStatus + 1 + + + + nm + /NMPdu/alarm_status + + + + + + MessageWithoutPDU + 8 + + + + + + ISignal + + + + ConstantBase + true + false + false + /Constants + + + + SystemSignalBase + false + false + false + /SystemSignal + + + + + + MultiplexedStatic + + + 0x7 + + + 3 + + + + /SwBaseType/S16 + + + + /SystemSignal/MultiplexedMessageStatic + + + + MultiplexedStatic2 + + + 0x7 + + + 8 + + + + /SwBaseType/S16 + + + + /SystemSignal/MultiplexedMessageStatic2 + + + + multiplexed_message_selector + + + 0.0 + + + 2 + + + + /SwBaseType/U8 + + + 7 + + + + + + /SystemSignal/MultiplexedMessageSelector + + + + Hello + + + 0 + + + 3 + + + + /SwBaseType/S16 + + + + /SystemSignal/Hello + + + + World1 + + + 3 + + + 2 + + + + /SwBaseType/S16 + + + + /SystemSignal/World1 + + + + World2 + + + 0 + + + 1 + + + + /SwBaseType/S16 + + + + /SystemSignal/World2 + + + + signal1 + + + 0b101 + + + 0b11 + /SystemSignal/Signal1 + + + + signal2 + 0xb + + + + /SwBaseType/S16 + + + + SystemSignalInner/Signal2 + + + + signal2_1c + 013 + + + + /SwBaseType/S16_1C + + + + /SystemSignal/SystemSignalInner/Signal2_1C + + + + signal2_sm + 11 + + + + /SwBaseType/S16_SM + + + + /SystemSignal/SystemSignalInner/Signal2_SM + + + + signal3 + 2 + + + + signal4 + 4 + + + + /SwBaseType/U8 + + + + /SystemSignal/Signal4 + + + + signal5 + 32 + + + + /SwBaseType/float + + + + + + + signal6 + + + BooleanFalse + + + 1 + /SystemSignal/Signal6 + + + + message1_CRC + 16 + + + + message1_SeqCounter + 16 + + + + message3_CRC + 8 + + + + message3_SeqCounter + 4 + + + + FireAlarm + + + BooleanFalse + + + 1 + /SystemSignal/FireAlarm + + + + message1Group + + /ISignal/signal1 + /ISignal/signal5 + /ISignal/signal6 + + + + + + /Transformers/Decepticons/Starscream + + 123 + 124 + 125 + 126 + 127 + 128 + 129 + 130 + 131 + 132 + 133 + 134 + 135 + 136 + 137 + 138 + + + + + + + + + message3Group + + /ISignal/message3_SeqCounter + /ISignal/message3_CRC + + + + + + /Transformers/Decepticons/Galvatron + + 321 + + + + + + + + + + + Transformers + + + + Decepticons + + + + Starscream + E2E + + + Profile2 + + + + + + Galvatron + E2E + + + Profile5 + + + + + + + + + + Constants + + + + BooleanTrue + + + + literal + true + + + + + + BooleanFalse + + + + literal + false + + + + + + + + MultiplexedIPdu + + + + multiplexed_message + 10 + + 0x070809 + + + + + + MOST-SIGNIFICANT-BYTE-LAST + 8 + 0 + + + + + /ISignalIPdu/multiplexed_message_0 + true + 0 + + + /ISignalIPdu/multiplexed_message_1 + false + 1 + + + + + 2 + 6 + + + /ISignalIPdu/multiplexed_message_static + + + + + + + + ContainerIPdu + + + + OneToContainThemAll + 64 + + /Cluster/Cluster0/Pch0/message1_triggering + /Cluster/Cluster0/Pch0/message2_triggering + /Cluster/Cluster0/Pch0/message3_triggering + /Cluster/Cluster0/Pch0/message3_secured_triggering + /Cluster/Cluster0/Pch0/multiplexed_triggering + + SHORT-HEADER + + + + + + ISignalIPdu + + + + multiplexed_message_static + 8 + + + + multiplexed_message_static_signal + /ISignal/MultiplexedStatic + 0 + + + + multiplexed_message_static2_signal + /ISignal/MultiplexedStatic2 + 8 + + + + + + multiplexed_message_0 + 8 + + + + multiplexed_message_selector + /ISignal/multiplexed_message_selector + 6 + + + + multiplexed_message_0_hello + /ISignal/Hello + 3 + + + + + + multiplexed_message_1 + 8 + + + + multiplexed_message_1_world1 + /ISignal/World1 + 4 + + + + multiplexed_message_1_world2 + /ISignal/World2 + 3 + + + + multiplexed_message_selector + /ISignal/multiplexed_message_selector + 6 + + + + + + message1 + 9 + + 0x0a0B0c + + + + + Counter + /ISignal/message1_SeqCounter + MOST-SIGNIFICANT-BYTE-LAST + 0 + + + + CRC + /ISignal/message1_CRC + MOST-SIGNIFICANT-BYTE-LAST + 16 + + + + Signal1 + /ISignal/signal1 + MOST-SIGNIFICANT-BYTE-FIRST + 36 + + + + Signal5 + /ISignal/signal5 + MOST-SIGNIFICANT-BYTE-LAST + 40 + + + + Signal6 + /ISignal/signal6 + MOST-SIGNIFICANT-BYTE-LAST + 32 + + + + + SignalGroup + /ISignal/message1Group + + + + + + message2 + 7 + + 0x1D2E3F + + + + + + + + 0 + + + 0.1 + + + + + + + 0 + + + 0.2 + + + + + + + + + + Signal2 + /ISignal/signal2 + MOST-SIGNIFICANT-BYTE-LAST + 18 + + + + Signal3 + /ISignal/signal3 + MOST-SIGNIFICANT-BYTE-LAST + 6 + + + + Signal4 + /ISignal/signal4 + MOST-SIGNIFICANT-BYTE-LAST + 30 + + + + + + message3 + 4 + + 0x010203 + + + + + message3_CRC + /ISignal/message3_CRC + MOST-SIGNIFICANT-BYTE-LAST + 0 + + + + message3_SeqCounter + /ISignal/message3_SeqCounter + MOST-SIGNIFICANT-BYTE-LAST + 8 + + + + SignalGroup + /ISignal/message3Group + + + + + + message4 + 6 + + + + Signal2 + /ISignal/signal2 + MOST-SIGNIFICANT-BYTE-LAST + 0 + + + + Signal2_1C + /ISignal/signal2_1c + MOST-SIGNIFICANT-BYTE-LAST + 16 + + + + Signal2_SM + /ISignal/signal2_sm + MOST-SIGNIFICANT-BYTE-LAST + 32 + + + 85 + + + + + + NMPdu + + + + alarm_status + 1 + + + + fire_alarm + /ISignal/FireAlarm + 0 + + + + + + + + SecuredIPdu + + + + message3_secured + 6 + + 0x040506 + + /SecOCProps/S/KnockKnock + /SecOCProps/S/SmellyCheese + /Cluster/Cluster0/Pch0/message3_triggering + + 1337 + + + + + + + SecOCProps + + + + S + + + + KnockKnock + 10 + + + + + + SmellyCheese + 32 + 6 + + + + + + + + Unit + + + + meters + m + + + + wizepoo + wp + + + + zilch + NoUnit + + + + + + CompuMethod + + + + Identical + IDENTICAL + + + + MultiplexedMessageSelector + TEXTTABLE + + + + 0 + 0 + + SELECT_HELLO + + + + 1 + 1 + + SELECT_WORLD + + + + 3 + 3 + + INVALID_SELECTION + + + + + + + + Signal1 + LINEAR + + + + 0 + 4 + + + 0 + 10 + + + 2 + + + + + + + + + Signal4 + TEXTTABLE + /Unit/zilch + + + + + One Comment + Ein Kommentar + + 1 + 1 + + one + + + + 2 + 2 + + two + + + + + + + + Signal6 + SCALE_LINEAR_AND_TEXTTABLE + /Unit/wizepoo + + + + + Nothing + Nichts + + 0 + 0 + + zero + + + + 0 + 1 + + + 0 + 0.1 + + + 1 + + + + + + + + + + + SystemSignal + + + + MultiplexedMessageStatic + + + + /CompuMethod/Identical + + + + + + + MultiplexedMessageStatic2 + + + + /CompuMethod/Identical + + + + + + + MultiplexedMessageSelector + + + + /CompuMethod/MultiplexedMessageSelector + + + + + + + Hello + + + + /CompuMethod/Identical + + + + + + + World1 + + + + /CompuMethod/Identical + + + + + + + World2 + + + + /CompuMethod/Identical + + + + + + + Signal1 + + Signal comment! + Signalkommentar! + + + + + /CompuMethod/Signal1 + /Unit/meters + + + + + + + Signal4 + + + + /CompuMethod/Signal4 + + + + + + + Signal6 + + + + /CompuMethod/Signal6 + + + + + + + FireAlarm + + + + /CompuMethod/Identical + + + + + + + + + SystemSignalInner + + + + Signal2 + + Signal comment! + + + + + Signal2_1C + + Signal comment! (1-Complement) + + + + + Signal2_SM + + + + + + + + + + + SwBaseType + + + + S16 + FIXED_LENGTH + 16 + 2C + + + + S16_1C + FIXED_LENGTH + 16 + 1C + + + + S16_SM + FIXED_LENGTH + 16 + SM + + + + U8 + FIXED_LENGTH + 8 + NONE + + + + float + FIXED_LENGTH + 32 + IEEE754 + + + + + + System + + + + cantools_system + SYSTEM_EXTRACT + MOST-SIGNIFICANT-BYTE-FIRST + + + + + From 4c42add3bc54b00e815573137a2ce251cbbadb3c Mon Sep 17 00:00:00 2001 From: Martin Heyden Date: Mon, 3 Jun 2024 05:02:41 -0400 Subject: [PATCH 07/18] added optional serialization that speeds up parsing for following runs --- opendut-edgar/restbus-simulation/Cargo.toml | 2 ++ .../restbus-simulation/src/arxml_parser.rs | 24 +++++++++++++++++- .../restbus-simulation/src/arxml_structs.rs | 14 +++++++++++ .../restbus-simulation/src/arxml_utils.rs | 25 ++++++++++++++++++- opendut-edgar/restbus-simulation/src/main.rs | 2 +- 5 files changed, 64 insertions(+), 3 deletions(-) diff --git a/opendut-edgar/restbus-simulation/Cargo.toml b/opendut-edgar/restbus-simulation/Cargo.toml index 7d0691a9..f485f642 100755 --- a/opendut-edgar/restbus-simulation/Cargo.toml +++ b/opendut-edgar/restbus-simulation/Cargo.toml @@ -9,3 +9,5 @@ edition = "2021" autosar-data = "0.9.0" nix = "0.25.1" tokio = { version = "1", features = ["full"] } +serde = { version = "1.0", features = ["derive"] } +serde_json = "1.0" diff --git a/opendut-edgar/restbus-simulation/src/arxml_parser.rs b/opendut-edgar/restbus-simulation/src/arxml_parser.rs index f27f6625..c5de0351 100755 --- a/opendut-edgar/restbus-simulation/src/arxml_parser.rs +++ b/opendut-edgar/restbus-simulation/src/arxml_parser.rs @@ -440,9 +440,23 @@ impl ArxmlParser { Main parsing method. Uses autosar-data libray for parsing ARXML. In the future, it might be extended to support Ethernet, Flexray, ... The resources to develop that should not be thaat high, since it is basically just extending the current parser. + Param file_name: ARXML target file name without ".ser" extension + Param safe_or_load_serialized: First look if serialized parsed data already exists by looking for file_name + ".ser". + If not exists, then parse and safe parsed structures as serialized data in file_name + ".ser" Returns a vector of CanCluster structures. */ - pub fn parse_file(&self, file_name: String) -> Option> { + pub fn parse_file(&self, file_name: &String, safe_or_load_serialized: bool) -> Option> { + if safe_or_load_serialized { + println!("[+] Loading data from serialized file"); + match load_serialized_data(file_name) { + Ok(value) => { + println!("[+] Successfully loaded serialized data."); + return Some(value) + } + _ => println!("[-] Could not load serialized data. Will continue parsing.") + } + } + let start = Instant::now(); let model = AutosarModel::new(); @@ -479,6 +493,14 @@ impl ArxmlParser { println!("[+] Duration of parsing: {:?}", start.elapsed()); + if safe_or_load_serialized { + println!("[+] Storing serialized data to file"); + match store_serialized_data(file_name, &can_clusters) { + Ok(()) => println!("[+] Successfully stored serialized data."), + _ => println!("[-] Could not store serialized data.") + } + } + return Some(can_clusters); } } diff --git a/opendut-edgar/restbus-simulation/src/arxml_structs.rs b/opendut-edgar/restbus-simulation/src/arxml_structs.rs index 9bb002d4..e5b14911 100755 --- a/opendut-edgar/restbus-simulation/src/arxml_structs.rs +++ b/opendut-edgar/restbus-simulation/src/arxml_structs.rs @@ -5,10 +5,13 @@ use std::collections::HashMap; +use serde::{Serialize, Deserialize}; + /* Represents important data from Autosar CanCluster element. */ #[derive(Debug)] +#[derive(Serialize, Deserialize)] pub struct CanCluster { pub name: String, pub baudrate: u64, @@ -20,6 +23,7 @@ pub struct CanCluster { Represents important data from Autosar CanFrameTriggering element. */ #[derive(Debug)] +#[derive(Serialize, Deserialize)] pub struct CanFrameTriggering { pub frame_triggering_name: String, pub frame_name: String, @@ -39,6 +43,7 @@ pub struct CanFrameTriggering { Represents important parent data from an Autosar *PDU element. */ #[derive(Debug)] +#[derive(Serialize, Deserialize)] pub struct PDUMapping { pub name: String, pub byte_order: bool, @@ -55,6 +60,7 @@ pub struct PDUMapping { Enum of all supported PDU types. */ #[derive(Debug)] +#[derive(Serialize, Deserialize)] pub enum PDU { ISignalIPDU(ISignalIPDU), NMPDU(NMPDU), @@ -69,6 +75,7 @@ pub enum PDU { Represents important data from an Autosar ISignalIPDU element. */ #[derive(Debug)] +#[derive(Serialize, Deserialize)] pub struct ISignalIPDU { pub cyclic_timing_period_value: f64, pub cyclic_timing_period_tolerance: Option, @@ -86,6 +93,7 @@ pub struct ISignalIPDU { Represents important data from an Autosar NmPdu element. */ #[derive(Debug)] +#[derive(Serialize, Deserialize)] pub struct NMPDU { pub unused_bit_pattern: bool, pub ungrouped_signals: Vec, @@ -96,6 +104,7 @@ pub struct NMPDU { Represents important data from an Autosar ISignal element. */ #[derive(Debug)] +#[derive(Serialize, Deserialize)] pub struct ISignal { pub name: String, pub byte_order: bool, @@ -109,6 +118,7 @@ pub struct ISignal { */ #[derive(Debug)] #[derive(Clone)] +#[derive(Serialize, Deserialize)] pub enum InitValues { Single(u64), Array(Vec), @@ -119,6 +129,7 @@ pub enum InitValues { Represents important data from an Autosar ISignal element. */ #[derive(Debug)] +#[derive(Serialize, Deserialize)] pub struct E2EDataTransformationProps { pub transformer_name: String, pub data_id: u64, @@ -126,6 +137,7 @@ pub struct E2EDataTransformationProps { } #[derive(Debug)] +#[derive(Serialize, Deserialize)] pub struct ISignalGroup { pub name: String, pub isignals: Vec, @@ -134,12 +146,14 @@ pub struct ISignalGroup { } #[derive(Debug)] +#[derive(Serialize, Deserialize)] pub enum TimeRangeTolerance { Relative(u64), Absolute(f64), } #[derive(Debug)] +#[derive(Serialize, Deserialize)] pub struct TimeRange { pub tolerance: Option, pub value: f64, diff --git a/opendut-edgar/restbus-simulation/src/arxml_utils.rs b/opendut-edgar/restbus-simulation/src/arxml_utils.rs index 5fc21b08..d6a096ac 100755 --- a/opendut-edgar/restbus-simulation/src/arxml_utils.rs +++ b/opendut-edgar/restbus-simulation/src/arxml_utils.rs @@ -7,6 +7,10 @@ use crate::restbus_structs::*; use crate::restbus_utils::*; use std::collections::HashMap; +use std::fs::File; +use std::io::prelude::*; +use std::io::Error; + use autosar_data::{CharacterData, Element, ElementName, EnumItem}; @@ -295,7 +299,7 @@ fn process_isignal_init_value(isignal: &ISignal, bits: &mut Vec) { _ => return } - if tmp_bit_array.len() != isignal.length.try_into().unwrap() { + if tmp_bit_array.len() != >::try_into(isignal.length).unwrap() { panic!("Miscalculation for tmp_bit_array"); } @@ -661,4 +665,23 @@ pub fn get_timed_can_frames_from_bus(can_clusters: &HashMap, } return timed_can_frames +} + +pub fn load_serialized_data(file_name: &String) -> Result, Error> { + let mut file = File::open(file_name.to_owned() + ".ser")?; + let mut contents = String::new(); + file.read_to_string(&mut contents)?; + + let deserialized: HashMap = serde_json::from_str(&contents)?; + + return Ok(deserialized); +} + +pub fn store_serialized_data(file_name: &String, can_clusters: &HashMap) -> Result<(), Error> { + let serialized = serde_json::to_string(can_clusters)?; + + let mut file = File::create(file_name.to_owned() + ".ser")?; + file.write_all(serialized.as_bytes())?; + + Ok(()) } \ No newline at end of file diff --git a/opendut-edgar/restbus-simulation/src/main.rs b/opendut-edgar/restbus-simulation/src/main.rs index f89bd9e7..248081c4 100755 --- a/opendut-edgar/restbus-simulation/src/main.rs +++ b/opendut-edgar/restbus-simulation/src/main.rs @@ -85,7 +85,7 @@ fn main() -> std::io::Result<()> { let arxml_parser: ArxmlParser = ArxmlParser {}; if let Some(can_clusters) = arxml_parser - .parse_file(file_name.to_string()) + .parse_file(&file_name.to_string(), true) { let bus_name = "Cluster0"; let target_file = "system.txt"; From b60f239a6061ed053b8e3798fff4f05c54f13f93 Mon Sep 17 00:00:00 2001 From: Martin Heyden <33530907+mrh1234@users.noreply.github.com> Date: Tue, 4 Jun 2024 15:59:24 +0200 Subject: [PATCH 08/18] Added some documentation for the restbus-simulation --- .../architecture/restbus-simulation/index.md | 76 +++++++++++++++++++ 1 file changed, 76 insertions(+) create mode 100644 doc/src/architecture/restbus-simulation/index.md diff --git a/doc/src/architecture/restbus-simulation/index.md b/doc/src/architecture/restbus-simulation/index.md new file mode 100644 index 00000000..6fad46b8 --- /dev/null +++ b/doc/src/architecture/restbus-simulation/index.md @@ -0,0 +1,76 @@ +# Restbus Simulation + +### Summary +This module provides a restbus-simulation to the OpenDuT user and it is split into an **_ARXML parser (AXP)_** +sub-module and a **_restbus-simulation (RSIM)_** sub-module. The AXP parses an ARXML file and the parsed data +can be used by the RSIM to establish a working simulation that knows about all the Frames/PDUs/Signals and +handles the simulation of them. + +Live changes of signals/timings/... shall be implemented by the end-user, which can use a simple API of the RSIM to +define changes in an abstract way. The goal is not to achieve the same functionality as the +well-known restbus-simulation tool. Instead, the OpenDuT user should have an easy possibility +to simulate a base environment that improves testing. + +Current implementation state: + - AXP -> Done + - RSIM -> Base implementation done and tested successfully. Have to extend it + to handle all types of PDUs and do some more modifications. + - RSIM API -> Todo + +### ARXML Parser (AXP) sub-module +This module parses an ARXML (Autosar XML) file and extracts all values necessary for a restbus-simulation. +First, the [autosar-data crate](https://crates.io/crates/autosar-data/0.9.0) is used for parsing an ARXML file. +Then, important data is extracted from the parsed data and some post-processing is made. The resulting +data is stored in structures, which basically represent different +[Autosar Elements](https://www.autosar.org/fileadmin/standards/R22-11/CP/AUTOSAR_TPS_SystemTemplate.pdf). + +Parsing and post-processing a big ARXML file can take a long time. For example, for a ~300 MB ARXML file, we need +around 40 seconds on a standard laptop. Therefore, the parser can be instructed to serialize the resulting structures and store them into +a file. This enables a +very quick re-establishment of everything, since we do not need to parse and post-process data for a second time. +Instead, the next time we run the program, we just can deserialize the data, which takes less than one second. + +The resulting structures can be modified before passing them to the RSIM. There is no direct API for creation/modification of +structures implemented yet, but manually modifying the structures by making use of AXP helper methods is easily possible. +If a use-case for structure modification exists, then a later API might be implemented, which +should not take a lot of time. However, currently, the idea is that everything is already properly defined through the +ARXML file. + +### Restbus-Simulation (RSIM) sub-module +The RSIM can be fed with the structures coming from the AXP. With these structures, the RSIM exactly knows how +Frames/PDUs/Signals/Timings/Initial Values, ... look like. It handles all the lower-level things and controls what is and how it is send +to the Bus. The user always has just an abstract overview of everything. See the **Configuration** section for learning about the configuration of everything. + +The RSIM makes use of the [Linux SocketCAN Broadcast Manager (BCM)](https://www.kernel.org/doc/Documentation/networking/can.txt), +which handles all the timing of (optionally) periodically sent messages. The BCM is setup and modified +via **BCM sockets**, in which we can define message bytes and their timing information. The Kernel handles then +the correct message transmission + timing. Furthermore, the BCM will also be used +to dynamically modify messages and their timing during runtime. + +The user itself has to provide the status changing logic. +With a simple API (see next section), the user can instruct the RSIM to modify the data that is sent to the bus. +The user has then control over single signals, timings, and more, by using the API. **For example**, we have a periodically sent message definining +that the car's doors are locked. The RSIM completely handles the periodic sending with the right timing etc. +The user can then tell the RSIM that the status +has changed by instructing the RSIM to change the signal (lock status) of that particular message or all messages +referencing that signal. This will be possible +with a simple API call like "Change _Signal CarLock_ to 0 (false)". As a result, the message/s referencing the signal +will be adapted automatically by the RSIM and the user does not need to know about any low-level implementation. +The details to the API will follow and will be +defined in the **RSIM API** section. +Right now no API exists, and the RSIM just plays Frames with initial values to the Bus. + +### RSIM API +Idea to discuss: + - TODO. + +### Configuration +Currently, the implementation is somewhat independent of OpenDuT. In the future, everything will be integrated +including the configuration. You can see in [main.rs](https://github.com/eclipse-opendut/opendut/blob/restbus-simulation/opendut-edgar/restbus-simulation/src/main.rs) +how to establish a restbus-simulation without configuration. + +### References: + - [autosar-data crate](https://crates.io/crates/autosar-data/0.9.0) + - [Autosar System Template](https://www.autosar.org/fileadmin/standards/R22-11/CP/AUTOSAR_TPS_SystemTemplate.pdf) + - [Linux SocketCAN Broadcast Manager (BCM)](https://www.kernel.org/doc/Documentation/networking/can.txt) + From 6b62b130d5aa9b5fed397d8fc4393c08ceab153b Mon Sep 17 00:00:00 2001 From: Martin Heyden <33530907+mrh1234@users.noreply.github.com> Date: Fri, 7 Jun 2024 16:41:25 +0200 Subject: [PATCH 09/18] Update index.md --- .../architecture/restbus-simulation/index.md | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/doc/src/architecture/restbus-simulation/index.md b/doc/src/architecture/restbus-simulation/index.md index 6fad46b8..515bf378 100644 --- a/doc/src/architecture/restbus-simulation/index.md +++ b/doc/src/architecture/restbus-simulation/index.md @@ -60,9 +60,20 @@ The details to the API will follow and will be defined in the **RSIM API** section. Right now no API exists, and the RSIM just plays Frames with initial values to the Bus. -### RSIM API -Idea to discuss: - - TODO. +### AXP + RSIM API and Integration +*_Idea to discuss:_* +- Implement MQTT client (MC) that controls AXP and RSIM +- MC builds basically the API. Every instance that can communicate with the MQTT server, also can communicate with our client. +- MC gets started in a separate thread by main Edgar on startup (if enabled) +- MC listens to instructions by polling pre-existing MQTT server and sends results/responses to MQTT server +- AXP can be instructed via API (i.e. via MC) to parse files that are located on EDGAR + - ARXML file is assumed to be located on Edgar indepedently by transfer from CARL to EDGAR or previous manual transfer + - AXP can be instructed separately from RSIM or a setup can be combined, i.e., a single command leads to parsing of ARXML file and restbus-simulation setup +- RSIM API provides control of RSIM by providing a simple API that can be used via MQTT (MC polls from server for commands) + - Even with the simple API, a very fine-grained control of RSIM is possible, since every important feature should be available. + - openDuT user provides updates of signals via API, while RSIM handles all the low-level details and ensures correctness of transmitted messages and their timings + - It's the user's task to provide the logic for dynamic changes of signals/timings/... during runtime + - Either the user logic is integrated via separate binaries/Python files/..., or we might be able to use (compiled) CAPL files, that already contain the logic for existing restbus simulations ### Configuration Currently, the implementation is somewhat independent of OpenDuT. In the future, everything will be integrated From 6615bf9a0ba8f30f32d93ee6a6bb19bda0d8951d Mon Sep 17 00:00:00 2001 From: Martin Heyden Date: Thu, 11 Jul 2024 08:09:08 -0400 Subject: [PATCH 10/18] added some comments to main.rs of Restbus Simulation --- opendut-edgar/restbus-simulation/src/main.rs | 41 +++++++++++++++++--- 1 file changed, 35 insertions(+), 6 deletions(-) diff --git a/opendut-edgar/restbus-simulation/src/main.rs b/opendut-edgar/restbus-simulation/src/main.rs index 248081c4..732e79be 100755 --- a/opendut-edgar/restbus-simulation/src/main.rs +++ b/opendut-edgar/restbus-simulation/src/main.rs @@ -18,6 +18,9 @@ use restbus_simulation::*; use restbus_structs::TimedCanFrame; +/* + Debug method. Combines the CAN ID and intial values as hex of a PDU. Returns this as string. +*/ fn get_pdu_hex(can_id: &u64, init_values: &Vec) -> String { let mut hex_string = String::new(); hex_string.push_str(&format!("{}:", can_id)); @@ -29,6 +32,10 @@ fn get_pdu_hex(can_id: &u64, init_values: &Vec) -> String { return hex_string; } + +/* + Debug method. Iterates over all PDUs of the target bus and returns all combinations of CAN IDs and initial values as string. +*/ fn collect_pdus(can_clusters: &HashMap, bus_name: String) -> Vec { let mut init_values_strings: Vec = Vec::new(); if let Some(can_cluster) = can_clusters.get(&bus_name) { @@ -58,6 +65,10 @@ fn collect_pdus(can_clusters: &HashMap, bus_name: String) -> return init_values_strings; } + +/* + Play a single CAN frame from the target bus periodcically (if it is periodic) to the bus to which ifname is connected to. +*/ fn test_find_frame_and_play(restbus_simulation: &RestbusSimulation, ifname: &String, can_clusters: &HashMap, bus_name: String, can_id: u64) { let timed_can_frames: Vec = get_timed_can_frame_from_id(can_clusters, bus_name, can_id); @@ -67,23 +78,34 @@ fn test_find_frame_and_play(restbus_simulation: &RestbusSimulation, ifname: &Str } } - + + +/* + Play all CAN frames from the target bus periodcically (only periodic frames are sent periodically) to the bus to which ifname is connected to. +*/ fn test_bus_play_all(restbus_simulation: &RestbusSimulation, ifname: &String, can_clusters: &HashMap, bus_name: String) { let timed_can_frames: Vec = get_timed_can_frames_from_bus(can_clusters, bus_name); match restbus_simulation.play_all(&timed_can_frames, ifname) { Ok(_val) => println!("Successfully established restbus simulation"), - Err(error) => println!("Could establish restbus simulation because: {}", error) + Err(error) => println!("Could not establish restbus simulation because: {}", error) } } + +/* + This shows an example of how to create a restbus-simulation with an ARXML file. +*/ fn main() -> std::io::Result<()> { println!("[+] Starting openDuT ARXML parser over main method."); - + + // Define file name. It is used as a path. let file_name = "system-4.2.arxml"; // from https://github.com/cantools/cantools/blob/master/tests/files/arxml/system-4.2.arxml + // Create ArxmlParser struct, which contains all the parsing methods. let arxml_parser: ArxmlParser = ArxmlParser {}; + // Parse the ARXML file. Use serialized file if it exists. Parsed data is stored in can cluster ARXML element representations. if let Some(can_clusters) = arxml_parser .parse_file(&file_name.to_string(), true) { @@ -91,32 +113,39 @@ fn main() -> std::io::Result<()> { let target_file = "system.txt"; let play_single_or_all = true; let ifname = String::from("vcan0"); - + + // Debug output for cluster in can_clusters.values() { println!("CanCluster: {cluster:?}"); } - + + // Debug output let mut frames = String::new(); for frame in collect_pdus(&can_clusters, String::from(bus_name)) { frames.push_str(frame.as_str()); frames.push_str("\n"); } + // Debug. Found PDUs of target bus are written to file. let mut f = File::create(target_file)?; f.write_all(&frames.as_bytes())?; println!("Trying to setup up restbus simulation"); + // Create RestbusSimulation structure, which contains all the relevant methods for the restbus simulation. let restbus_simulation: RestbusSimulation = RestbusSimulation {}; if !play_single_or_all { + // Play a single CAN frame from the target bus periodcically (if it is periodic) to the bus to which ifname is connected to. test_find_frame_and_play(&restbus_simulation, &ifname, &can_clusters, String::from(bus_name), 0x3E9); } else { + // Play all CAN frames from the target bus periodcically (only periodic frames are sent periodically) to the bus to which ifname is connected to. test_bus_play_all(&restbus_simulation, &ifname, &can_clusters, String::from(bus_name)); } - thread::sleep(Duration::from_secs(30)); + // Sleep as long as restbus-simulation should run. + thread::sleep(Duration::from_secs(300)); } Ok(()) } From 1a81656225535ea30baa28d97b723b23c064c18c Mon Sep 17 00:00:00 2001 From: Martin Heyden Date: Fri, 12 Jul 2024 10:57:52 -0400 Subject: [PATCH 11/18] rebased to development --- Cargo.lock | 49 ++++++++++++++++++- Cargo.toml | 3 ++ opendut-edgar/Cargo.toml | 1 + opendut-edgar/edgar.toml | 7 +++ opendut-edgar/restbus-simulation/Cargo.toml | 20 +++++--- .../restbus-simulation/src/arxml_parser.rs | 22 +++++---- opendut-edgar/restbus-simulation/src/lib.rs | 6 +++ opendut-edgar/src/service/start.rs | 29 +++++++++++ 8 files changed, 119 insertions(+), 18 deletions(-) create mode 100644 opendut-edgar/restbus-simulation/src/lib.rs diff --git a/Cargo.lock b/Cargo.lock index 1022dabe..2a739d9c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -281,6 +281,29 @@ version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" +[[package]] +name = "autosar-data" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "220cf40add8815bc13a6c409a2ad62304ec0354e149bbcfe3c3516a1cf3f1726" +dependencies = [ + "autosar-data-specification", + "parking_lot 0.12.3", + "rustc-hash", + "smallvec", + "thiserror", +] + +[[package]] +name = "autosar-data-specification" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4b10b95c5e9c2cf046261b1d71e4ef6d063569378ac6a95687a9a88fb053852d" +dependencies = [ + "num-derive", + "num-traits", +] + [[package]] name = "autotools" version = "0.2.7" @@ -3451,6 +3474,17 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9" +[[package]] +name = "num-derive" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed3955f1a9c7c0c15e092f9c887db08b1fc683305fdf6eb6684f22555355e202" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.66", +] + [[package]] name = "num-integer" version = "0.1.46" @@ -3759,6 +3793,7 @@ dependencies = [ "opendut-edgar-kernel-modules", "opendut-edgar-plugin-api", "opendut-netbird-client-api", + "opendut-restbus-simulation", "opendut-types", "opendut-util", "opentelemetry", @@ -3877,6 +3912,18 @@ dependencies = [ "url", ] +[[package]] +name = "opendut-restbus-simulation" +version = "0.1.0" +dependencies = [ + "autosar-data", + "nix 0.27.1", + "serde", + "serde_json", + "tokio", + "tracing", +] + [[package]] name = "opendut-theo" version = "0.3.0-dev" @@ -5748,7 +5795,7 @@ dependencies = [ [[package]] name = "smallvec" -version = "1.13.2" +version = "1.11.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" dependencies = [ diff --git a/Cargo.toml b/Cargo.toml index 69e3df20..443095d3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -7,6 +7,7 @@ members = [ "opendut-edgar/netbird-client-api", "opendut-edgar/opendut-edgar-kernel-modules", "opendut-edgar/plugin-api", + "opendut-edgar/restbus-simulation", "opendut-lea", "opendut-types", "opendut-util", @@ -39,6 +40,7 @@ opendut-edgar-plugin-api = { path = "opendut-edgar/plugin-api" } opendut-netbird-client-api = { path = "opendut-edgar/netbird-client-api" } opendut-edgar-kernel-modules = { path = "opendut-edgar/opendut-edgar-kernel-modules" } opendut-vpn-netbird = { path = "opendut-vpn/opendut-vpn-netbird" } +opendut-restbus-simulation = { path = "opendut-edgar/restbus-simulation" } opendut-types = { path = "opendut-types" } opendut-theo = { path = ".ci/docker/theo" } opendut-util = { path = "opendut-util" } @@ -48,6 +50,7 @@ opendut-vpn = { path = "opendut-vpn" } anyhow = "1.0.79" assert_fs = "1.1.1" async-trait = "0.1.77" +autosar-data = "0.9.0" axum = "0.6.20" axum-server = "0.5.1" axum-server-dual-protocol = "0.5.2" diff --git a/opendut-edgar/Cargo.toml b/opendut-edgar/Cargo.toml index 8c283bb5..82a33121 100644 --- a/opendut-edgar/Cargo.toml +++ b/opendut-edgar/Cargo.toml @@ -10,6 +10,7 @@ opendut-auth = { workspace = true, features = ["registration_client"] } opendut-carl-api = { workspace = true, features = ["client"] } opendut-edgar-kernel-modules = { workspace = true } opendut-netbird-client-api = { workspace = true } +opendut-restbus-simulation = { workspace = true } opendut-types = { workspace = true } opendut-util = { workspace = true } diff --git a/opendut-edgar/edgar.toml b/opendut-edgar/edgar.toml index 909caaac..e3188e5a 100644 --- a/opendut-edgar/edgar.toml +++ b/opendut-edgar/edgar.toml @@ -49,3 +49,10 @@ cpu.collection.interval.ms = 5000 ping.interval.ms = 30000 target.bandwidth.kilobit.per.second = 100_000 rperf.backoff.max.elapsed.time.ms = 120000 + +[restbus-simulation] +enabled = false +arxml.path = "./opendut-edgar/restbus-simulation/src/system-4.2.arxml" +arxml.serialization = true +simulation.target.cluster = "Cluster0" +simulation.interface = "vcan0" diff --git a/opendut-edgar/restbus-simulation/Cargo.toml b/opendut-edgar/restbus-simulation/Cargo.toml index f485f642..4b863ad2 100755 --- a/opendut-edgar/restbus-simulation/Cargo.toml +++ b/opendut-edgar/restbus-simulation/Cargo.toml @@ -1,13 +1,19 @@ [package] -name = "restbus" +name = "opendut-restbus-simulation" version = "0.1.0" -edition = "2021" +edition.workspace = true +rust-version.workspace = true +license.workspace = true # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -autosar-data = "0.9.0" -nix = "0.25.1" -tokio = { version = "1", features = ["full"] } -serde = { version = "1.0", features = ["derive"] } -serde_json = "1.0" +autosar-data = { workspace = true } +nix = { workspace = true } +tokio = { workspace = true } +tracing = { workspace = true } +serde = { workspace = true } +serde_json = { workspace = true } + +[lints] +workspace = true diff --git a/opendut-edgar/restbus-simulation/src/arxml_parser.rs b/opendut-edgar/restbus-simulation/src/arxml_parser.rs index c5de0351..ba3f4753 100755 --- a/opendut-edgar/restbus-simulation/src/arxml_parser.rs +++ b/opendut-edgar/restbus-simulation/src/arxml_parser.rs @@ -16,6 +16,8 @@ use core::panic; use autosar_data::{AutosarModel, CharacterData, Element, ElementName, EnumItem}; +use tracing::{error, info, warn}; + pub struct ArxmlParser { } @@ -420,7 +422,7 @@ impl ArxmlParser { Ok(value) => { can_frame_triggerings.insert(value.can_id.clone(), value); } - Err(error) => println!("[-] WARNING: {}", error), + Err(error) => error!("WARNING: {}", error), } } } @@ -447,13 +449,13 @@ impl ArxmlParser { */ pub fn parse_file(&self, file_name: &String, safe_or_load_serialized: bool) -> Option> { if safe_or_load_serialized { - println!("[+] Loading data from serialized file"); + info!("Loading data from serialized file"); match load_serialized_data(file_name) { Ok(value) => { - println!("[+] Successfully loaded serialized data."); + info!("Successfully loaded serialized data."); return Some(value) } - _ => println!("[-] Could not load serialized data. Will continue parsing.") + _ => warn!("Could not load serialized data. Will continue parsing.") } } @@ -466,7 +468,7 @@ impl ArxmlParser { } // DEBUG - println!("[+] Duration of loading was: {:?}", start.elapsed()); + info!("Duration of loading was: {:?}", start.elapsed()); // DEBUG END let mut can_clusters: HashMap = HashMap::new(); @@ -484,20 +486,20 @@ impl ArxmlParser { Ok(value) => { can_clusters.insert(value.name.clone(), value); } - Err(error) => println!("[-] WARNING: {}", error) + Err(error) => warn!("WARNING: {}", error) } } _ => {} } } - println!("[+] Duration of parsing: {:?}", start.elapsed()); + info!("Duration of parsing: {:?}", start.elapsed()); if safe_or_load_serialized { - println!("[+] Storing serialized data to file"); + info!("Storing serialized data to file"); match store_serialized_data(file_name, &can_clusters) { - Ok(()) => println!("[+] Successfully stored serialized data."), - _ => println!("[-] Could not store serialized data.") + Ok(()) => info!("Successfully stored serialized data."), + _ => error!("Could not store serialized data.") } } diff --git a/opendut-edgar/restbus-simulation/src/lib.rs b/opendut-edgar/restbus-simulation/src/lib.rs new file mode 100644 index 00000000..573baf1c --- /dev/null +++ b/opendut-edgar/restbus-simulation/src/lib.rs @@ -0,0 +1,6 @@ +pub mod arxml_parser; +pub mod arxml_structs; +pub mod arxml_utils; +pub mod restbus_simulation; +pub mod restbus_utils; +pub mod restbus_structs; diff --git a/opendut-edgar/src/service/start.rs b/opendut-edgar/src/service/start.rs index 7c19ca8c..bf4d2d6e 100644 --- a/opendut-edgar/src/service/start.rs +++ b/opendut-edgar/src/service/start.rs @@ -8,6 +8,12 @@ use anyhow::Context; use opendut_carl_api::proto::services::peer_messaging_broker; use opendut_carl_api::proto::services::peer_messaging_broker::downstream::Message; use opendut_carl_api::proto::services::peer_messaging_broker::{ApplyPeerConfiguration, TracingContext}; +use opendut_carl_api::proto::services::peer_messaging_broker::downstream::Message; +use opendut_restbus_simulation::arxml_parser::ArxmlParser; +use opendut_restbus_simulation::arxml_utils::get_timed_can_frames_from_bus; +use opendut_restbus_simulation::restbus_simulation::RestbusSimulation; +use opendut_restbus_simulation::restbus_structs::TimedCanFrame; +use opendut_types::cluster::{ClusterAssignment, PeerClusterAssignment}; use opendut_types::peer::configuration::{OldPeerConfiguration, PeerConfiguration}; use opendut_types::peer::PeerId; use opendut_util::settings::LoadedConfig; @@ -119,6 +125,29 @@ pub async fn run_stream_receiver( }, } }; + + let restbus_simulation: RestbusSimulation = RestbusSimulation {}; + + let restbus_simulation_enabled = settings.config.get::("restbus-simulation.enabled")?; + + if restbus_simulation_enabled { + let arxml_file_path = settings.config.get::("restbus-simulation.arxml.path")?; + let arxml_serialization = settings.config.get::("restbus-simulation.arxml.serialization")?; + let target_cluster = settings.config.get::("restbus-simulation.simulation.target.cluster")?; + let ifname = settings.config.get::("restbus-simulation.simulation.interface")?; + + let arxml_parser: ArxmlParser = ArxmlParser {}; + + if let Some(can_clusters) = arxml_parser.parse_file(&arxml_file_path, arxml_serialization) { + // Play all CAN frames from the target bus periodcically (only periodic frames are sent periodically) to the bus to which ifname is connected to. + let timed_can_frames: Vec = get_timed_can_frames_from_bus(&can_clusters, target_cluster); + + match restbus_simulation.play_all(&timed_can_frames, &ifname) { + Ok(_val) => info!("Successfully established restbus simulation"), + Err(error) => info!("Could not establish restbus simulation because: {}", error) + } + } + } let remote_address = vpn::retrieve_remote_host(&settings).await?; From fa59e49fba3ed3f87daa05763c65d27617ae39e1 Mon Sep 17 00:00:00 2001 From: Martin Heyden Date: Wed, 17 Jul 2024 09:53:48 -0400 Subject: [PATCH 12/18] adapted code to be ready for any architecture --- opendut-edgar/restbus-simulation/src/restbus_structs.rs | 6 ++++++ opendut-edgar/restbus-simulation/src/restbus_utils.rs | 8 ++++---- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/opendut-edgar/restbus-simulation/src/restbus_structs.rs b/opendut-edgar/restbus-simulation/src/restbus_structs.rs index 845a10f9..421e2510 100755 --- a/opendut-edgar/restbus-simulation/src/restbus_structs.rs +++ b/opendut-edgar/restbus-simulation/src/restbus_structs.rs @@ -104,3 +104,9 @@ pub enum BCMFlags { RxRtrFrame = 0x0400,*/ CanFdFrame = 0x0800 } + +#[cfg(target_pointer_width = "64")] +pub type TimevalNum = i64; + +#[cfg(target_pointer_width = "32")] +pub type TimevalNum = i32; \ No newline at end of file diff --git a/opendut-edgar/restbus-simulation/src/restbus_utils.rs b/opendut-edgar/restbus-simulation/src/restbus_utils.rs index fe745733..2920d997 100755 --- a/opendut-edgar/restbus-simulation/src/restbus_utils.rs +++ b/opendut-edgar/restbus-simulation/src/restbus_utils.rs @@ -159,8 +159,8 @@ pub fn create_time_can_frame_structure(count: u32, ival1_tv_sec: u64, ival1_tv_u frame_tx_behavior: frame_tx_behavior, data_vector: copy_data_vector, count: count, - ival1: timeval { tv_sec: ival1_tv_sec as i64, tv_usec: ival1_tv_usec as i64 }, - ival2: timeval { tv_sec: ival2_tv_sec as i64, tv_usec: ival2_tv_usec as i64 }, + ival1: timeval { tv_sec: ival1_tv_sec as TimevalNum, tv_usec: ival1_tv_usec as TimevalNum}, + ival2: timeval { tv_sec: ival2_tv_sec as TimevalNum, tv_usec: ival2_tv_usec as TimevalNum}, } } @@ -179,8 +179,8 @@ pub fn create_bcm_head(count: u32, ival1_tv_sec: u64, ival1_tv_usec: u64 opcode: OPCODE::TxSetup as u32, flags: BCMFlags::SetTimer as u32 | BCMFlags::StartTimer as u32 | canfd_flag, count: count, - ival1: timeval { tv_sec: ival1_tv_sec as i64, tv_usec: ival1_tv_usec as i64 }, - ival2: timeval { tv_sec: ival2_tv_sec as i64, tv_usec: ival2_tv_usec as i64 }, + ival1: timeval { tv_sec: ival1_tv_sec as TimevalNum, tv_usec: ival1_tv_usec as TimevalNum }, + ival2: timeval { tv_sec: ival2_tv_sec as TimevalNum, tv_usec: ival2_tv_usec as TimevalNum }, can_id: can_id, nframes: frames.len() as u32, }; From d8b5f4c33a7033cb9a9ae54451b891473d7930cd Mon Sep 17 00:00:00 2001 From: Martin Heyden Date: Tue, 20 Aug 2024 12:43:26 -0400 Subject: [PATCH 13/18] fixed smallvec library dependencies issue by upgrading autosar-data library --- Cargo.lock | 18 ++++++++++-------- Cargo.toml | 2 +- .../restbus-simulation/src/arxml_parser.rs | 3 +-- .../restbus-simulation/src/arxml_utils.rs | 4 ++-- 4 files changed, 14 insertions(+), 13 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 2a739d9c..ea80c717 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -283,22 +283,24 @@ checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" [[package]] name = "autosar-data" -version = "0.9.1" +version = "0.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "220cf40add8815bc13a6c409a2ad62304ec0354e149bbcfe3c3516a1cf3f1726" +checksum = "966f7a3f9eed49783f5ace2fd90aa81dc8328af2a7f6b14e1b93bbcddc16f48f" dependencies = [ "autosar-data-specification", + "fxhash", + "indexmap 2.3.0", + "num-traits", "parking_lot 0.12.3", - "rustc-hash", "smallvec", "thiserror", ] [[package]] name = "autosar-data-specification" -version = "0.9.1" +version = "0.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4b10b95c5e9c2cf046261b1d71e4ef6d063569378ac6a95687a9a88fb053852d" +checksum = "29aab7eb036d0fdaa696ea39ba87c68c913567e90ef86b58f7acc94fb1966d60" dependencies = [ "num-derive", "num-traits", @@ -3482,7 +3484,7 @@ checksum = "ed3955f1a9c7c0c15e092f9c887db08b1fc683305fdf6eb6684f22555355e202" dependencies = [ "proc-macro2", "quote", - "syn 2.0.66", + "syn 2.0.72", ] [[package]] @@ -3917,7 +3919,7 @@ name = "opendut-restbus-simulation" version = "0.1.0" dependencies = [ "autosar-data", - "nix 0.27.1", + "nix 0.29.0", "serde", "serde_json", "tokio", @@ -5795,7 +5797,7 @@ dependencies = [ [[package]] name = "smallvec" -version = "1.11.2" +version = "1.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" dependencies = [ diff --git a/Cargo.toml b/Cargo.toml index 443095d3..acab7062 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -50,7 +50,7 @@ opendut-vpn = { path = "opendut-vpn" } anyhow = "1.0.79" assert_fs = "1.1.1" async-trait = "0.1.77" -autosar-data = "0.9.0" +autosar-data = "0.14.0" axum = "0.6.20" axum-server = "0.5.1" axum-server-dual-protocol = "0.5.2" diff --git a/opendut-edgar/restbus-simulation/src/arxml_parser.rs b/opendut-edgar/restbus-simulation/src/arxml_parser.rs index ba3f4753..8eb9ff87 100755 --- a/opendut-edgar/restbus-simulation/src/arxml_parser.rs +++ b/opendut-edgar/restbus-simulation/src/arxml_parser.rs @@ -476,8 +476,7 @@ impl ArxmlParser { // Iterate over Autosar elements and handle CanCluster elements for element in model .identifiable_elements() - .iter() - .filter_map(|path| model.get_element_by_path(&path)) + .filter_map(|(_path, weak)| weak.upgrade()) { match element.element_name() { ElementName::CanCluster => { diff --git a/opendut-edgar/restbus-simulation/src/arxml_utils.rs b/opendut-edgar/restbus-simulation/src/arxml_utils.rs index d6a096ac..13404db9 100755 --- a/opendut-edgar/restbus-simulation/src/arxml_utils.rs +++ b/opendut-edgar/restbus-simulation/src/arxml_utils.rs @@ -57,13 +57,13 @@ pub fn get_time_range(base: &Element) -> Option { let value = base .get_sub_element(ElementName::Value) .and_then(|elem| elem.character_data()) - .and_then(|cdata| cdata.double_value())?; + .and_then(|cdata| cdata.float_value())?; let tolerance = if let Some(absolute_tolerance) = base .get_sub_element(ElementName::AbsoluteTolerance) .and_then(|elem| elem.get_sub_element(ElementName::Absolute)) .and_then(|elem| elem.character_data()) - .and_then(|cdata| cdata.double_value()) + .and_then(|cdata| cdata.float_value()) { Some(TimeRangeTolerance::Absolute(absolute_tolerance)) } else { From 433cfb84c309cb1228a60782975d113f7c02d3fb Mon Sep 17 00:00:00 2001 From: Martin Heyden <33530907+mrh1234@users.noreply.github.com> Date: Tue, 20 Aug 2024 18:53:37 +0200 Subject: [PATCH 14/18] Update index.md --- doc/src/architecture/restbus-simulation/index.md | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/doc/src/architecture/restbus-simulation/index.md b/doc/src/architecture/restbus-simulation/index.md index 515bf378..3b992450 100644 --- a/doc/src/architecture/restbus-simulation/index.md +++ b/doc/src/architecture/restbus-simulation/index.md @@ -13,7 +13,7 @@ to simulate a base environment that improves testing. Current implementation state: - AXP -> Done - - RSIM -> Base implementation done and tested successfully. Have to extend it + - RSIM -> Base implementation done and can be used already. Have to extend it to handle all types of PDUs and do some more modifications. - RSIM API -> Todo @@ -76,9 +76,7 @@ Right now no API exists, and the RSIM just plays Frames with initial values to t - Either the user logic is integrated via separate binaries/Python files/..., or we might be able to use (compiled) CAPL files, that already contain the logic for existing restbus simulations ### Configuration -Currently, the implementation is somewhat independent of OpenDuT. In the future, everything will be integrated -including the configuration. You can see in [main.rs](https://github.com/eclipse-opendut/opendut/blob/restbus-simulation/opendut-edgar/restbus-simulation/src/main.rs) -how to establish a restbus-simulation without configuration. +The restbus-simulation can be enabled and configured via the [edgar.toml](https://github.com/eclipse-opendut/opendut/blob/restbus-simulation/opendut-edgar/edgar.toml) file. It is disabled by default. When enabling it, then it runs as long as the main edgar is running. It automatically gets stopped when the main edgar is stopped. ### References: - [autosar-data crate](https://crates.io/crates/autosar-data/0.9.0) From 14fdab19e66706e798e403e2237966cd9a29eaa1 Mon Sep 17 00:00:00 2001 From: Martin Heyden Date: Wed, 21 Aug 2024 04:34:26 -0400 Subject: [PATCH 15/18] improved code to remove warnings occuring at cargo ci check --- .../restbus-simulation/src/arxml_parser.rs | 134 ++++++++---------- .../restbus-simulation/src/arxml_structs.rs | 16 +-- .../restbus-simulation/src/arxml_utils.rs | 88 ++++++------ opendut-edgar/restbus-simulation/src/main.rs | 12 +- .../src/restbus_simulation.rs | 16 ++- .../restbus-simulation/src/restbus_structs.rs | 2 +- .../restbus-simulation/src/restbus_utils.rs | 75 +++++----- 7 files changed, 164 insertions(+), 179 deletions(-) diff --git a/opendut-edgar/restbus-simulation/src/arxml_parser.rs b/opendut-edgar/restbus-simulation/src/arxml_parser.rs index 8eb9ff87..f9e85dd0 100755 --- a/opendut-edgar/restbus-simulation/src/arxml_parser.rs +++ b/opendut-edgar/restbus-simulation/src/arxml_parser.rs @@ -36,14 +36,14 @@ impl ArxmlParser { .get_sub_element(ElementName::ISignalRef) .and_then(|elem| elem.get_reference_target().ok()) { - let refpath = get_required_string(&mapping, + let refpath = get_required_string(mapping, ElementName::ISignalRef); let name = get_required_item_name(&signal, "ISignalRef"); - let byte_order = get_required_string(&mapping, ElementName::PackingByteOrder); + let byte_order = get_required_string(mapping, ElementName::PackingByteOrder); - let start_pos = get_required_int_value(&mapping, + let start_pos = get_required_int_value(mapping, ElementName::StartPosition); let length = get_required_int_value(&signal, @@ -86,14 +86,14 @@ impl ArxmlParser { } let remaining_signals: Vec<(String, String, u64, u64, InitValues)> = signals.values().cloned().collect(); - if remaining_signals.len() > 0 { + if !remaining_signals.is_empty() { for (name, byte_order, start_pos, length, init_values) in remaining_signals { let isignal_struct: ISignal = ISignal { - name: name, + name, byte_order: get_byte_order(&byte_order), - start_pos: start_pos, - length: length, - init_values: init_values + start_pos, + length, + init_values }; ungrouped_signals.push(isignal_struct); } @@ -108,7 +108,7 @@ impl ArxmlParser { 1. Parses an Autosar ISignalIPdu element. 2. Returns important data in a self-defined ISignalIPDU structure. */ - fn handle_isignal_ipdu(&self, pdu: &Element) -> Option { + fn handle_isignal_ipdu(&self, pdu: &Element) -> Option { // Find out these values: ... let mut cyclic_timing_period_value: f64 = 0_f64; let mut cyclic_timing_period_tolerance: Option = None; @@ -143,7 +143,7 @@ impl ArxmlParser { } } - let unused_bit_pattern = get_unused_bit_pattern(&pdu); + let unused_bit_pattern = get_unused_bit_pattern(pdu); let mut grouped_signals: Vec = Vec::new(); @@ -151,28 +151,28 @@ impl ArxmlParser { self.handle_isignals(pdu, &mut grouped_signals, &mut ungrouped_signals); - let isginal_ipdu: ISignalIPDU = ISignalIPDU { - cyclic_timing_period_value: cyclic_timing_period_value, - cyclic_timing_period_tolerance: cyclic_timing_period_tolerance, - cyclic_timing_offset_value: cyclic_timing_offset_value, - cyclic_timing_offset_tolerance: cyclic_timing_offset_tolerance, - number_of_repetitions: number_of_repetitions, - repetition_period_value: repetition_period_value, - repetition_period_tolerance: repetition_period_tolerance, - unused_bit_pattern: unused_bit_pattern, - ungrouped_signals: ungrouped_signals, - grouped_signals: grouped_signals + let isginal_ipdu: ISignalIPdu = ISignalIPdu { + cyclic_timing_period_value, + cyclic_timing_period_tolerance, + cyclic_timing_offset_value, + cyclic_timing_offset_tolerance, + number_of_repetitions, + repetition_period_value, + repetition_period_tolerance, + unused_bit_pattern, + ungrouped_signals, + grouped_signals }; - return Some(isginal_ipdu); + Some(isginal_ipdu) } /* 1. Parses an Autosar NmPdu element 2. Returns important data in a self-defined NMPDU structure. */ - fn handle_nm_pdu(&self, pdu: &Element) -> Option { - let unused_bit_pattern = get_unused_bit_pattern(&pdu); + fn handle_nm_pdu(&self, pdu: &Element) -> Option { + let unused_bit_pattern = get_unused_bit_pattern(pdu); let mut grouped_signals: Vec = Vec::new(); @@ -180,13 +180,13 @@ impl ArxmlParser { self.handle_isignals(pdu, &mut grouped_signals, &mut ungrouped_signals); - let nm_pdu: NMPDU = NMPDU { - unused_bit_pattern: unused_bit_pattern, - ungrouped_signals: ungrouped_signals, - grouped_signals: grouped_signals + let nm_pdu: NmPdu = NmPdu { + unused_bit_pattern, + ungrouped_signals, + grouped_signals }; - return Some(nm_pdu); + Some(nm_pdu) } /* @@ -194,7 +194,7 @@ impl ArxmlParser { 2. Parses the Autosar PDU element 3. Returns important data in a self-defined PDU mapping structure. */ - fn handle_pdu_mapping(&self, pdu_mapping: &Element) -> Result { + fn handle_pdu_mapping(&self, pdu_mapping: &Element) -> Result { let pdu = get_required_reference( pdu_mapping, ElementName::PduRef); @@ -222,19 +222,19 @@ impl ArxmlParser { ElementName::ContainedIPduProps, ElementName::HeaderIdLongHeader); //let mut pdu_specific: PDU = PDU::Temp(0); - let pdu_specific: PDU; + let pdu_specific: Pdu; match pdu.element_name() { ElementName::ISignalIPdu => { if let Some(value) = self.handle_isignal_ipdu(&pdu) { - pdu_specific = PDU::ISignalIPDU(value); + pdu_specific = Pdu::ISignalIPdu(value); } else { panic!("Error in handle_isignal_ipdu"); } } ElementName::NmPdu => { if let Some(value) = self.handle_nm_pdu(&pdu) { - pdu_specific = PDU::NMPDU(value); + pdu_specific = Pdu::NmPdu(value); } else { panic!("Error in handle_nm_pdu"); } @@ -248,12 +248,12 @@ impl ArxmlParser { }*/ // Handle more? _ => { - let error = format!("PDU type {} not supported. Will skip it.", pdu.element_name().to_string()); + let error = format!("PDU type {} not supported. Will skip it.", pdu.element_name()); return Err(error) } } - let pdu_mapping: PDUMapping = PDUMapping { + let pdu_mapping: PduMapping = PduMapping { name: pdu_name, byte_order: get_byte_order(&byte_order), // start_position: start_position, @@ -265,7 +265,7 @@ impl ArxmlParser { pdu: pdu_specific }; - return Ok(pdu_mapping); + Ok(pdu_mapping) } /* @@ -277,7 +277,7 @@ impl ArxmlParser { can_frame_triggering, "CanFrameTriggering"); let can_id = get_required_int_value( - &can_frame_triggering, + can_frame_triggering, ElementName::Identifier); let frame = get_required_reference( @@ -297,7 +297,7 @@ impl ArxmlParser { }; let mut addressing_mode: bool = false; - if addressing_mode_str.to_uppercase() == String::from("EXTENDED") { + if addressing_mode_str.to_uppercase() == *"EXTENDED" { addressing_mode = true; } @@ -306,9 +306,8 @@ impl ArxmlParser { let frame_rx_behavior_str = get_optional_string( can_frame_triggering, ElementName::CanFrameRxBehavior); - if frame_rx_behavior_str.to_uppercase() == String::from("CAN-FD") { - frame_rx_behavior = true; - } else if frame_rx_behavior_str == "" && has_fd_baudrate { + if frame_rx_behavior_str.to_uppercase() == *"CAN-FD" + || frame_rx_behavior_str.is_empty() && has_fd_baudrate { frame_rx_behavior = true; } @@ -317,9 +316,8 @@ impl ArxmlParser { let frame_tx_behavior_str = get_optional_string( can_frame_triggering, ElementName::CanFrameTxBehavior); - if frame_tx_behavior_str.to_uppercase() == String::from("CAN-FD") { - frame_tx_behavior = true; - } else if frame_tx_behavior_str == "" && has_fd_baudrate { + if frame_tx_behavior_str.to_uppercase() == *"CAN-FD" + || frame_tx_behavior_str.is_empty() && has_fd_baudrate { frame_tx_behavior = true; } @@ -333,16 +331,13 @@ impl ArxmlParser { let mut rx_ecus: Vec = Vec::new(); let mut tx_ecus: Vec = Vec::new(); - match process_frame_ports(can_frame_triggering, &can_frame_triggering_name, &mut rx_ecus, &mut tx_ecus) { - Err(err) => return Err(err), - _ => {} - } + process_frame_ports(can_frame_triggering, &can_frame_triggering_name, &mut rx_ecus, &mut tx_ecus)?; let frame_length = get_optional_int_value( &frame, ElementName::FrameLength); - let mut pdu_mappings_vec: Vec = Vec::new(); + let mut pdu_mappings_vec: Vec = Vec::new(); // assign here and other similar variable? if let Some(mappings) = frame.get_sub_element(ElementName::PduToFrameMappings) { @@ -356,20 +351,20 @@ impl ArxmlParser { let can_frame_triggering_struct: CanFrameTriggering = CanFrameTriggering { frame_triggering_name: can_frame_triggering_name, - frame_name: frame_name, - can_id: can_id, - addressing_mode: addressing_mode, - frame_rx_behavior: frame_rx_behavior, - frame_tx_behavior: frame_tx_behavior, - rx_range_lower: rx_range_lower, - rx_range_upper: rx_range_upper, + frame_name, + can_id, + addressing_mode, + frame_rx_behavior, + frame_tx_behavior, + rx_range_lower, + rx_range_upper, receiver_ecus: rx_ecus, sender_ecus: tx_ecus, - frame_length: frame_length, + frame_length, pdu_mappings: pdu_mappings_vec }; - return Ok(can_frame_triggering_struct); + Ok(can_frame_triggering_struct) } /* @@ -420,7 +415,7 @@ impl ArxmlParser { for can_frame_triggering in frame_triggerings.sub_elements() { match self.handle_can_frame_triggering(&can_frame_triggering, has_fd_baudrate) { Ok(value) => { - can_frame_triggerings.insert(value.can_id.clone(), value); + can_frame_triggerings.insert(value.can_id, value); } Err(error) => error!("WARNING: {}", error), } @@ -432,10 +427,10 @@ impl ArxmlParser { name: can_cluster_name, baudrate: can_cluster_baudrate, canfd_baudrate: can_cluster_fd_baudrate, - can_frame_triggerings: can_frame_triggerings + can_frame_triggerings }; - return Ok(can_cluster_struct); + Ok(can_cluster_struct) } /* @@ -478,17 +473,14 @@ impl ArxmlParser { .identifiable_elements() .filter_map(|(_path, weak)| weak.upgrade()) { - match element.element_name() { - ElementName::CanCluster => { - let result: Result = self.handle_can_cluster(&element); - match result { - Ok(value) => { - can_clusters.insert(value.name.clone(), value); - } - Err(error) => warn!("WARNING: {}", error) + if element.element_name() == ElementName::CanCluster { + let result: Result = self.handle_can_cluster(&element); + match result { + Ok(value) => { + can_clusters.insert(value.name.clone(), value); } + Err(error) => warn!("WARNING: {}", error) } - _ => {} } } @@ -502,6 +494,6 @@ impl ArxmlParser { } } - return Some(can_clusters); + Some(can_clusters) } } diff --git a/opendut-edgar/restbus-simulation/src/arxml_structs.rs b/opendut-edgar/restbus-simulation/src/arxml_structs.rs index e5b14911..6c47f5b4 100755 --- a/opendut-edgar/restbus-simulation/src/arxml_structs.rs +++ b/opendut-edgar/restbus-simulation/src/arxml_structs.rs @@ -36,7 +36,7 @@ pub struct CanFrameTriggering { pub sender_ecus: Vec, pub receiver_ecus: Vec, pub frame_length: u64, - pub pdu_mappings: Vec + pub pdu_mappings: Vec } /* @@ -44,7 +44,7 @@ pub struct CanFrameTriggering { */ #[derive(Debug)] #[derive(Serialize, Deserialize)] -pub struct PDUMapping { +pub struct PduMapping { pub name: String, pub byte_order: bool, // pub start_position: u64, @@ -53,7 +53,7 @@ pub struct PDUMapping { pub category: String, pub contained_header_id_short: String, pub contained_header_id_long: String, - pub pdu: PDU + pub pdu: Pdu } /* @@ -61,9 +61,9 @@ pub struct PDUMapping { */ #[derive(Debug)] #[derive(Serialize, Deserialize)] -pub enum PDU { - ISignalIPDU(ISignalIPDU), - NMPDU(NMPDU), +pub enum Pdu { + ISignalIPdu(ISignalIPdu), + NmPdu(NmPdu), // DCMIPDU(DCMIPDU), // NMPDU(NMPDU), // ContaineredPDU(XY), @@ -76,7 +76,7 @@ pub enum PDU { */ #[derive(Debug)] #[derive(Serialize, Deserialize)] -pub struct ISignalIPDU { +pub struct ISignalIPdu { pub cyclic_timing_period_value: f64, pub cyclic_timing_period_tolerance: Option, pub cyclic_timing_offset_value: f64, @@ -94,7 +94,7 @@ pub struct ISignalIPDU { */ #[derive(Debug)] #[derive(Serialize, Deserialize)] -pub struct NMPDU { +pub struct NmPdu { pub unused_bit_pattern: bool, pub ungrouped_signals: Vec, pub grouped_signals: Vec, diff --git a/opendut-edgar/restbus-simulation/src/arxml_utils.rs b/opendut-edgar/restbus-simulation/src/arxml_utils.rs index 13404db9..c590e462 100755 --- a/opendut-edgar/restbus-simulation/src/arxml_utils.rs +++ b/opendut-edgar/restbus-simulation/src/arxml_utils.rs @@ -10,10 +10,12 @@ use std::collections::HashMap; use std::fs::File; use std::io::prelude::*; use std::io::Error; - +use std::vec; use autosar_data::{CharacterData, Element, ElementName, EnumItem}; +use nix::libc::timeval; + /* Converts a CharacterData type from the autosar_data library @@ -95,7 +97,7 @@ pub fn get_sub_element_and_time_range(base: &Element, sub_elem_name: ElementName */ pub fn get_required_item_name(element: &Element, element_name: &str) -> String { if let Some(item_name) = element.item_name() { - return item_name; + item_name } else { panic!("Error getting required item name of {}", element_name); } @@ -109,7 +111,7 @@ pub fn get_required_sub_subelement(element: &Element, subelement_name: ElementNa .get_sub_element(subelement_name) .and_then(|elem| elem.get_sub_element(sub_subelement_name)) { - return sub_subelement; + sub_subelement } else { panic!("Error getting sub_subelement. Tried to retrieve {} and then {}", subelement_name, @@ -121,10 +123,10 @@ pub fn get_required_sub_subelement(element: &Element, subelement_name: ElementNa Tries to get a subelement and convert it's value to u64. */ pub fn get_subelement_int_value(element: &Element, subelement_name: ElementName) -> Option { - return element + element .get_sub_element(subelement_name) .and_then(|elem| elem.character_data()) - .and_then(|cdata| decode_integer(&cdata)); + .and_then(|cdata| decode_integer(&cdata)) } /* @@ -132,7 +134,7 @@ pub fn get_subelement_int_value(element: &Element, subelement_name: ElementName) */ pub fn get_required_int_value(element: &Element, subelement_name: ElementName) -> u64 { if let Some(int_value) = get_subelement_int_value(element, subelement_name) { - return int_value; + int_value } else { panic!("Error getting required integer value of {}", subelement_name); } @@ -142,11 +144,7 @@ pub fn get_required_int_value(element: &Element, subelement_name: ElementName) - Gets the u64 value for a element. This is optional. So, if the subelement does not exist, then 0 is returned. */ pub fn get_optional_int_value(element: &Element, subelement_name: ElementName) -> u64 { - if let Some(int_value) = get_subelement_int_value(element, subelement_name) { - return int_value; - } else { - return 0; - } + get_subelement_int_value(element, subelement_name).unwrap_or_default() } /* @@ -183,10 +181,10 @@ pub fn get_required_reference(element: &Element, subelement_name: ElementName) - Tries to get a subelement and return it's String value. */ pub fn get_subelement_string_value(element: &Element, subelement_name: ElementName) -> Option { - return element + element .get_sub_element(subelement_name) .and_then(|elem| elem.character_data()) - .map(|cdata| cdata.to_string()); + .map(|cdata| cdata.to_string()) } /* @@ -194,7 +192,7 @@ pub fn get_subelement_string_value(element: &Element, subelement_name: ElementNa */ pub fn get_required_string(element: &Element, subelement_name: ElementName) -> String { if let Some(value) = get_subelement_string_value(element, subelement_name) { - return value; + value } else { panic!("Error getting required String value of {}", subelement_name); } @@ -204,26 +202,17 @@ pub fn get_required_string(element: &Element, subelement_name: ElementName) -> S Gets the String value for a element. This is optional. So, if the subelement does not exist, then "" is returned. */ pub fn get_optional_string(element: &Element, subelement_name: ElementName) -> String { - if let Some(value) = get_subelement_string_value(element, subelement_name) { - return value; - } else { - return String::from(""); - } + get_subelement_string_value(element, subelement_name).unwrap_or_default() } /* Gets the String value of a subsubelement. In case the subelement or subsubelement do not exist, then "" is returned. */ pub fn get_subelement_optional_string(element: &Element, subelement_name: ElementName, sub_subelement_name: ElementName) -> String { - if let Some(value) = element.get_sub_element(subelement_name) + element.get_sub_element(subelement_name) .and_then(|elem| elem.get_sub_element(sub_subelement_name)) .and_then(|elem| elem.character_data()) - .map(|cdata| cdata.to_string()) - { - return value; - } else { - return String::from(""); - } + .map(|cdata| cdata.to_string()).unwrap_or_default() } /* @@ -245,10 +234,10 @@ pub fn get_byte_order(byte_order: &String) -> bool { if byte_order.eq("MOST-SIGNIFICANT-BYTE-LAST") { return false; } - return true; + true } -fn process_isignal_init_value(isignal: &ISignal, bits: &mut Vec) { +fn process_isignal_init_value(isignal: &ISignal, bits: &mut [bool]) { let mut tmp_bit_array: Vec = Vec::new(); let init_values = &isignal.init_values; let isignal_byte_order = isignal.byte_order; @@ -257,7 +246,7 @@ fn process_isignal_init_value(isignal: &ISignal, bits: &mut Vec) { match init_values { InitValues::Single(value) => { - let mut n = value.clone(); + let mut n = *value; while n != 0 { tmp_bit_array.push(n & 1 != 0); @@ -279,7 +268,7 @@ fn process_isignal_init_value(isignal: &ISignal, bits: &mut Vec) { for isignal_value in values { let byte_len: usize = 8; - let mut n = isignal_value.clone(); + let mut n = *isignal_value; let mut tmp_tmp_bit_array: Vec = Vec::new(); while n != 0 { @@ -368,7 +357,7 @@ pub fn extract_init_values(unused_bit_pattern: bool, ungrouped_signals: &Vec bool { // even though it needs to exist at least for ISignalIPdus, we keep it as optional, since at least one encounter shows that it might be missing. // then use 0 as default value - let mut unused_bit_pattern_int = get_optional_int_value(&pdu, ElementName::UnusedBitPattern); + let mut unused_bit_pattern_int = get_optional_int_value(pdu, ElementName::UnusedBitPattern); let unused_bit_pattern: bool; // supports values > 1. Just look at least significant bit - unused_bit_pattern_int = unused_bit_pattern_int & 1; + unused_bit_pattern_int &= 1; if unused_bit_pattern_int == 0 { unused_bit_pattern = false; @@ -392,7 +381,7 @@ pub fn get_unused_bit_pattern(pdu: &Element) -> bool { panic!("Error reading unused_bit_pattern. Value is {}", unused_bit_pattern_int); } - return unused_bit_pattern; + unused_bit_pattern } /* @@ -466,7 +455,7 @@ pub fn process_init_value(init_value_elem: &mut Element, init_values: &mut InitV } else { let mut init_value_array: Vec = Vec::new(); - let num_val_elements = get_required_sub_subelement(&init_value_elem, + let num_val_elements = get_required_sub_subelement(init_value_elem, ElementName::ArrayValueSpecification, ElementName::Elements); @@ -487,7 +476,7 @@ pub fn process_signal_group(signal_group: &Element, signals: &mut HashMap, grouped_signals: &mut Vec) -> Option<()> { - let group_name = get_required_item_name(&signal_group, "ISignalGroupRef"); + let group_name = get_required_item_name(signal_group, "ISignalGroupRef"); let mut signal_group_signals: Vec = Vec::new(); @@ -560,9 +549,9 @@ pub fn process_signal_group(signal_group: &Element, let props_struct: E2EDataTransformationProps = E2EDataTransformationProps { - transformer_name: transformer_name, - data_id: data_id, - data_length: data_length + transformer_name, + data_id, + data_length }; props_vector.push(props_struct); @@ -573,7 +562,7 @@ pub fn process_signal_group(signal_group: &Element, let isignal_group_struct: ISignalGroup = ISignalGroup { name: group_name, isignals: signal_group_signals, - data_transformations: data_transformations, + data_transformations, transformation_props: props_vector }; @@ -600,7 +589,7 @@ pub fn get_timed_can_frame(can_frame_triggering: &CanFrameTriggering, timed_can_ let mut ival2_tv_usec: u64 = 0; let init_values: Vec; match &pdu_mapping.pdu { - PDU::ISignalIPDU(pdu) => { + Pdu::ISignalIPdu(pdu) => { count = pdu.number_of_repetitions as u32; if pdu.repetition_period_value != 0.0 { @@ -621,7 +610,7 @@ pub fn get_timed_can_frame(can_frame_triggering: &CanFrameTriggering, timed_can_ pdu_mapping.length, &pdu_mapping.byte_order); } - PDU::NMPDU(pdu) => { + Pdu::NmPdu(pdu) => { ival2_tv_usec = 100000; // every 100 ms init_values = extract_init_values(pdu.unused_bit_pattern, &pdu.ungrouped_signals, @@ -630,8 +619,13 @@ pub fn get_timed_can_frame(can_frame_triggering: &CanFrameTriggering, timed_can_ &pdu_mapping.byte_order); } } - timed_can_frames.push(create_time_can_frame_structure(count, ival1_tv_sec, ival1_tv_usec, ival2_tv_sec, - ival2_tv_usec, can_id, len, addressing_mode, frame_tx_behavior, &init_values)); + + let ival1 = timeval { tv_sec: ival1_tv_sec as TimevalNum, tv_usec: ival1_tv_usec as TimevalNum}; + let ival2 = timeval { tv_sec: ival2_tv_sec as TimevalNum, tv_usec: ival2_tv_usec as TimevalNum}; + + let ivals: Vec = vec![ival1, ival2]; + + timed_can_frames.push(create_time_can_frame_structure(count, &ivals, can_id, len, addressing_mode, frame_tx_behavior, &init_values)); } } @@ -648,7 +642,7 @@ pub fn get_timed_can_frame_from_id(can_clusters: &HashMap, b } } - return timed_can_frames + timed_can_frames } /* @@ -664,7 +658,7 @@ pub fn get_timed_can_frames_from_bus(can_clusters: &HashMap, } } - return timed_can_frames + timed_can_frames } pub fn load_serialized_data(file_name: &String) -> Result, Error> { @@ -674,7 +668,7 @@ pub fn load_serialized_data(file_name: &String) -> Result = serde_json::from_str(&contents)?; - return Ok(deserialized); + Ok(deserialized) } pub fn store_serialized_data(file_name: &String, can_clusters: &HashMap) -> Result<(), Error> { diff --git a/opendut-edgar/restbus-simulation/src/main.rs b/opendut-edgar/restbus-simulation/src/main.rs index 732e79be..2c09e010 100755 --- a/opendut-edgar/restbus-simulation/src/main.rs +++ b/opendut-edgar/restbus-simulation/src/main.rs @@ -29,7 +29,7 @@ fn get_pdu_hex(can_id: &u64, init_values: &Vec) -> String { } println!("Values: {}", hex_string); - return hex_string; + hex_string } @@ -42,7 +42,7 @@ fn collect_pdus(can_clusters: &HashMap, bus_name: String) -> for can_frame_triggering in can_cluster.can_frame_triggerings.values() { for pdu_mapping in &can_frame_triggering.pdu_mappings { match &pdu_mapping.pdu { - PDU::ISignalIPDU(pdu) => { + Pdu::ISignalIPdu(pdu) => { let init_values = extract_init_values(pdu.unused_bit_pattern, &pdu.ungrouped_signals, &pdu.grouped_signals, @@ -50,7 +50,7 @@ fn collect_pdus(can_clusters: &HashMap, bus_name: String) -> &pdu_mapping.byte_order); init_values_strings.push(get_pdu_hex(&can_frame_triggering.can_id, &init_values)) } - PDU::NMPDU(pdu) => { + Pdu::NmPdu(pdu) => { let init_values = extract_init_values(pdu.unused_bit_pattern, &pdu.ungrouped_signals, &pdu.grouped_signals, @@ -62,7 +62,7 @@ fn collect_pdus(can_clusters: &HashMap, bus_name: String) -> } } } - return init_values_strings; + init_values_strings } @@ -123,12 +123,12 @@ fn main() -> std::io::Result<()> { let mut frames = String::new(); for frame in collect_pdus(&can_clusters, String::from(bus_name)) { frames.push_str(frame.as_str()); - frames.push_str("\n"); + frames.push('\n'); } // Debug. Found PDUs of target bus are written to file. let mut f = File::create(target_file)?; - f.write_all(&frames.as_bytes())?; + f.write_all(frames.as_bytes())?; println!("Trying to setup up restbus simulation"); diff --git a/opendut-edgar/restbus-simulation/src/restbus_simulation.rs b/opendut-edgar/restbus-simulation/src/restbus_simulation.rs index 83b2624f..880cdb58 100755 --- a/opendut-edgar/restbus-simulation/src/restbus_simulation.rs +++ b/opendut-edgar/restbus-simulation/src/restbus_simulation.rs @@ -7,6 +7,8 @@ use crate::restbus_utils::*; use crate::restbus_structs::*; +use nix::libc::timeval; + pub struct RestbusSimulation { } @@ -27,13 +29,13 @@ impl RestbusSimulation { for timed_can_frame in timed_can_frames { let mut write_bytes: Vec = Vec::new(); - let mut can_frames: Vec = Vec::new(); - - can_frames.push( - create_can_frame_structure(timed_can_frame.can_id, timed_can_frame.len, timed_can_frame.addressing_mode, timed_can_frame.frame_tx_behavior, &timed_can_frame.data_vector)); + let can_frames: Vec = vec![ + create_can_frame_structure(timed_can_frame.can_id, timed_can_frame.len, timed_can_frame.addressing_mode, timed_can_frame.frame_tx_behavior, &timed_can_frame.data_vector)]; + + let ival1 = timeval { tv_sec: timed_can_frame.ival1.tv_sec as TimevalNum, tv_usec: timed_can_frame.ival1.tv_usec as TimevalNum }; + let ival2 = timeval { tv_sec: timed_can_frame.ival2.tv_sec as TimevalNum, tv_usec: timed_can_frame.ival2.tv_usec as TimevalNum }; - create_bcm_structure_bytes(timed_can_frame.count, timed_can_frame.ival1.tv_sec as u64, timed_can_frame.ival1.tv_usec as u64, - timed_can_frame.ival2.tv_sec as u64, timed_can_frame.ival2.tv_usec as u64, timed_can_frame.can_id, timed_can_frame.frame_tx_behavior, &can_frames, &mut write_bytes); + create_bcm_structure_bytes(timed_can_frame.count, ival1, ival2, timed_can_frame.can_id, timed_can_frame.frame_tx_behavior, &can_frames, &mut write_bytes); write_bytes_global.push(write_bytes); @@ -50,7 +52,7 @@ impl RestbusSimulation { //println!("successfully wrote to socket"); } - return Ok(true); + Ok(true) } /*pub fn play_single_bcm_frame(&self, ifname: &String, count: u32, ival1_tv_sec: i64, ival1_tv_usec: i64, diff --git a/opendut-edgar/restbus-simulation/src/restbus_structs.rs b/opendut-edgar/restbus-simulation/src/restbus_structs.rs index 421e2510..fe6dab7d 100755 --- a/opendut-edgar/restbus-simulation/src/restbus_structs.rs +++ b/opendut-edgar/restbus-simulation/src/restbus_structs.rs @@ -72,7 +72,7 @@ pub struct TimedCanFrame { /* Opcodes defining the operation that the BCM should do. Same as in https://github.com/linux-can/can-utils/blob/master/include/linux/can/bcm.h. */ -pub enum OPCODE { +pub enum Opcode { TxSetup = 1, /* TxDelete, TxRead, diff --git a/opendut-edgar/restbus-simulation/src/restbus_utils.rs b/opendut-edgar/restbus-simulation/src/restbus_utils.rs index 2920d997..b7ce4cc8 100755 --- a/opendut-edgar/restbus-simulation/src/restbus_utils.rs +++ b/opendut-edgar/restbus-simulation/src/restbus_utils.rs @@ -14,7 +14,7 @@ use nix::libc::{__c_anonymous_sockaddr_can_can_addr, __c_anonymous_sockaddr_can_ /* Convert Rust vector to C like pointer that is required when using C derived method */ -fn vec_to_c_void(vec: &Vec) -> *const c_void { +fn vec_to_c_void(vec: &[u8]) -> *const c_void { vec.as_ptr() as *const c_void } @@ -30,7 +30,7 @@ pub fn create_socket() -> Result { return Err(format!("Could not create socket due to {}", Error::last_os_error())); } - return Ok(sock); + Ok(sock) } /* @@ -59,7 +59,7 @@ pub fn connect_socket(sock: i32, ifname: &String) -> Result { let my_sockaddr: sockaddr_can = sockaddr_can { can_family: AF_CAN as u16, can_ifindex: ifindex as i32, - can_addr: can_addr + can_addr }; let sockaddr_can_ptr: *const sockaddr_can = &my_sockaddr as *const sockaddr_can; @@ -74,27 +74,27 @@ pub fn connect_socket(sock: i32, ifname: &String) -> Result { return Err(format!("Could not connect socket due to {}", Error::last_os_error())); } - return Ok(connect_res); + Ok(connect_res) } /* Writes to existing socket */ -pub fn write_socket(sock: i32, write_bytes: &Vec, count: usize) -> Result { +pub fn write_socket(sock: i32, write_bytes: &[u8], count: usize) -> Result { let wres = unsafe { - write(sock, vec_to_c_void(&write_bytes), count) + write(sock, vec_to_c_void(write_bytes), count) }; if wres < 0 { return Err(format!("Could not write to socket due to {}", Error::last_os_error())); } - return Ok(wres); + Ok(wres) } /* Fills u8 values from a vector into an array */ -fn fill_data_array(data: &mut [u8], data_vector: &Vec) { +fn fill_data_array(data: &mut [u8], data_vector: &[u8]) { let mut i = 0; while i < data_vector.len() { data[i] = data_vector[i]; @@ -105,7 +105,7 @@ fn fill_data_array(data: &mut [u8], data_vector: &Vec) { /* Creates a self-defined CanFrame structure that is either a CanFdFrame or a Can20Frame */ -pub fn create_can_frame_structure(can_id: u32, len: u8, addressing_mode: bool, frame_tx_behavior: bool, data_vector: &Vec) -> CanFrame { +pub fn create_can_frame_structure(can_id: u32, len: u8, addressing_mode: bool, frame_tx_behavior: bool, data_vector: &[u8]) -> CanFrame { let mut eflag: u32 = 0x0; if addressing_mode { @@ -117,50 +117,49 @@ pub fn create_can_frame_structure(can_id: u32, len: u8, addressing_mode: bool, f fill_data_array(&mut data, data_vector); - return CanFrame::CanFdFrame( CanFdFrame { + CanFrame::CanFdFrame( CanFdFrame { can_id: can_id | eflag, - len: len, + len, flags: 0, // are there any relevant flags? __res0: 0, __res1: 0, - data: data, - }); + data, + }) } else { let mut data: [u8; 8] = [0; 8]; fill_data_array(&mut data, data_vector); - return CanFrame::Can20Frame( Can20Frame { + CanFrame::Can20Frame( Can20Frame { can_id: can_id | eflag, - len: len, + len, __pad: 0, __res0: 0, __res1: 0, - data: data, - }); + data, + }) } } /* Creates a self-defined TimedCanFrame structure that holds the necessary data used for creating a CanFrame later */ -pub fn create_time_can_frame_structure(count: u32, ival1_tv_sec: u64, ival1_tv_usec: u64, ival2_tv_sec: u64, - ival2_tv_usec: u64, can_id: u32, len: u8, addressing_mode: bool, frame_tx_behavior: bool, data_vector: &Vec) -> TimedCanFrame { +pub fn create_time_can_frame_structure(count: u32, ivals: &[timeval], can_id: u32, len: u8, addressing_mode: bool, frame_tx_behavior: bool, data_vector: &Vec) -> TimedCanFrame { let mut copy_data_vector: Vec = Vec::new(); for data in data_vector { copy_data_vector.push(*data); } - return TimedCanFrame { - can_id: can_id, - len: len, - addressing_mode: addressing_mode, - frame_tx_behavior: frame_tx_behavior, + TimedCanFrame { + can_id, + len, + addressing_mode, + frame_tx_behavior, data_vector: copy_data_vector, - count: count, - ival1: timeval { tv_sec: ival1_tv_sec as TimevalNum, tv_usec: ival1_tv_usec as TimevalNum}, - ival2: timeval { tv_sec: ival2_tv_sec as TimevalNum, tv_usec: ival2_tv_usec as TimevalNum}, + count, + ival1: ivals[0], + ival2: ivals[1] } } @@ -168,31 +167,29 @@ pub fn create_time_can_frame_structure(count: u32, ival1_tv_sec: u64, ival1_tv_u Creates a BcmMsgHead structure, which is a header of messages send to/from the CAN Broadcast Manager See also https://github.com/linux-can/can-utils/blob/master/include/linux/can/bcm.h */ -pub fn create_bcm_head(count: u32, ival1_tv_sec: u64, ival1_tv_usec: u64 - , ival2_tv_sec: u64, ival2_tv_usec: u64, can_id: u32, frame_tx_behavior: bool, frames: &Vec) -> BcmMsgHead { +pub fn create_bcm_head(count: u32, ival1: timeval, ival2: timeval, can_id: u32, frame_tx_behavior: bool, frames: &[CanFrame]) -> BcmMsgHead { let mut canfd_flag: u32 = 0x0; if frame_tx_behavior { canfd_flag = BCMFlags::CanFdFrame as u32; } - return BcmMsgHead { - opcode: OPCODE::TxSetup as u32, + BcmMsgHead { + opcode: Opcode::TxSetup as u32, flags: BCMFlags::SetTimer as u32 | BCMFlags::StartTimer as u32 | canfd_flag, - count: count, - ival1: timeval { tv_sec: ival1_tv_sec as TimevalNum, tv_usec: ival1_tv_usec as TimevalNum }, - ival2: timeval { tv_sec: ival2_tv_sec as TimevalNum, tv_usec: ival2_tv_usec as TimevalNum }, - can_id: can_id, + count, + ival1, + ival2, + can_id, nframes: frames.len() as u32, - }; + } } /* Converts a BcmMsgHead structure and the payload (which are CanFrames) to a byte representation. The write_bytes vector is filled with the bytes and can be then later be used by the caller. */ -pub fn create_bcm_structure_bytes(count: u32, ival1_tv_sec: u64, ival1_tv_usec: u64 - , ival2_tv_sec: u64, ival2_tv_usec: u64, can_id: u32, frame_tx_behavior: bool, frames: &Vec, write_bytes: &mut Vec) { - let head: BcmMsgHead = create_bcm_head(count, ival1_tv_sec, ival1_tv_usec, ival2_tv_sec, ival2_tv_usec, can_id, frame_tx_behavior, frames); +pub fn create_bcm_structure_bytes(count: u32, ival1: timeval, ival2: timeval, can_id: u32, frame_tx_behavior: bool, frames: &Vec, write_bytes: &mut Vec) { + let head: BcmMsgHead = create_bcm_head(count, ival1, ival2, can_id, frame_tx_behavior, frames); let ptr: *const u8 = &head as *const BcmMsgHead as *const u8; let bytes: &[u8] = unsafe { slice::from_raw_parts(ptr, mem::size_of::()) }; From 3c5d7b8fc22ba1700b8202cefdf210726491057e Mon Sep 17 00:00:00 2001 From: Linus Hafkemeyer Date: Mon, 28 Oct 2024 14:18:04 +0100 Subject: [PATCH 16/18] EDGAR -> Fix double import --- Cargo.lock | 4 ++-- opendut-edgar/src/service/start.rs | 2 -- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index ea80c717..26a566ac 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -289,7 +289,7 @@ checksum = "966f7a3f9eed49783f5ace2fd90aa81dc8328af2a7f6b14e1b93bbcddc16f48f" dependencies = [ "autosar-data-specification", "fxhash", - "indexmap 2.3.0", + "indexmap 2.6.0", "num-traits", "parking_lot 0.12.3", "smallvec", @@ -3484,7 +3484,7 @@ checksum = "ed3955f1a9c7c0c15e092f9c887db08b1fc683305fdf6eb6684f22555355e202" dependencies = [ "proc-macro2", "quote", - "syn 2.0.72", + "syn 2.0.85", ] [[package]] diff --git a/opendut-edgar/src/service/start.rs b/opendut-edgar/src/service/start.rs index bf4d2d6e..2c092b0b 100644 --- a/opendut-edgar/src/service/start.rs +++ b/opendut-edgar/src/service/start.rs @@ -8,12 +8,10 @@ use anyhow::Context; use opendut_carl_api::proto::services::peer_messaging_broker; use opendut_carl_api::proto::services::peer_messaging_broker::downstream::Message; use opendut_carl_api::proto::services::peer_messaging_broker::{ApplyPeerConfiguration, TracingContext}; -use opendut_carl_api::proto::services::peer_messaging_broker::downstream::Message; use opendut_restbus_simulation::arxml_parser::ArxmlParser; use opendut_restbus_simulation::arxml_utils::get_timed_can_frames_from_bus; use opendut_restbus_simulation::restbus_simulation::RestbusSimulation; use opendut_restbus_simulation::restbus_structs::TimedCanFrame; -use opendut_types::cluster::{ClusterAssignment, PeerClusterAssignment}; use opendut_types::peer::configuration::{OldPeerConfiguration, PeerConfiguration}; use opendut_types::peer::PeerId; use opendut_util::settings::LoadedConfig; From 6aed695f641b432e627e1f0048805456aeda0823 Mon Sep 17 00:00:00 2001 From: Linus Hafkemeyer Date: Mon, 28 Oct 2024 16:40:23 +0100 Subject: [PATCH 17/18] EDGAR -> Minor cleanup of RBS code --- .../{src => samples}/system-4.2.arxml | 0 .../src/restbus_simulation.rs | 16 +------ .../restbus-simulation/src/restbus_utils.rs | 47 +++++++------------ 3 files changed, 18 insertions(+), 45 deletions(-) rename opendut-edgar/restbus-simulation/{src => samples}/system-4.2.arxml (100%) diff --git a/opendut-edgar/restbus-simulation/src/system-4.2.arxml b/opendut-edgar/restbus-simulation/samples/system-4.2.arxml similarity index 100% rename from opendut-edgar/restbus-simulation/src/system-4.2.arxml rename to opendut-edgar/restbus-simulation/samples/system-4.2.arxml diff --git a/opendut-edgar/restbus-simulation/src/restbus_simulation.rs b/opendut-edgar/restbus-simulation/src/restbus_simulation.rs index 880cdb58..ea6456fa 100755 --- a/opendut-edgar/restbus-simulation/src/restbus_simulation.rs +++ b/opendut-edgar/restbus-simulation/src/restbus_simulation.rs @@ -24,8 +24,6 @@ impl RestbusSimulation { connect_socket(sock, ifname)?; - let mut write_bytes_global: Vec> = Vec::new(); - for timed_can_frame in timed_can_frames { let mut write_bytes: Vec = Vec::new(); @@ -37,21 +35,9 @@ impl RestbusSimulation { create_bcm_structure_bytes(timed_can_frame.count, ival1, ival2, timed_can_frame.can_id, timed_can_frame.frame_tx_behavior, &can_frames, &mut write_bytes); - write_bytes_global.push(write_bytes); - + write_socket(sock, &write_bytes)?; } - for write_bytes in write_bytes_global { - /*println!("write byte is {}", write_bytes.len()); - for byte in &write_bytes { - print!("{:02x} ", byte); - } - println!("");*/ - - write_socket(sock, &write_bytes, write_bytes.len())?; - //println!("successfully wrote to socket"); - } - Ok(true) } diff --git a/opendut-edgar/restbus-simulation/src/restbus_utils.rs b/opendut-edgar/restbus-simulation/src/restbus_utils.rs index b7ce4cc8..e46fba80 100755 --- a/opendut-edgar/restbus-simulation/src/restbus_utils.rs +++ b/opendut-edgar/restbus-simulation/src/restbus_utils.rs @@ -9,14 +9,7 @@ use std::io::Error; use std::os::raw::c_void; use std::ffi::CString; -use nix::libc::{__c_anonymous_sockaddr_can_can_addr, __c_anonymous_sockaddr_can_tp, connect, if_nametoindex, sockaddr, sockaddr_can, socket, timeval, write, AF_CAN, CAN_BCM, CAN_EFF_FLAG, SOCK_DGRAM}; - -/* - Convert Rust vector to C like pointer that is required when using C derived method -*/ -fn vec_to_c_void(vec: &[u8]) -> *const c_void { - vec.as_ptr() as *const c_void -} +use nix::libc::{__c_anonymous_sockaddr_can_can_addr, __c_anonymous_sockaddr_can_tp, c_int, connect, if_nametoindex, sockaddr, sockaddr_can, socket, socklen_t, timeval, write, AF_CAN, CAN_BCM, CAN_EFF_FLAG, SOCK_DGRAM}; /* Create a socket using libc's socket function. This is required since not Rust equivalent library or method exists for establishing a BCM CAN socket @@ -47,27 +40,21 @@ pub fn connect_socket(sock: i32, ifname: &String) -> Result { } }; - let sock_addr_can_tp = __c_anonymous_sockaddr_can_tp { - rx_id: 0, - tx_id: 0 - }; - - let can_addr = __c_anonymous_sockaddr_can_can_addr { - tp: sock_addr_can_tp - }; - - let my_sockaddr: sockaddr_can = sockaddr_can { + let sockaddr: sockaddr_can = sockaddr_can { can_family: AF_CAN as u16, can_ifindex: ifindex as i32, - can_addr + can_addr: __c_anonymous_sockaddr_can_can_addr { + tp: __c_anonymous_sockaddr_can_tp { + rx_id: 0, + tx_id: 0 + } + } }; - let sockaddr_can_ptr: *const sockaddr_can = &my_sockaddr as *const sockaddr_can; - let sockaddr_ptr = sockaddr_can_ptr as *const sockaddr; + let sockaddr_ptr = &sockaddr as *const sockaddr_can as *const sockaddr; - //let conv_addr = SockaddrLike::from_raw(my_sockaddr, Some(mem::size_of::<&sockaddr_can>() as u32)); let connect_res = unsafe { - connect(sock, sockaddr_ptr, mem::size_of::<&sockaddr_can>() as u32) + connect(sock, sockaddr_ptr, mem::size_of::<&sockaddr_can>() as socklen_t) }; if connect_res < 0 { @@ -80,9 +67,13 @@ pub fn connect_socket(sock: i32, ifname: &String) -> Result { /* Writes to existing socket */ -pub fn write_socket(sock: i32, write_bytes: &[u8], count: usize) -> Result { +pub fn write_socket(sock: i32, buf: &[u8]) -> Result { let wres = unsafe { - write(sock, vec_to_c_void(write_bytes), count) + write( + sock as c_int, + buf.as_ptr() as *const c_void, + buf.len() + ) }; if wres < 0 { return Err(format!("Could not write to socket due to {}", Error::last_os_error())); @@ -106,11 +97,7 @@ fn fill_data_array(data: &mut [u8], data_vector: &[u8]) { Creates a self-defined CanFrame structure that is either a CanFdFrame or a Can20Frame */ pub fn create_can_frame_structure(can_id: u32, len: u8, addressing_mode: bool, frame_tx_behavior: bool, data_vector: &[u8]) -> CanFrame { - let mut eflag: u32 = 0x0; - - if addressing_mode { - eflag = CAN_EFF_FLAG; - } + let eflag: u32 = if addressing_mode { CAN_EFF_FLAG } else { 0 }; if frame_tx_behavior { let mut data: [u8; 64] = [0; 64]; From 39905ff4928941ab4536424216b43e2256c31e08 Mon Sep 17 00:00:00 2001 From: Linus Hafkemeyer Date: Thu, 31 Oct 2024 11:45:09 +0100 Subject: [PATCH 18/18] EDGAR -> RBS improvements --- Cargo.lock | 2 + opendut-edgar/restbus-simulation/Cargo.toml | 4 +- .../restbus-simulation/src/arxml_parser.rs | 174 ++++++------ .../restbus-simulation/src/arxml_structs.rs | 2 +- .../restbus-simulation/src/arxml_utils.rs | 252 ++++++++---------- opendut-edgar/restbus-simulation/src/main.rs | 10 +- .../src/restbus_simulation.rs | 4 +- opendut-edgar/src/service/start.rs | 17 +- 8 files changed, 217 insertions(+), 248 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 26a566ac..3d080e82 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3918,10 +3918,12 @@ dependencies = [ name = "opendut-restbus-simulation" version = "0.1.0" dependencies = [ + "anyhow", "autosar-data", "nix 0.29.0", "serde", "serde_json", + "thiserror", "tokio", "tracing", ] diff --git a/opendut-edgar/restbus-simulation/Cargo.toml b/opendut-edgar/restbus-simulation/Cargo.toml index 4b863ad2..809cbaed 100755 --- a/opendut-edgar/restbus-simulation/Cargo.toml +++ b/opendut-edgar/restbus-simulation/Cargo.toml @@ -8,12 +8,14 @@ license.workspace = true # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] +anyhow = { workspace = true } autosar-data = { workspace = true } nix = { workspace = true } tokio = { workspace = true } tracing = { workspace = true } -serde = { workspace = true } +serde = { workspace = true, features = ["derive"] } serde_json = { workspace = true } +thiserror = { workspace = true } [lints] workspace = true diff --git a/opendut-edgar/restbus-simulation/src/arxml_parser.rs b/opendut-edgar/restbus-simulation/src/arxml_parser.rs index f9e85dd0..db1f5900 100755 --- a/opendut-edgar/restbus-simulation/src/arxml_parser.rs +++ b/opendut-edgar/restbus-simulation/src/arxml_parser.rs @@ -12,11 +12,11 @@ use crate::arxml_utils::*; use std::time::Instant; use std::collections::HashMap; -use core::panic; +use anyhow::{anyhow, bail, Result}; use autosar_data::{AutosarModel, CharacterData, Element, ElementName, EnumItem}; -use tracing::{error, info, warn}; +use tracing::{error, info, warn, debug}; pub struct ArxmlParser { @@ -30,29 +30,31 @@ impl ArxmlParser { */ fn handle_isignal_to_pdu_mappings(&self, mapping: &Element, signals: &mut HashMap, - signal_groups: &mut Vec) + signal_groups: &mut Vec) -> Result<()> { if let Some(signal) = mapping .get_sub_element(ElementName::ISignalRef) .and_then(|elem| elem.get_reference_target().ok()) { - let refpath = get_required_string(mapping, - ElementName::ISignalRef); + let refpath = get_subelement_string_value(mapping, ElementName::ISignalRef) + .ok_or_else(|| anyhow!("Error getting required String value of {}", ElementName::ISignalRef))?; - let name = get_required_item_name(&signal, "ISignalRef"); + let name = signal.item_name() + .ok_or_else(|| Error::GetItemName{item: "ISignalRef"})?; - let byte_order = get_required_string(mapping, ElementName::PackingByteOrder); + let byte_order = get_subelement_string_value(mapping, ElementName::PackingByteOrder) + .ok_or_else(|| anyhow!("Error getting required String value of {}", ElementName::PackingByteOrder))?; let start_pos = get_required_int_value(mapping, - ElementName::StartPosition); + ElementName::StartPosition)?; let length = get_required_int_value(&signal, - ElementName::Length); + ElementName::Length)?; let mut init_values: InitValues = InitValues::NotExist(true); if let Some(mut init_value_elem) = signal.get_sub_element(ElementName::InitValue) { - process_init_value(&mut init_value_elem, &mut init_values, &name); + process_init_value(&mut init_value_elem, &mut init_values, &name)?; } signals.insert(refpath, (name, byte_order, start_pos, length, init_values)); } else if let Some(signal_group) = mapping @@ -62,13 +64,15 @@ impl ArxmlParser { // store the signal group for now signal_groups.push(signal_group); } + + Ok(()) } /* 1. Parses and processes all the ISignals defined in the parent ISignalIPdu. 2. Fills the important extracted data into the grouped_signals and ungrouped_signals vectors of structures. */ - fn handle_isignals(&self, pdu: &Element, grouped_signals: &mut Vec, ungrouped_signals: &mut Vec) -> Option<()> { + fn handle_isignals(&self, pdu: &Element, grouped_signals: &mut Vec, ungrouped_signals: &mut Vec) -> Result<()> { //let mut signals: HashMap, Option)> = HashMap::new(); let mut signals: HashMap = HashMap::new(); let mut signal_groups = Vec::new(); @@ -77,12 +81,12 @@ impl ArxmlParser { if let Some(isignal_to_pdu_mappings) = pdu.get_sub_element(ElementName::ISignalToPduMappings) { // collect information about the signals and signal groups for mapping in isignal_to_pdu_mappings.sub_elements() { - self.handle_isignal_to_pdu_mappings(&mapping, &mut signals, &mut signal_groups); + self.handle_isignal_to_pdu_mappings(&mapping, &mut signals, &mut signal_groups)?; } } for signal_group in &signal_groups { - process_signal_group(signal_group, &mut signals, grouped_signals); + process_signal_group(signal_group, &mut signals, grouped_signals)?; } let remaining_signals: Vec<(String, String, u64, u64, InitValues)> = signals.values().cloned().collect(); @@ -101,14 +105,14 @@ impl ArxmlParser { ungrouped_signals.sort_by(|a, b| a.start_pos.cmp(&b.start_pos)); - Some(()) + Ok(()) } /* 1. Parses an Autosar ISignalIPdu element. 2. Returns important data in a self-defined ISignalIPDU structure. */ - fn handle_isignal_ipdu(&self, pdu: &Element) -> Option { + fn handle_isignal_ipdu(&self, pdu: &Element) -> Result { // Find out these values: ... let mut cyclic_timing_period_value: f64 = 0_f64; let mut cyclic_timing_period_tolerance: Option = None; @@ -149,7 +153,7 @@ impl ArxmlParser { let mut ungrouped_signals: Vec = Vec::new(); - self.handle_isignals(pdu, &mut grouped_signals, &mut ungrouped_signals); + self.handle_isignals(pdu, &mut grouped_signals, &mut ungrouped_signals)?; let isginal_ipdu: ISignalIPdu = ISignalIPdu { cyclic_timing_period_value, @@ -164,21 +168,21 @@ impl ArxmlParser { grouped_signals }; - Some(isginal_ipdu) + Ok(isginal_ipdu) } /* 1. Parses an Autosar NmPdu element 2. Returns important data in a self-defined NMPDU structure. */ - fn handle_nm_pdu(&self, pdu: &Element) -> Option { + fn handle_nm_pdu(&self, pdu: &Element) -> Result { let unused_bit_pattern = get_unused_bit_pattern(pdu); let mut grouped_signals: Vec = Vec::new(); let mut ungrouped_signals: Vec = Vec::new(); - self.handle_isignals(pdu, &mut grouped_signals, &mut ungrouped_signals); + self.handle_isignals(pdu, &mut grouped_signals, &mut ungrouped_signals)?; let nm_pdu: NmPdu = NmPdu { unused_bit_pattern, @@ -186,7 +190,7 @@ impl ArxmlParser { grouped_signals }; - Some(nm_pdu) + Ok(nm_pdu) } /* @@ -194,20 +198,20 @@ impl ArxmlParser { 2. Parses the Autosar PDU element 3. Returns important data in a self-defined PDU mapping structure. */ - fn handle_pdu_mapping(&self, pdu_mapping: &Element) -> Result { + fn handle_pdu_mapping(&self, pdu_mapping: &Element) -> Result { let pdu = get_required_reference( pdu_mapping, - ElementName::PduRef); + ElementName::PduRef)?; - let pdu_name = get_required_item_name( - &pdu, "Pdu"); + let pdu_name = pdu.item_name() + .ok_or_else(|| Error::GetItemName{item: "Pdu"})?; //let byte_order = get_required_string(pdu_mapping, let byte_order = get_optional_string(pdu_mapping, ElementName::PackingByteOrder); let pdu_length = get_required_int_value(&pdu, - ElementName::Length); + ElementName::Length)?; let pdu_dynamic_length = get_optional_string(&pdu, ElementName::HasDynamicLength); @@ -221,42 +225,21 @@ impl ArxmlParser { let pdu_contained_header_id_long = get_subelement_optional_string(&pdu, ElementName::ContainedIPduProps, ElementName::HeaderIdLongHeader); - //let mut pdu_specific: PDU = PDU::Temp(0); - let pdu_specific: Pdu; - - match pdu.element_name() { + let pdu_specific = match pdu.element_name() { ElementName::ISignalIPdu => { - if let Some(value) = self.handle_isignal_ipdu(&pdu) { - pdu_specific = Pdu::ISignalIPdu(value); - } else { - panic!("Error in handle_isignal_ipdu"); - } + self.handle_isignal_ipdu(&pdu).map(Pdu::ISignalIPdu)? } ElementName::NmPdu => { - if let Some(value) = self.handle_nm_pdu(&pdu) { - pdu_specific = Pdu::NmPdu(value); - } else { - panic!("Error in handle_nm_pdu"); - } + self.handle_nm_pdu(&pdu).map(Pdu::NmPdu)? } - /*ElementName::ContainerIPdu => { // Add support in future if needed - panic!("endounter containerpdu"); - //self.handle_container_ipdu(&pdu); - }*/ - /*ElementName::SecuredIPdu => { // Add support in future if needed - self.handle_secured_ipdu(&pdu); - }*/ - // Handle more? _ => { - let error = format!("PDU type {} not supported. Will skip it.", pdu.element_name()); - return Err(error) + bail!("PDU type {} not supported. Will skip it.", pdu.element_name()) } - } + }; let pdu_mapping: PduMapping = PduMapping { name: pdu_name, byte_order: get_byte_order(&byte_order), - // start_position: start_position, length: pdu_length, dynamic_length: pdu_dynamic_length, category: pdu_category, @@ -272,20 +255,20 @@ impl ArxmlParser { 1. Parses an Autosar CanFrameTriggering element. 2. Returns important data in a self-defined CanFrameTriggering structure. */ - fn handle_can_frame_triggering(&self, can_frame_triggering: &Element, has_fd_baudrate: bool) -> Result { - let can_frame_triggering_name= get_required_item_name( - can_frame_triggering, "CanFrameTriggering"); + fn handle_can_frame_triggering(&self, can_frame_triggering: &Element, has_fd_baudrate: bool) -> Result { + let can_frame_triggering_name = can_frame_triggering.item_name() + .ok_or_else(|| Error::GetItemName{item: "CanFrameTriggering"})?; let can_id = get_required_int_value( can_frame_triggering, - ElementName::Identifier); + ElementName::Identifier)?; let frame = get_required_reference( can_frame_triggering, - ElementName::FrameRef); + ElementName::FrameRef)?; - let frame_name = get_required_item_name( - &frame, "Frame"); + let frame_name = frame.item_name() + .ok_or_else(|| Error::GetItemName{item: "Frame"})?; let addressing_mode_str = if let Some(CharacterData::Enum(value)) = can_frame_triggering .get_sub_element(ElementName::CanAddressingMode) @@ -296,10 +279,7 @@ impl ArxmlParser { EnumItem::Standard.to_string() }; - let mut addressing_mode: bool = false; - if addressing_mode_str.to_uppercase() == *"EXTENDED" { - addressing_mode = true; - } + let can_29_bit_addressing = addressing_mode_str.eq_ignore_ascii_case("EXTENDED"); // allow it to be missing. When missing, then derive value from CanCluster let mut frame_rx_behavior = false; @@ -324,8 +304,8 @@ impl ArxmlParser { let mut rx_range_lower: u64 = 0; let mut rx_range_upper: u64 = 0; if let Some(range_elem) = can_frame_triggering.get_sub_element(ElementName::RxIdentifierRange) { - rx_range_lower = get_required_int_value(&range_elem, ElementName::LowerCanId); - rx_range_upper = get_required_int_value(&range_elem, ElementName::UpperCanId); + rx_range_lower = get_required_int_value(&range_elem, ElementName::LowerCanId)?; + rx_range_upper = get_required_int_value(&range_elem, ElementName::UpperCanId)?; } let mut rx_ecus: Vec = Vec::new(); @@ -344,7 +324,7 @@ impl ArxmlParser { for pdu_mapping in mappings.sub_elements() { match self.handle_pdu_mapping(&pdu_mapping) { Ok(value) => pdu_mappings_vec.push(value), - Err(error) => return Err(error) + Err(error) => bail!(error) } } } @@ -353,7 +333,7 @@ impl ArxmlParser { frame_triggering_name: can_frame_triggering_name, frame_name, can_id, - addressing_mode, + can_29_bit_addressing, frame_rx_behavior, frame_tx_behavior, rx_range_lower, @@ -371,16 +351,15 @@ impl ArxmlParser { 1. Parses an Autosar CanCluster element 2. Returns important data in a self-defined CanCluster structure. */ - fn handle_can_cluster(&self, can_cluster: &Element) -> Result { - let can_cluster_name = get_required_item_name( - can_cluster, "CanCluster"); + fn handle_can_cluster(&self, can_cluster: &Element) -> Result { + let can_cluster_name = can_cluster.item_name() + .ok_or_else(|| Error::GetItemName{item: "CanCluster"})?; let can_cluster_conditional = get_required_sub_subelement( can_cluster, ElementName::CanClusterVariants, - ElementName::CanClusterConditional); + ElementName::CanClusterConditional)?; - //let can_cluster_baudrate = self.get_required_subelement_int_value( let can_cluster_baudrate = get_optional_int_value( &can_cluster_conditional, ElementName::Baudrate); @@ -392,8 +371,7 @@ impl ArxmlParser { let has_fd_baudrate = can_cluster_baudrate > 0; if can_cluster_baudrate == 0 && can_cluster_fd_baudrate == 0 { - let msg = format!("Baudrate and FD Baudrate of CanCluster {} do not exist or are 0. Skipping this CanCluster.", can_cluster_name); - return Err(msg.to_string()); + bail!("Baudrate and FD Baudrate of CanCluster {} do not exist or are 0. Skipping this CanCluster.", can_cluster_name) } // iterate over PhysicalChannels and handle the CanFrameTriggerings inside them @@ -405,8 +383,7 @@ impl ArxmlParser { { physical_channels = value; } else { - let msg = format!("Cannot handle physical channels of CanCluster {}", can_cluster_name); - return Err(msg.to_string()); + bail!("Cannot handle physical channels of CanCluster {}", can_cluster_name) } let mut can_frame_triggerings: HashMap = HashMap::new(); @@ -442,13 +419,13 @@ impl ArxmlParser { If not exists, then parse and safe parsed structures as serialized data in file_name + ".ser" Returns a vector of CanCluster structures. */ - pub fn parse_file(&self, file_name: &String, safe_or_load_serialized: bool) -> Option> { + pub fn parse_file(&self, file_name: &String, safe_or_load_serialized: bool) -> Result, String> { if safe_or_load_serialized { info!("Loading data from serialized file"); match load_serialized_data(file_name) { Ok(value) => { info!("Successfully loaded serialized data."); - return Some(value) + return Ok(value) } _ => warn!("Could not load serialized data. Will continue parsing.") } @@ -459,12 +436,10 @@ impl ArxmlParser { let model = AutosarModel::new(); if let Err(err) = model.load_file(file_name, false) { - panic!("Parsing failed. Error: {}", err.to_string()); + return Err(format!("Parsing failed. Error: {}", err)); } - // DEBUG - info!("Duration of loading was: {:?}", start.elapsed()); - // DEBUG END + debug!("Duration of loading was: {:?}", start.elapsed()); let mut can_clusters: HashMap = HashMap::new(); @@ -474,8 +449,7 @@ impl ArxmlParser { .filter_map(|(_path, weak)| weak.upgrade()) { if element.element_name() == ElementName::CanCluster { - let result: Result = self.handle_can_cluster(&element); - match result { + match self.handle_can_cluster(&element) { Ok(value) => { can_clusters.insert(value.name.clone(), value); } @@ -494,6 +468,38 @@ impl ArxmlParser { } } - Some(can_clusters) + Ok(can_clusters) } } + +#[cfg(test)] +mod tests { + use super::*; + use std::path::PathBuf; + + fn get_sample_file_path() -> String{ + let mut sample_file_path = PathBuf::from(env!("CARGO_MANIFEST_DIR")); + sample_file_path.push("samples/system-4.2.arxml"); + + return sample_file_path.into_os_string().into_string().unwrap() + } + + #[test] + fn test_parsing() { + let arxml_parser: ArxmlParser = ArxmlParser {}; + + let parse_res = arxml_parser.parse_file(&get_sample_file_path(), false).unwrap(); + + assert_eq!(parse_res.len(), 1); + let (cluster_name, cluster) = parse_res.iter().next().unwrap(); + + assert_eq!(&String::from("Cluster0"), cluster_name); + + println!("{}", cluster.can_frame_triggerings.len()); + + assert_eq!(cluster.can_frame_triggerings.len(), 5) + + // TODO: Extend this test + } + +} \ No newline at end of file diff --git a/opendut-edgar/restbus-simulation/src/arxml_structs.rs b/opendut-edgar/restbus-simulation/src/arxml_structs.rs index 6c47f5b4..cc15129b 100755 --- a/opendut-edgar/restbus-simulation/src/arxml_structs.rs +++ b/opendut-edgar/restbus-simulation/src/arxml_structs.rs @@ -28,7 +28,7 @@ pub struct CanFrameTriggering { pub frame_triggering_name: String, pub frame_name: String, pub can_id: u64, - pub addressing_mode: bool, + pub can_29_bit_addressing: bool, pub frame_rx_behavior: bool, pub frame_tx_behavior: bool, pub rx_range_lower: u64, diff --git a/opendut-edgar/restbus-simulation/src/arxml_utils.rs b/opendut-edgar/restbus-simulation/src/arxml_utils.rs index c590e462..322553df 100755 --- a/opendut-edgar/restbus-simulation/src/arxml_utils.rs +++ b/opendut-edgar/restbus-simulation/src/arxml_utils.rs @@ -9,13 +9,15 @@ use crate::restbus_utils::*; use std::collections::HashMap; use std::fs::File; use std::io::prelude::*; -use std::io::Error; use std::vec; +use anyhow::{anyhow, bail, Result}; + use autosar_data::{CharacterData, Element, ElementName, EnumItem}; use nix::libc::timeval; +use tracing::warn; /* Converts a CharacterData type from the autosar_data library @@ -25,21 +27,12 @@ pub fn decode_integer(cdata: &CharacterData) -> Option { if let CharacterData::String(text) = cdata { if text == "0" { Some(0) - } else if text.starts_with("0x") { - let hexstr = text.strip_prefix("0x").unwrap(); - Some(u64::from_str_radix(hexstr, 16).ok()?) - } else if text.starts_with("0X") { - let hexstr = text.strip_prefix("0X").unwrap(); - Some(u64::from_str_radix(hexstr, 16).ok()?) - } else if text.starts_with("0b") { - let binstr = text.strip_prefix("0b").unwrap(); - Some(u64::from_str_radix(binstr, 2).ok()?) - } else if text.starts_with("0B") { - let binstr = text.strip_prefix("0B").unwrap(); - Some(u64::from_str_radix(binstr, 2).ok()?) - } else if text.starts_with('0') { - let octstr = text.strip_prefix('0').unwrap(); - Some(u64::from_str_radix(octstr, 8).ok()?) + } else if text.starts_with("0x") || text.starts_with("0X") { + Some(u64::from_str_radix(&text[2..], 16).ok()?) + } else if text.starts_with("0b") || text.starts_with("0B") { + Some(u64::from_str_radix(&text[2..], 2).ok()?) + } else if let Some(stripped_octal) = text.strip_prefix('0') { + Some(u64::from_str_radix(stripped_octal, 8).ok()?) } else if text.to_ascii_lowercase() == "false" { Some(0) } else if text.to_ascii_lowercase() == "true" { @@ -92,31 +85,13 @@ pub fn get_sub_element_and_time_range(base: &Element, sub_elem_name: ElementName } } -/* - Gets a required item name from the element. This has to be possible. -*/ -pub fn get_required_item_name(element: &Element, element_name: &str) -> String { - if let Some(item_name) = element.item_name() { - item_name - } else { - panic!("Error getting required item name of {}", element_name); - } -} - /* Gets a required subsubelement from the element. This needs to succeed. */ -pub fn get_required_sub_subelement(element: &Element, subelement_name: ElementName, sub_subelement_name: ElementName) -> Element { - if let Some(sub_subelement) = element - .get_sub_element(subelement_name) - .and_then(|elem| elem.get_sub_element(sub_subelement_name)) - { - sub_subelement - } else { - panic!("Error getting sub_subelement. Tried to retrieve {} and then {}", - subelement_name, - sub_subelement_name); - } +pub fn get_required_sub_subelement(element: &Element, subelement_name: ElementName, sub_subelement_name: ElementName) -> Result { + element.get_sub_element(subelement_name) + .and_then(|elem| elem.get_sub_element(sub_subelement_name)) + .ok_or_else(|| anyhow!("Sub-sub-element of {subelement_name}->{sub_subelement_name} does not exist")) } /* @@ -132,12 +107,9 @@ pub fn get_subelement_int_value(element: &Element, subelement_name: ElementName) /* Gets the u64 value for a element. This has to succeed. */ -pub fn get_required_int_value(element: &Element, subelement_name: ElementName) -> u64 { - if let Some(int_value) = get_subelement_int_value(element, subelement_name) { - int_value - } else { - panic!("Error getting required integer value of {}", subelement_name); - } +pub fn get_required_int_value(element: &Element, subelement_name: ElementName) -> Result { + get_subelement_int_value(element, subelement_name) + .ok_or_else(|| anyhow!("Error getting required integer value of {subelement_name}")) } /* @@ -150,31 +122,31 @@ pub fn get_optional_int_value(element: &Element, subelement_name: ElementName) - /* Resolves a reference and returns the target Element. This has to succeed. */ -pub fn get_required_reference(element: &Element, subelement_name: ElementName) -> Element { - if let Some(subelement) = element.get_sub_element(subelement_name) { +pub fn get_required_reference(element: &Element, subelement_name: ElementName) -> Result { + if let Some(subelement) = element.get_sub_element(subelement_name){ match subelement.get_reference_target() { - Ok(reference) => return reference, + Ok(reference) => return Ok(reference), Err(err) => { - println!("[-] Warning: Constant ref error: {}. Will try modification of target name and reference again.", err); + warn!("[-] Warning: Constant ref error: {}. Will try modification of target name and reference again.", err); match subelement.character_data() { Some(val) => { let new_dest = "/Constants/".to_string() + val.to_string().as_str(); match subelement.set_character_data(CharacterData::String(new_dest)) { Ok(()) => {} - Err(err) => println!("[-] Warning: Error setting new dest: {}", err) + Err(err) => warn!("[-] Warning: Error setting new dest: {}", err) } match subelement.get_reference_target() { - Ok(reference) => return reference, - Err(err) => println!("[-] Warning: Constant ref retry error: {}.", err), + Ok(reference) => return Ok(reference), + Err(err) => warn!("[-] Warning: Constant ref retry error: {}.", err), } } - _ => println!("[-] Warning: No success in retry because Element CharacterData is not set."), + _ => warn!("[-] Warning: No success in retry because Element CharacterData is not set."), } } } } - panic!("Error getting required reference for {}", subelement_name); + bail!("Error getting required reference for {}", subelement_name) } /* @@ -187,17 +159,6 @@ pub fn get_subelement_string_value(element: &Element, subelement_name: ElementNa .map(|cdata| cdata.to_string()) } -/* - Gets the String value for a element. This has to succeed. -*/ -pub fn get_required_string(element: &Element, subelement_name: ElementName) -> String { - if let Some(value) = get_subelement_string_value(element, subelement_name) { - value - } else { - panic!("Error getting required String value of {}", subelement_name); - } -} - /* Gets the String value for a element. This is optional. So, if the subelement does not exist, then "" is returned. */ @@ -237,12 +198,12 @@ pub fn get_byte_order(byte_order: &String) -> bool { true } -fn process_isignal_init_value(isignal: &ISignal, bits: &mut [bool]) { +fn process_isignal_init_value(isignal: &ISignal, bits: &mut [bool]) -> Result<()>{ let mut tmp_bit_array: Vec = Vec::new(); let init_values = &isignal.init_values; let isignal_byte_order = isignal.byte_order; - let isignal_length: usize = isignal.length.try_into().unwrap(); - let isignal_start: usize = isignal.start_pos.try_into().unwrap(); + let isignal_length: usize = isignal.length.try_into()?; + let isignal_start: usize = isignal.start_pos.try_into()?; match init_values { InitValues::Single(value) => { @@ -263,7 +224,7 @@ fn process_isignal_init_value(isignal: &ISignal, bits: &mut [bool]) { } InitValues::Array(values) => { if isignal_length % 8 != 0 { - panic!("ISignal length for array is not divisable through 8. Length is {}", isignal_length); + bail!("ISignal length for array is not divisible by 8. Length is {}", isignal_length) } for isignal_value in values { @@ -285,11 +246,11 @@ fn process_isignal_init_value(isignal: &ISignal, bits: &mut [bool]) { tmp_bit_array.extend(tmp_tmp_bit_array); } } - _ => return + _ => return Ok(()) } - if tmp_bit_array.len() != >::try_into(isignal.length).unwrap() { - panic!("Miscalculation for tmp_bit_array"); + if tmp_bit_array.len() != >::try_into(isignal.length)? { + bail!("Miscalculation for tmp_bit_array") } let mut index: usize = 0; @@ -297,7 +258,9 @@ fn process_isignal_init_value(isignal: &ISignal, bits: &mut [bool]) { while index < isignal_length { bits[isignal_start + index] = tmp_bit_array[index]; index += 1; - } + } + + Ok(()) } /* @@ -306,18 +269,18 @@ fn process_isignal_init_value(isignal: &ISignal, bits: &mut [bool]) { Currenlty assumes Little Endian byte ordering and has support for signals that are Little Endian or Big Endian. Bit positions in undefined ranges are set to unused_bit_pattern. */ -pub fn extract_init_values(unused_bit_pattern: bool, ungrouped_signals: &Vec, grouped_signals: &Vec, length: u64, byte_order: &bool) -> Vec { - let dlc: usize = length.try_into().unwrap(); +pub fn extract_init_values(unused_bit_pattern: bool, ungrouped_signals: &Vec, grouped_signals: &Vec, length: u64, byte_order: &bool) -> Result> { + let dlc: usize = length.try_into()?; let mut bits = vec![unused_bit_pattern; dlc * 8]; // Using unusued_bit_pattern for undefined bits for isignal in ungrouped_signals { - process_isignal_init_value(isignal, &mut bits); + process_isignal_init_value(isignal, &mut bits)?; } for isignal_group in grouped_signals { for isignal in &isignal_group.isignals { - process_isignal_init_value(isignal, &mut bits); + process_isignal_init_value(isignal, &mut bits)?; } } @@ -350,14 +313,10 @@ pub fn extract_init_values(unused_bit_pattern: bool, ungrouped_signals: &Vec bool { // even though it needs to exist at least for ISignalIPdus, we keep it as optional, since at least one encounter shows that it might be missing. // then use 0 as default value - let mut unused_bit_pattern_int = get_optional_int_value(pdu, ElementName::UnusedBitPattern); + let unused_bit_pattern_int = get_optional_int_value(pdu, ElementName::UnusedBitPattern); - let unused_bit_pattern: bool; - // supports values > 1. Just look at least significant bit - unused_bit_pattern_int &= 1; - - if unused_bit_pattern_int == 0 { - unused_bit_pattern = false; - } else if unused_bit_pattern_int == 1 { - unused_bit_pattern = true; - } else { - panic!("Error reading unused_bit_pattern. Value is {}", unused_bit_pattern_int); - } - - unused_bit_pattern + (unused_bit_pattern_int & 1) != 0 } /* Processes the Autosar FramePortRefs elements inside a CanFrameTriggering to find out the ECUs (names) that send and receive this underlying CAN frame. */ -pub fn process_frame_ports(can_frame_triggering: &Element, can_frame_triggering_name: &String, rx_ecus: &mut Vec, tx_ecus: &mut Vec) -> Result<(), String> { +pub fn process_frame_ports(can_frame_triggering: &Element, can_frame_triggering_name: &String, rx_ecus: &mut Vec, tx_ecus: &mut Vec) -> Result<()> { if let Some(frame_ports) = can_frame_triggering.get_sub_element(ElementName::FramePortRefs) { let frame_ports: Vec = frame_ports.sub_elements() .filter(|se| se.element_name() == ElementName::FramePortRef) @@ -403,13 +350,13 @@ pub fn process_frame_ports(can_frame_triggering: &Element, can_frame_triggering_ match direction { EnumItem::In => rx_ecus.push(ecu_name), EnumItem::Out => tx_ecus.push(ecu_name), - _ => return Err(format!("Invalid direction ID encountered in FramePort. Skipping CanFrameTriggering {}", can_frame_triggering_name)) + _ => bail!("Invalid direction ID encountered in FramePort. Skipping CanFrameTriggering {}", can_frame_triggering_name) } } else { - return Err(format!("No CommunicationDirection encountered in FramePort. Skipping CanFrameTriggering {}", can_frame_triggering_name)) + bail!("No CommunicationDirection encountered in FramePort. Skipping CanFrameTriggering {}", can_frame_triggering_name) } } else { - return Err(format!("Could not extract ECUName in FramePort. Skipping CanFrameTriggering {}", can_frame_triggering_name)) ; + bail!("Could not extract ECUName in FramePort. Skipping CanFrameTriggering {}", can_frame_triggering_name) ; } } }/* else { @@ -422,36 +369,34 @@ pub fn process_frame_ports(can_frame_triggering: &Element, can_frame_triggering_ /* Processes the Autosar InitValue element of an ISignal. Extracts one or more of them an put them into passed init_values argument. */ -pub fn process_init_value(init_value_elem: &mut Element, init_values: &mut InitValues, signal_name: &String) { - let init_value_type: u8; - - let mut subelement = init_value_elem.get_sub_element_at(0).unwrap(); +pub fn process_init_value(init_value_elem: &mut Element, init_values: &mut InitValues, signal_name: &String) -> Result<()> { + let mut subelement = init_value_elem.get_sub_element_at(0) + .ok_or_else(|| anyhow!("Failed to obtain subelement of init_value_elem"))?; if subelement.element_name().eq(&ElementName::ConstantReference) { let constant = get_required_reference( &subelement, - ElementName::ConstantRef); + ElementName::ConstantRef)?; - *init_value_elem = constant.get_sub_element(ElementName::ValueSpec).unwrap(); + *init_value_elem = constant.get_sub_element(ElementName::ValueSpec) + .ok_or_else(|| anyhow!("Failed to obtain subelement of constant"))?; - subelement = init_value_elem.get_sub_element_at(0).unwrap(); - } - - if subelement.element_name().eq(&ElementName::NumericalValueSpecification) { - init_value_type = 0; - } else if subelement.element_name().eq(&ElementName::ArrayValueSpecification) { - init_value_type = 1; - } else { - panic!("Unrecognized sublement {} for init-value", subelement.element_name()); + subelement = init_value_elem.get_sub_element_at(0) + .ok_or_else(|| anyhow!("Failed to obtain subelement of init_value_elem"))?; } + let init_value_type = match subelement.element_name() { + ElementName::NumericalValueSpecification => 0, + ElementName::ArrayValueSpecification => 1, + _ => bail!("Unrecognized subelement {} for init-value", subelement.element_name()) + }; + if init_value_type == 0 { - if let Some(num_val) = init_value_elem.get_sub_element(ElementName::NumericalValueSpecification) { - let init_value = get_required_int_value(&num_val, ElementName::Value); - *init_values = InitValues::Single(init_value); - } else { - panic!("InitValue element does not have NumercialValueSpecification for signal {}", signal_name); - } + let num_val = init_value_elem.get_sub_element(ElementName::NumericalValueSpecification) + .ok_or_else(|| anyhow!("InitValue element does not have NumercialValueSpecification for signal {}", signal_name))?; + let init_value = get_required_int_value(&num_val, ElementName::Value)?; + *init_values = InitValues::Single(init_value); + } else { let mut init_value_array: Vec = Vec::new(); @@ -459,12 +404,14 @@ pub fn process_init_value(init_value_elem: &mut Element, init_values: &mut InitV ElementName::ArrayValueSpecification, ElementName::Elements); - for num_val_elem in num_val_elements.sub_elements() { - init_value_array.push(get_required_int_value(&num_val_elem, ElementName::Value)); + for num_val_elem in num_val_elements?.sub_elements() { + init_value_array.push(get_required_int_value(&num_val_elem, ElementName::Value)?); } *init_values = InitValues::Array(init_value_array); } + + Ok(()) } /* @@ -474,13 +421,15 @@ pub fn process_init_value(init_value_elem: &mut Element, init_values: &mut InitV */ pub fn process_signal_group(signal_group: &Element, signals: &mut HashMap, - grouped_signals: &mut Vec) -> Option<()> + grouped_signals: &mut Vec) -> Result<()> { - let group_name = get_required_item_name(signal_group, "ISignalGroupRef"); + let group_name = signal_group.item_name() + .ok_or_else(|| Error::GetItemName{item: "ISignalGroupRef"})?; let mut signal_group_signals: Vec = Vec::new(); - let isignal_refs = signal_group.get_sub_element(ElementName::ISignalRefs)?; + let isignal_refs = signal_group.get_sub_element(ElementName::ISignalRefs) + .ok_or_else(|| anyhow!("Element has no sub-element"))?; // Removing ok and needed? for isignal_ref in isignal_refs.sub_elements() @@ -511,11 +460,11 @@ pub fn process_signal_group(signal_group: &Element, { for elem in com_transformations.sub_elements() { let data_transformation = get_required_reference(&elem, - ElementName::DataTransformationRef); + ElementName::DataTransformationRef)?; - data_transformations.push(get_required_item_name( - &data_transformation, - "DataTransformation")); + let data_transformation_name = data_transformation.item_name() + .ok_or_else(|| Error::GetItemName{item: "DataTransformation"})?; + data_transformations.push(data_transformation_name); } } @@ -531,16 +480,16 @@ pub fn process_signal_group(signal_group: &Element, .and_then(|elem| elem.get_sub_element(ElementName::EndToEndTransformationISignalPropsConditional)) { let transformer_reference = get_required_reference(&e2exf_props_cond, - ElementName::TransformerRef); + ElementName::TransformerRef)?; - let transformer_name = get_required_item_name(&transformer_reference, - "TransformerName"); + let transformer_name = transformer_reference.item_name() + .ok_or_else(|| Error::GetItemName{item: "TransformerName"})?; let data_ids = e2exf_props_cond - .get_sub_element(ElementName::DataIds)?; + .get_sub_element(ElementName::DataIds).ok_or_else(|| anyhow!("Element has no sub-element"))?; let data_id = get_required_int_value(&data_ids, - ElementName::DataId); + ElementName::DataId)?; // allow optional for now //let data_length = get_required_int_value(&e2exf_props_cond, @@ -568,7 +517,7 @@ pub fn process_signal_group(signal_group: &Element, grouped_signals.push(isignal_group_struct); - Some(()) + Ok(()) } /* @@ -576,10 +525,10 @@ pub fn process_signal_group(signal_group: &Element, 2. Create TimedCanFrame sructure out of data and put the structure into timed_can_frames vector. Note: Should normally only add one TimedCanFrame but multiple may be added in case multiple PDU Mappings exist for a Can frame. */ -pub fn get_timed_can_frame(can_frame_triggering: &CanFrameTriggering, timed_can_frames: &mut Vec) { +pub fn get_timed_can_frame(can_frame_triggering: &CanFrameTriggering, timed_can_frames: &mut Vec) -> Result<()> { let can_id: u32 = can_frame_triggering.can_id as u32; let len: u8 = can_frame_triggering.frame_length as u8; - let addressing_mode: bool = can_frame_triggering.addressing_mode; + let addressing_mode: bool = can_frame_triggering.can_29_bit_addressing; let frame_tx_behavior: bool = can_frame_triggering.frame_tx_behavior; for pdu_mapping in &can_frame_triggering.pdu_mappings { let mut count: u32 = 0; @@ -608,7 +557,7 @@ pub fn get_timed_can_frame(can_frame_triggering: &CanFrameTriggering, timed_can_ &pdu.ungrouped_signals, &pdu.grouped_signals, pdu_mapping.length, - &pdu_mapping.byte_order); + &pdu_mapping.byte_order)?; } Pdu::NmPdu(pdu) => { ival2_tv_usec = 100000; // every 100 ms @@ -616,7 +565,7 @@ pub fn get_timed_can_frame(can_frame_triggering: &CanFrameTriggering, timed_can_ &pdu.ungrouped_signals, &pdu.grouped_signals, pdu_mapping.length, - &pdu_mapping.byte_order); + &pdu_mapping.byte_order)?; } } @@ -627,41 +576,42 @@ pub fn get_timed_can_frame(can_frame_triggering: &CanFrameTriggering, timed_can_ timed_can_frames.push(create_time_can_frame_structure(count, &ivals, can_id, len, addressing_mode, frame_tx_behavior, &init_values)); } + Ok(()) } /* 1. Find CanFrameTriggering structure based on CAN id. 2. Put its important data as TimedCanFrame structure into timed_can_frames vector. */ -pub fn get_timed_can_frame_from_id(can_clusters: &HashMap, bus_name: String, can_id: u64) -> Vec { +pub fn get_timed_can_frame_from_id(can_clusters: &HashMap, bus_name: String, can_id: u64) -> Result> { let mut timed_can_frames: Vec = Vec::new(); if let Some(can_cluster) = can_clusters.get(&bus_name) { if let Some(can_frame_triggering) = can_cluster.can_frame_triggerings.get(&can_id) { - get_timed_can_frame(can_frame_triggering, &mut timed_can_frames); + get_timed_can_frame(can_frame_triggering, &mut timed_can_frames)?; } } - timed_can_frames + Ok(timed_can_frames) } /* 1. Iterate over all CanFrameTriggerings belonging to a CanCluster structure. 2. Put all CanFrameTriggering important data as TimedCanFrame structures into timed_can_frames vector. */ -pub fn get_timed_can_frames_from_bus(can_clusters: &HashMap, bus_name: String) -> Vec { +pub fn get_timed_can_frames_from_bus(can_clusters: &HashMap, bus_name: String) -> Result> { let mut timed_can_frames: Vec = Vec::new(); if let Some(can_cluster) = can_clusters.get(&bus_name) { for can_frame_triggering in can_cluster.can_frame_triggerings.values() { - get_timed_can_frame(can_frame_triggering, &mut timed_can_frames) + get_timed_can_frame(can_frame_triggering, &mut timed_can_frames)? } } - timed_can_frames + Ok(timed_can_frames) } -pub fn load_serialized_data(file_name: &String) -> Result, Error> { +pub fn load_serialized_data(file_name: &String) -> Result> { let mut file = File::open(file_name.to_owned() + ".ser")?; let mut contents = String::new(); file.read_to_string(&mut contents)?; @@ -671,11 +621,17 @@ pub fn load_serialized_data(file_name: &String) -> Result) -> Result<(), Error> { +pub fn store_serialized_data(file_name: &String, can_clusters: &HashMap) -> Result<()> { let serialized = serde_json::to_string(can_clusters)?; let mut file = File::create(file_name.to_owned() + ".ser")?; file.write_all(serialized.as_bytes())?; Ok(()) +} + +#[derive(Debug, thiserror::Error)] +pub enum Error<'a> { + #[error("Failed to get required item name of '{item}'")] + GetItemName { item: &'a str }, } \ No newline at end of file diff --git a/opendut-edgar/restbus-simulation/src/main.rs b/opendut-edgar/restbus-simulation/src/main.rs index 2c09e010..eee8f9d5 100755 --- a/opendut-edgar/restbus-simulation/src/main.rs +++ b/opendut-edgar/restbus-simulation/src/main.rs @@ -47,7 +47,7 @@ fn collect_pdus(can_clusters: &HashMap, bus_name: String) -> &pdu.ungrouped_signals, &pdu.grouped_signals, pdu_mapping.length, - &pdu_mapping.byte_order); + &pdu_mapping.byte_order).unwrap(); init_values_strings.push(get_pdu_hex(&can_frame_triggering.can_id, &init_values)) } Pdu::NmPdu(pdu) => { @@ -55,7 +55,7 @@ fn collect_pdus(can_clusters: &HashMap, bus_name: String) -> &pdu.ungrouped_signals, &pdu.grouped_signals, pdu_mapping.length, - &pdu_mapping.byte_order); + &pdu_mapping.byte_order).unwrap(); init_values_strings.push(get_pdu_hex(&can_frame_triggering.can_id, &init_values)) } } @@ -70,7 +70,7 @@ fn collect_pdus(can_clusters: &HashMap, bus_name: String) -> Play a single CAN frame from the target bus periodcically (if it is periodic) to the bus to which ifname is connected to. */ fn test_find_frame_and_play(restbus_simulation: &RestbusSimulation, ifname: &String, can_clusters: &HashMap, bus_name: String, can_id: u64) { - let timed_can_frames: Vec = get_timed_can_frame_from_id(can_clusters, bus_name, can_id); + let timed_can_frames: Vec = get_timed_can_frame_from_id(can_clusters, bus_name, can_id).unwrap(); match restbus_simulation.play_all(&timed_can_frames, ifname) { Ok(_val) => println!("Successfully sent message with can id {}", can_id), @@ -84,7 +84,7 @@ fn test_find_frame_and_play(restbus_simulation: &RestbusSimulation, ifname: &Str Play all CAN frames from the target bus periodcically (only periodic frames are sent periodically) to the bus to which ifname is connected to. */ fn test_bus_play_all(restbus_simulation: &RestbusSimulation, ifname: &String, can_clusters: &HashMap, bus_name: String) { - let timed_can_frames: Vec = get_timed_can_frames_from_bus(can_clusters, bus_name); + let timed_can_frames: Vec = get_timed_can_frames_from_bus(can_clusters, bus_name).unwrap(); match restbus_simulation.play_all(&timed_can_frames, ifname) { Ok(_val) => println!("Successfully established restbus simulation"), @@ -106,7 +106,7 @@ fn main() -> std::io::Result<()> { let arxml_parser: ArxmlParser = ArxmlParser {}; // Parse the ARXML file. Use serialized file if it exists. Parsed data is stored in can cluster ARXML element representations. - if let Some(can_clusters) = arxml_parser + if let Ok(can_clusters) = arxml_parser .parse_file(&file_name.to_string(), true) { let bus_name = "Cluster0"; diff --git a/opendut-edgar/restbus-simulation/src/restbus_simulation.rs b/opendut-edgar/restbus-simulation/src/restbus_simulation.rs index ea6456fa..40ec96e3 100755 --- a/opendut-edgar/restbus-simulation/src/restbus_simulation.rs +++ b/opendut-edgar/restbus-simulation/src/restbus_simulation.rs @@ -19,7 +19,7 @@ impl RestbusSimulation { 2. Converts all TimedCanFrames into regular CAN frames and puts each CAN frame into a BCM struct. 3. All created BCM structs are written to the BCM socket (sent to the Broadcast Manager). */ - pub fn play_all(&self, timed_can_frames: &Vec, ifname: &String) -> Result { + pub fn play_all(&self, timed_can_frames: &Vec, ifname: &String) -> Result<(), String> { let sock = create_socket()?; connect_socket(sock, ifname)?; @@ -38,7 +38,7 @@ impl RestbusSimulation { write_socket(sock, &write_bytes)?; } - Ok(true) + Ok(()) } /*pub fn play_single_bcm_frame(&self, ifname: &String, count: u32, ival1_tv_sec: i64, ival1_tv_usec: i64, diff --git a/opendut-edgar/src/service/start.rs b/opendut-edgar/src/service/start.rs index 2c092b0b..5cfc2cf6 100644 --- a/opendut-edgar/src/service/start.rs +++ b/opendut-edgar/src/service/start.rs @@ -11,7 +11,6 @@ use opendut_carl_api::proto::services::peer_messaging_broker::{ApplyPeerConfigur use opendut_restbus_simulation::arxml_parser::ArxmlParser; use opendut_restbus_simulation::arxml_utils::get_timed_can_frames_from_bus; use opendut_restbus_simulation::restbus_simulation::RestbusSimulation; -use opendut_restbus_simulation::restbus_structs::TimedCanFrame; use opendut_types::peer::configuration::{OldPeerConfiguration, PeerConfiguration}; use opendut_types::peer::PeerId; use opendut_util::settings::LoadedConfig; @@ -136,14 +135,18 @@ pub async fn run_stream_receiver( let arxml_parser: ArxmlParser = ArxmlParser {}; - if let Some(can_clusters) = arxml_parser.parse_file(&arxml_file_path, arxml_serialization) { + if let Ok(can_clusters) = arxml_parser.parse_file(&arxml_file_path, arxml_serialization) { // Play all CAN frames from the target bus periodcically (only periodic frames are sent periodically) to the bus to which ifname is connected to. - let timed_can_frames: Vec = get_timed_can_frames_from_bus(&can_clusters, target_cluster); - - match restbus_simulation.play_all(&timed_can_frames, &ifname) { - Ok(_val) => info!("Successfully established restbus simulation"), - Err(error) => info!("Could not establish restbus simulation because: {}", error) + match get_timed_can_frames_from_bus(&can_clusters, target_cluster) { + Ok(timed_can_frames) => { + if let Err(err) = restbus_simulation.play_all(&timed_can_frames, &ifname) { + error!("Failed to start restbus simulation: {err}") + } + }, + Err(err) => error!("Failed to start restbus simulation - error while extracting timed CAN frames from ARXML: {err}"), } + + info!("Successfully established restbus simulation"); } }