From 33480981871da447ca2fa08bc16988e401a6b59a Mon Sep 17 00:00:00 2001 From: Daniel Thaler Date: Fri, 26 Jul 2024 19:26:45 +0200 Subject: [PATCH] Basic support for software components --- autosar-data-abstraction/src/lib.rs | 1 + .../src/software_component/mod.rs | 312 ++++++++++++++++++ autosar-data-abstraction/src/system.rs | 24 +- autosar-data-abstraction/tests/test.rs | 39 ++- 4 files changed, 373 insertions(+), 3 deletions(-) create mode 100644 autosar-data-abstraction/src/software_component/mod.rs diff --git a/autosar-data-abstraction/src/lib.rs b/autosar-data-abstraction/src/lib.rs index 4023c56..5996ff3 100644 --- a/autosar-data-abstraction/src/lib.rs +++ b/autosar-data-abstraction/src/lib.rs @@ -2,6 +2,7 @@ use autosar_data::{AutosarDataError, AutosarModel, Element, EnumItem}; use thiserror::Error; pub mod communication; +pub mod software_component; mod arpackage; mod ecuinstance; diff --git a/autosar-data-abstraction/src/software_component/mod.rs b/autosar-data-abstraction/src/software_component/mod.rs new file mode 100644 index 0000000..39e55c0 --- /dev/null +++ b/autosar-data-abstraction/src/software_component/mod.rs @@ -0,0 +1,312 @@ +use crate::*; +use autosar_data::ElementName; + +pub trait AbstractSwComponentType: AbstractionElement { + /// iterator over the instances of the component type + fn instances(&self) -> SwComponentPrototypeIterator { + let model_result = self.element().model(); + let path_result = self.element().path(); + if let (Ok(model), Ok(path)) = (model_result, path_result) { + let reflist = model.get_references_to(&path); + SwComponentPrototypeIterator::new(reflist) + } else { + SwComponentPrototypeIterator::new(vec![]) + } + } + + /// iterator over all compositions containing instances of the component type + fn compositions(&self) -> impl Iterator { + self.instances() + .filter_map(|swcp| swcp.element().named_parent().ok().flatten()) + .filter_map(|elem| CompositionSwComponentType::try_from(elem).ok()) + } +} + +//################################################################## + +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct CompositionSwComponentType(Element); +abstraction_element!(CompositionSwComponentType, CompositionSwComponentType); + +impl CompositionSwComponentType { + /// create a new composition component with the given name + pub fn new(name: &str, package: &ArPackage) -> Result { + let elements = package.element().get_or_create_sub_element(ElementName::Elements)?; + let composition = elements.create_named_sub_element(ElementName::CompositionSwComponentType, name)?; + Ok(Self(composition)) + } + + /// check if the composition is a parent (or grand-parent, etc.) of the component + pub fn is_parent_of(&self, other: &T) -> bool { + // the expectation is that in normal cases each component has only one parent + // additionally there should never be any cycles in the composition hierarchy + let mut work_items = other.compositions().collect::>(); + let mut counter = 1000; // just to prevent infinite loops, since I don't trust files generated by other tools + while !work_items.is_empty() && counter > 0 { + counter -= 1; + if work_items.contains(self) { + return true; + } + // the uses of pop here makes this a depth-first search in the case where there are multiple parents + let item = work_items.pop().unwrap(); + work_items.extend(item.compositions()); + } + + false + } + + /// add a component of type component_type to the composition + /// + /// It is not allowed to form cycles in the composition hierarchy, and this will return an error + pub fn add_component(&self, name: &str, component_type: &SwComponentType) -> Result<(), AutosarAbstractionError> { + if let SwComponentType::Composition(composition_component) = component_type { + if composition_component.is_parent_of(self) { + return Err(AutosarAbstractionError::InvalidParameter( + "Creating a cycle in the composition hierarchy".to_string(), + )); + } + } + + let components = self.element().get_or_create_sub_element(ElementName::Components)?; + let component = components.create_named_sub_element(ElementName::SwComponentPrototype, name)?; + component + .create_sub_element(ElementName::TypeTref)? + .set_reference_target(&component_type.element())?; + Ok(()) + } + + /// get an iterator over the components of the composition + pub fn components(&self) -> CompositionComponentsIter { + CompositionComponentsIter::new(self.element().get_sub_element(ElementName::Components)) + } +} + +impl AbstractSwComponentType for CompositionSwComponentType {} + +//################################################################## + +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct ApplicationSwComponentType(Element); +abstraction_element!(ApplicationSwComponentType, ApplicationSwComponentType); + +impl ApplicationSwComponentType { + pub fn new(name: &str, package: &ArPackage) -> Result { + let elements = package.element().get_or_create_sub_element(ElementName::Elements)?; + let application = elements.create_named_sub_element(ElementName::ApplicationSwComponentType, name)?; + Ok(Self(application)) + } +} + +impl AbstractSwComponentType for ApplicationSwComponentType {} + +//################################################################## + +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct ComplexDeviceDriverSwComponentType(Element); +abstraction_element!(ComplexDeviceDriverSwComponentType, ComplexDeviceDriverSwComponentType); + +impl ComplexDeviceDriverSwComponentType { + pub fn new(name: &str, package: &ArPackage) -> Result { + let elements = package.element().get_or_create_sub_element(ElementName::Elements)?; + let cdd = elements.create_named_sub_element(ElementName::ComplexDeviceDriverSwComponentType, name)?; + Ok(Self(cdd)) + } +} + +impl AbstractSwComponentType for ComplexDeviceDriverSwComponentType {} + +//################################################################## + +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct ServiceSwComponentType(Element); +abstraction_element!(ServiceSwComponentType, ServiceSwComponentType); + +impl ServiceSwComponentType { + pub fn new(name: &str, package: &ArPackage) -> Result { + let elements = package.element().get_or_create_sub_element(ElementName::Elements)?; + let service = elements.create_named_sub_element(ElementName::ServiceSwComponentType, name)?; + Ok(Self(service)) + } +} + +impl AbstractSwComponentType for ServiceSwComponentType {} + +//################################################################## + +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct SensorActuatorSwComponentType(Element); +abstraction_element!(SensorActuatorSwComponentType, SensorActuatorSwComponentType); + +impl SensorActuatorSwComponentType { + pub fn new(name: &str, package: &ArPackage) -> Result { + let elements = package.element().get_or_create_sub_element(ElementName::Elements)?; + let sensor_actuator = elements.create_named_sub_element(ElementName::SensorActuatorSwComponentType, name)?; + Ok(Self(sensor_actuator)) + } +} + +impl AbstractSwComponentType for SensorActuatorSwComponentType {} + +//################################################################## + +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct EcuAbstractionSwComponentType(Element); +abstraction_element!(EcuAbstractionSwComponentType, EcuAbstractionSwComponentType); + +impl EcuAbstractionSwComponentType { + pub fn new(name: &str, package: &ArPackage) -> Result { + let elements = package.element().get_or_create_sub_element(ElementName::Elements)?; + let ecu_abstraction = elements.create_named_sub_element(ElementName::EcuAbstractionSwComponentType, name)?; + Ok(Self(ecu_abstraction)) + } +} + +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub enum SwComponentType { + Composition(CompositionSwComponentType), + Application(ApplicationSwComponentType), + ComplexDeviceDriver(ComplexDeviceDriverSwComponentType), + Service(ServiceSwComponentType), + SensorActuator(SensorActuatorSwComponentType), + EcuAbstraction(EcuAbstractionSwComponentType), +} + +impl AbstractionElement for SwComponentType { + fn element(&self) -> &Element { + match self { + SwComponentType::Composition(comp) => comp.element(), + SwComponentType::Application(app) => app.element(), + SwComponentType::ComplexDeviceDriver(cdd) => cdd.element(), + SwComponentType::Service(service) => service.element(), + SwComponentType::SensorActuator(sensor_actuator) => sensor_actuator.element(), + SwComponentType::EcuAbstraction(ecu_abstraction) => ecu_abstraction.element(), + } + } +} + +impl TryFrom for SwComponentType { + type Error = AutosarAbstractionError; + + fn try_from(element: Element) -> Result { + match element.element_name() { + ElementName::CompositionSwComponentType => { + Ok(SwComponentType::Composition(CompositionSwComponentType(element))) + } + ElementName::ApplicationSwComponentType => { + Ok(SwComponentType::Application(ApplicationSwComponentType(element))) + } + ElementName::ComplexDeviceDriverSwComponentType => Ok(SwComponentType::ComplexDeviceDriver( + ComplexDeviceDriverSwComponentType(element), + )), + ElementName::ServiceSwComponentType => Ok(SwComponentType::Service(ServiceSwComponentType(element))), + ElementName::SensorActuatorSwComponentType => { + Ok(SwComponentType::SensorActuator(SensorActuatorSwComponentType(element))) + } + ElementName::EcuAbstractionSwComponentType => { + Ok(SwComponentType::EcuAbstraction(EcuAbstractionSwComponentType(element))) + } + _ => Err(AutosarAbstractionError::ConversionError { + element, + dest: "SwComponentType".to_string(), + }), + } + } +} + +impl From for SwComponentType { + fn from(comp: CompositionSwComponentType) -> Self { + SwComponentType::Composition(comp) + } +} + +impl From for SwComponentType { + fn from(app: ApplicationSwComponentType) -> Self { + SwComponentType::Application(app) + } +} + +impl From for SwComponentType { + fn from(cdd: ComplexDeviceDriverSwComponentType) -> Self { + SwComponentType::ComplexDeviceDriver(cdd) + } +} + +impl From for SwComponentType { + fn from(service: ServiceSwComponentType) -> Self { + SwComponentType::Service(service) + } +} + +impl From for SwComponentType { + fn from(sensor_actuator: SensorActuatorSwComponentType) -> Self { + SwComponentType::SensorActuator(sensor_actuator) + } +} + +impl From for SwComponentType { + fn from(ecu_abstraction: EcuAbstractionSwComponentType) -> Self { + SwComponentType::EcuAbstraction(ecu_abstraction) + } +} + +impl AbstractSwComponentType for SwComponentType {} + +//################################################################## + +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct SwComponentPrototype(Element); +abstraction_element!(SwComponentPrototype, SwComponentPrototype); + +//################################################################## + +element_iterator!(CompositionComponentsIter, SwComponentType, Some); + +//################################################################## + +reflist_iterator!(SwComponentPrototypeIterator, SwComponentPrototype); + +//################################################################## + +#[cfg(test)] +mod test { + use autosar_data::AutosarVersion; + + use super::*; + + #[test] + fn software_compositions() { + let model = AutosarModel::new(); + let _file = model.create_file("filename", AutosarVersion::LATEST).unwrap(); + let package = ArPackage::get_or_create(&model, "/package").unwrap(); + + let comp1 = CompositionSwComponentType::new("comp1", &package).unwrap(); + let comp2 = CompositionSwComponentType::new("comp2", &package).unwrap(); + let comp3 = CompositionSwComponentType::new("comp3", &package).unwrap(); + let comp4 = CompositionSwComponentType::new("comp4", &package).unwrap(); + + comp1.add_component("comp2", &comp2.clone().into()).unwrap(); + comp2.add_component("comp3", &comp3.clone().into()).unwrap(); + comp3.add_component("comp4", &comp4.clone().into()).unwrap(); + + assert_eq!(comp1.instances().count(), 0); + assert_eq!(comp2.instances().count(), 1); + assert_eq!(comp3.instances().count(), 1); + assert_eq!(comp4.instances().count(), 1); + + assert!(comp1.is_parent_of(&comp2)); + assert!(comp1.is_parent_of(&comp3)); + assert!(comp1.is_parent_of(&comp4)); + + assert!(!comp2.is_parent_of(&comp1)); + assert!(comp2.is_parent_of(&comp3)); + assert!(comp2.is_parent_of(&comp4)); + + assert!(!comp3.is_parent_of(&comp1)); + assert!(!comp3.is_parent_of(&comp2)); + assert!(comp3.is_parent_of(&comp4)); + + assert!(!comp4.is_parent_of(&comp1)); + assert!(!comp4.is_parent_of(&comp2)); + assert!(!comp4.is_parent_of(&comp3)); + } +} diff --git a/autosar-data-abstraction/src/system.rs b/autosar-data-abstraction/src/system.rs index 5973518..c09902f 100644 --- a/autosar-data-abstraction/src/system.rs +++ b/autosar-data-abstraction/src/system.rs @@ -1,8 +1,11 @@ use std::iter::FusedIterator; use crate::communication::{ - CanCluster, CanClusterSettings, CanFrame, Cluster, ContainerIPdu, DcmIPdu, EthernetCluster, FlexrayCluster, FlexrayClusterSettings, FlexrayFrame, GeneralPurposeIPdu, GeneralPurposePdu, ISignal, ISignalGroup, ISignalIPdu, MultiplexedIPdu, NPdu, NmPdu, SecuredIPdu, SystemSignal, SystemSignalGroup + CanCluster, CanClusterSettings, CanFrame, Cluster, ContainerIPdu, DcmIPdu, EthernetCluster, FlexrayCluster, + FlexrayClusterSettings, FlexrayFrame, GeneralPurposeIPdu, GeneralPurposePdu, ISignal, ISignalGroup, ISignalIPdu, + MultiplexedIPdu, NPdu, NmPdu, SecuredIPdu, SystemSignal, SystemSignalGroup, }; +use crate::software_component::CompositionSwComponentType; use crate::{abstraction_element, AbstractionElement, ArPackage, AutosarAbstractionError, EcuInstance}; use autosar_data::{AutosarDataError, AutosarModel, Element, ElementName, WeakElement}; @@ -665,6 +668,25 @@ impl System { .create_sub_element(ElementName::FibexElementRef)?; fibex_element_ref.set_reference_target(elem) } + + /// set the root software composition of the system + pub fn set_root_sw_composition( + &self, + name: &str, + composition_type: &CompositionSwComponentType, + ) -> Result<(), AutosarAbstractionError> { + let root_compositions = self.0 + .get_or_create_sub_element(ElementName::RootSoftwareCompositions)?; + + if let Some(existing_composition) = root_compositions.get_sub_element(ElementName::RootSwCompositionPrototype) { + root_compositions.remove_sub_element(existing_composition)?; + } + root_compositions + .create_named_sub_element(ElementName::RootSwCompositionPrototype, name)? + .get_or_create_sub_element(ElementName::SoftwareCompositionTref)? + .set_reference_target(composition_type.element())?; + Ok(()) + } } //######################################################### diff --git a/autosar-data-abstraction/tests/test.rs b/autosar-data-abstraction/tests/test.rs index bf3f547..0add547 100644 --- a/autosar-data-abstraction/tests/test.rs +++ b/autosar-data-abstraction/tests/test.rs @@ -7,8 +7,7 @@ mod test { FlexrayChannelName, FlexrayClusterSettings, FlexrayCommunicationCycle, IPv4AddressSource, NetworkEndpointAddress, SocketAddressType, SocketConnectionIpduIdentifierSet, SystemSignal, TpConfig, TransferProperty, - }, - AbstractionElement, ArPackage, ByteOrder, System, SystemCategory, + }, software_component::CompositionSwComponentType, AbstractionElement, ArPackage, ByteOrder, System, SystemCategory }; #[test] @@ -123,6 +122,15 @@ mod test { ft_2.connect_to_ecu(&ecu_instance_b, CommunicationDirection::Out) .unwrap(); + // software component modeling + let swc_package = ArPackage::get_or_create(&model, "/SoftwareComponents").unwrap(); + let root_composition = CompositionSwComponentType::new("RootComposition", &swc_package).unwrap(); + + // ... Todo: create other swc elements ... + + // add the root composition to the system + system.set_root_sw_composition("CanTestComposition", &root_composition).unwrap(); + println!("{}", model.files().next().unwrap().serialize().unwrap()); model.write().unwrap(); } @@ -239,6 +247,15 @@ mod test { .create_pdu_port(&ecu_instance, CommunicationDirection::Out) .unwrap(); + // software component modeling + let swc_package = ArPackage::get_or_create(&model, "/SoftwareComponents").unwrap(); + let root_composition = CompositionSwComponentType::new("RootComposition", &swc_package).unwrap(); + + // ... Todo: create other swc elements ... + + // add the root composition to the system + system.set_root_sw_composition("EthernetTestComposition", &root_composition).unwrap(); + println!("{}", model.files().next().unwrap().serialize().unwrap()); model.write().unwrap(); } @@ -372,6 +389,15 @@ mod test { .create_pdu_port(&ecu_instance, CommunicationDirection::Out) .unwrap(); + // software component modeling + let swc_package = ArPackage::get_or_create(&model, "/SoftwareComponents").unwrap(); + let root_composition = CompositionSwComponentType::new("RootComposition", &swc_package).unwrap(); + + // ... Todo: create other swc elements ... + + // add the root composition to the system + system.set_root_sw_composition("EthernetTestComposition", &root_composition).unwrap(); + println!("{}", model.files().next().unwrap().serialize().unwrap()); model.write().unwrap(); } @@ -467,6 +493,15 @@ mod test { ft_2.connect_to_ecu(&ecu_instance_b, CommunicationDirection::Out) .unwrap(); + // software component modeling + let swc_package = ArPackage::get_or_create(&model, "/SoftwareComponents").unwrap(); + let root_composition = CompositionSwComponentType::new("RootComposition", &swc_package).unwrap(); + + // ... Todo: create other swc elements ... + + // add the root composition to the system + system.set_root_sw_composition("FlexrayTestComposition", &root_composition).unwrap(); + println!("{}", model.files().next().unwrap().serialize().unwrap()); // model.write().unwrap(); }