Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion MODULE.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -180,7 +180,7 @@ bazel_dep(name = "rules_rust", version = "0.68.1-score")

bazel_dep(name = "buildifier_prebuilt", version = "8.2.1.2", dev_dependency = True)

bazel_dep(name = "score_crates", version = "0.0.7", repo_name = "score_communication_crate_index")
bazel_dep(name = "score_crates", version = "0.0.9", repo_name = "score_communication_crate_index")
bazel_dep(name = "boost.program_options", version = "1.87.0")
bazel_dep(name = "boost.interprocess", version = "1.87.0")

Expand Down
73 changes: 51 additions & 22 deletions score/mw/com/example/com-api-example/basic-consumer-producer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,18 @@
* SPDX-License-Identifier: Apache-2.0
********************************************************************************/

// This demo app writing and reading tire pressure data using producer and consumer respectively.
// It is demonstrating the composition of consumer and producer in one struct,
// but they can be used separately as well.
// The example is using Lola runtime, but it can be used with any runtime by changing the runtime initialization part.
// Note: The example is using unwrap and panic in some places for simplicity,
// but it is recommended to handle errors properly in production code.

use clap::Parser;
use std::path::PathBuf;

use com_api::{
Builder, Error, FindServiceSpecifier, InstanceSpecifier, Interface, LolaRuntimeBuilderImpl,
Builder, FindServiceSpecifier, InstanceSpecifier, Interface, LolaRuntimeBuilderImpl,
OfferedProducer, Producer, Publisher, Result, Runtime, RuntimeBuilder, SampleContainer,
SampleMaybeUninit, SampleMut, ServiceDiscovery, Subscriber, Subscription,
};
Expand Down Expand Up @@ -88,7 +95,7 @@ impl<R: Runtime> VehicleMonitor<R> {
let mut sample_buf = SampleContainer::new(3);

match self.tire_subscriber.try_receive(&mut sample_buf, 1) {
Ok(0) => Err(Error::Fail),
Ok(0) => Ok("No tire data received".to_string()),
Ok(x) => {
let sample = sample_buf.pop_front().unwrap();
Ok(format!("{} samples received: sample[0] = {:?}", x, *sample))
Expand Down Expand Up @@ -119,16 +126,20 @@ impl<R: Runtime> VehicleMonitor<R> {
fn create_consumer<R: Runtime>(runtime: &R, service_id: InstanceSpecifier) -> VehicleConsumer<R> {
let consumer_discovery =
runtime.find_service::<VehicleInterface>(FindServiceSpecifier::Specific(service_id));
let available_service_instances = consumer_discovery.get_available_instances().unwrap();
let available_service_instances = consumer_discovery
.get_available_instances()
.expect("Failed to get available service instances");

// Select service instance at specific handle_index
let handle_index = 0; // or any index you need from vector of instances
let consumer_builder = available_service_instances
.into_iter()
.nth(handle_index)
.unwrap();
.expect("Failed to get consumer builder at specified handle index");

consumer_builder.build().unwrap()
consumer_builder
.build()
.expect("Failed to build consumer instance")
}

#[allow(dead_code)]
Expand All @@ -142,16 +153,16 @@ async fn create_consumer_async<R: Runtime>(
let available_service_instances = consumer_discovery
.get_available_instances_async()
.await
.unwrap();
.expect("Failed to get available service instances asynchronously");

// Select service instance at specific handle_index
let handle_index = 0; // or any index you need from vector of instances
let consumer_builder = available_service_instances
.into_iter()
.nth(handle_index)
.unwrap();
.expect("Failed to get consumer builder at specified handle index");

consumer_builder.build().unwrap()
consumer_builder.build().expect("Failed to build consumer instance")
}

// Create a producer for the specified service identifier
Expand All @@ -160,8 +171,10 @@ fn create_producer<R: Runtime>(
service_id: InstanceSpecifier,
) -> VehicleOfferedProducer<R> {
let producer_builder = runtime.producer_builder::<VehicleInterface>(service_id);
let producer = producer_builder.build().unwrap();
producer.offer().unwrap()
let producer = producer_builder
.build()
.expect("Failed to build producer instance");
producer.offer().expect("Failed to offer producer instance")
}

// Run the example with the specified runtime
Expand All @@ -176,12 +189,22 @@ fn run_with_runtime<R: Runtime>(name: &str, runtime: &R) {
let tire_pressure = 5.0;
println!("Setting tire pressure to {tire_pressure}");
for i in 0..5 {
monitor
.write_tire_data(Tire {
pressure: tire_pressure + i as f32,
})
.unwrap();
let tire_data = monitor.read_tire_data().unwrap();
match monitor.write_tire_data(Tire {
pressure: tire_pressure + i as f32,
}) {
Ok(_) => (),
Err(e) => {
eprintln!("Failed to write tire data: {:?}", e);
continue;
}
}
let tire_data = match monitor.read_tire_data() {
Ok(data) => data,
Err(e) => {
eprintln!("Failed to read tire data: {:?}", e);
continue;
}
};
println!("{tire_data}");
}
//unoffer returns producer back, so if needed it can be used further
Expand Down Expand Up @@ -338,15 +361,21 @@ mod test {
offered_producer: VehicleOfferedProducer<R>,
) -> VehicleOfferedProducer<R> {
for i in 0..10 {
let uninit_sample = offered_producer.left_tire.allocate().unwrap();
let uninit_sample = match offered_producer.left_tire.allocate() {
Ok(sample) => sample,
Err(e) => {
eprintln!("Failed to allocate sample: {:?}", e);
continue;
}
};
let sample = uninit_sample.write(Tire {
pressure: 1.0 + i as f32,
});
sample.send().unwrap();
println!(
"[SENDER] Sent sample with pressure: {:.2} psi",
1.0 + i as f32
);
match sample.send() {
Ok(_) => (),
Err(e) => eprintln!("Failed to send sample: {:?}", e),
}
println!("Sent sample with pressure: {}", 1.0 + i as f32);
tokio::time::sleep(tokio::time::Duration::from_millis(1000)).await;
}
offered_producer
Expand Down
2 changes: 2 additions & 0 deletions score/mw/com/impl/rust/com-api/com-api-concept/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ rust_library(
name = "com-api-concept",
srcs = [
"com_api_concept.rs",
"error.rs",
"interface_macros.rs",
"lib.rs",
"reloc.rs",
Expand All @@ -30,6 +31,7 @@ rust_library(
],
deps = [
"@score_baselibs_rust//src/containers",
"@score_communication_crate_index//:thiserror",
],
)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@
//! - Structures
//! - Tuples

use crate::error::*;
use crate::Reloc;
pub use com_api_concept_macros::CommData;
use containers::fixed_capacity::FixedCapacityQueue;
Expand All @@ -54,16 +55,6 @@ use core::future::Future;
use core::ops::{Deref, DerefMut};
use std::path::Path;

/// Error enumeration for different failure cases in the Consumer/Producer/Runtime APIs.
#[derive(Debug)]
pub enum Error {
/// TODO: To be replaced, dummy value for "something went wrong"
Fail,
Timeout,
AllocateFailed,
SubscribeFailed,
}

/// Result type alias with `std::result::Result` using `com_api::Error` as error type
pub type Result<T> = core::result::Result<T, Error>;

Expand Down Expand Up @@ -262,15 +253,15 @@ impl InstanceSpecifier {
/// A `Result` containing the constructed `InstanceSpecifier` on success and an `Error` on failure.
///
/// # Errors
/// Returns `Error::Fail` if the provided string does not conform to the required format.
/// Returns `Error::InstanceSpecifierInvalid` if the provided string does not conform to the required format.
pub fn new(service_name: impl AsRef<str>) -> Result<InstanceSpecifier> {
let service_name = service_name.as_ref();
if Self::check_str(service_name) {
Ok(Self {
specifier: service_name.to_string(),
})
} else {
Err(Error::Fail)
Err(Error::ServiceError(ServiceFailedReason::InstanceSpecifierInvalid))
}
}
}
Expand Down Expand Up @@ -700,11 +691,11 @@ impl<S> SampleContainer<S> {
/// A `Result` indicating success or failure.
///
/// # Errors
/// Returns 'Error' if container is already full.
/// Returns 'Error::AllocateError' if container is already full.
pub fn push_back(&mut self, new: S) -> Result<()> {
self.inner
.push_back(new)
.map_err(|_| Error::AllocateFailed)?;
self.inner.push_back(new).map_err(|_| {
Error::AllocateError(AllocationFailureReason::OutOfMemory)
})?;
Ok(())
}

Expand Down
106 changes: 106 additions & 0 deletions score/mw/com/impl/rust/com-api/com-api-concept/error.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
/********************************************************************************
* Copyright (c) 2026 Contributors to the Eclipse Foundation
*
* See the NOTICE file(s) distributed with this work for additional
* information regarding copyright ownership.
*
* This program and the accompanying materials are made available under the
* terms of the Apache License Version 2.0 which is available at
* https://www.apache.org/licenses/LICENSE-2.0
*
* SPDX-License-Identifier: Apache-2.0
********************************************************************************/

use thiserror::Error;

/// Detailed information about Service Discovery failure reasons,
/// including specific issues with service interfaces, instance specifiers, and handle retrieval.
#[derive(Debug, Error)]
pub enum ServiceFailedReason {
#[error("Invalid instance specifier format or content, which may not be according to the expected format or contain invalid content")]
InstanceSpecifierInvalid,
#[error("Service not found, which may not be available currently or accessible")]
ServiceNotFound,
#[error("Failed to offer service instance for discovery")]
OfferServiceFailed,
#[error("Failed to start asynchronous discovery")]
FailedToStartDiscovery,
}

/// Reason for producer failure
#[derive(Debug, Error)]
pub enum ProducerFailedReason {
#[error("Invalid instance specifier format or content, which may not be according to the expected format or contain invalid content")]
InstanceSpecifierInvalid,
#[error("Skeleton creation failed")]
SkeletonCreationFailed,
#[error("Builder creation failed for producer instance")]
BuilderCreationFailed,
}

/// Reason for consumer failure
#[derive(Debug, Error)]
pub enum ConsumerFailedReason {
#[error("Invalid instance specifier format or content, which may not be according to the expected format or contain invalid content")]
InstanceSpecifierInvalid,
#[error("Service not found from service discovery handle")]
ServiceHandleNotFound,
#[error("Proxy creation failed")]
ProxyCreationFailed,
}

/// Memory allocation error details
#[derive(Debug, Error)]
pub enum AllocationFailureReason {
#[error(
"Requested size exceeds available memory which is configured during subscription setup"
)]
OutOfMemory,
#[error("Invalid allocation request")]
InvalidRequest,
#[error("Failed to allocate sample in shared memory for skeleton event")]
AllocationToSharedMemoryFailed,
}

/// Comprehensive error reasons for receive failure
#[derive(Debug, Error)]
pub enum ReceiveFailedReason {
#[error("Error at the time of initializing receive operation")]
InitializationFailed,
#[error("Receive operation failed because of internal error")]
ReceiveError,
#[error("Internal receive buffer not available for receive operation")]
BufferUnavailable,
#[error("Sample size out of bounds, expected at most {max}, but got {requested}")]
SampleCountOutOfBounds { max: usize, requested: usize },
}

/// Comprehensive error reasons for event-related failures
#[derive(Debug, Error)]
pub enum EventFailedReason {
#[error("Event creation error, possibly due to invalid event type or internal error")]
EventCreationFailed,
#[error("Event publication failed, possibly due to internal error")]
EventPublishFailed,
#[error("Failed to send sample data to the service instance, possibly due to internal error")]
SendingDataFailed,
#[error("Event not available for subscription, possibly due to missing event type or incompatible service")]
EventNotAvailable,
}

/// Error enumeration for different failure cases in the Consumer/Producer/Runtime APIs.
#[derive(Debug, Error)]
pub enum Error {
#[error("Service error due to: {0}")]
ServiceError(ServiceFailedReason),
#[error("Producer error due to: {0}")]
ProducerError(ProducerFailedReason),
#[error("Consumer error due to: {0}")]
ConsumerError(ConsumerFailedReason),
#[error("Event operation failed due to: {0}")]
EventError(EventFailedReason),
#[error("Sample container allocation failed due to: {0}")]
AllocateError(AllocationFailureReason),
#[error("Receive operation failed due to: {0}")]
ReceiveError(ReceiveFailedReason),
}
2 changes: 2 additions & 0 deletions score/mw/com/impl/rust/com-api/com-api-concept/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,11 @@
/// The interface macro generates the necessary types and trait implementations for defining communication interfaces,
/// while the Reloc type provides a safe abstraction for moving data across thread or process boundaries without violating Rust's ownership rules.
mod com_api_concept;
mod error;
mod interface_macros;
mod reloc;
pub use com_api_concept::*;
pub use error::*;
#[doc(hidden)]
pub use paste;
pub use reloc::Reloc;
Loading
Loading