Skip to content
Closed
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
1 change: 1 addition & 0 deletions score/mw/com/example/com-api-example/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ rust_library(
"@score_baselibs//src/log/score_log",
"@score_communication_crate_index//:clap",
"@score_communication_crate_index//:futures",
"@score_communication_crate_index//:tokio",
],
)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
* SPDX-License-Identifier: Apache-2.0
********************************************************************************/

use score_com::{interface, CommData, ProviderInfo, Publisher, Reloc, Subscriber};
use score_com::{interface, CommData, FieldPublisher, ProviderInfo, Publisher, Reloc, Subscriber};
use score_log::ScoreDebug;

#[derive(Debug, Reloc, CommData, ScoreDebug)]
Expand Down Expand Up @@ -47,3 +47,32 @@ interface!(
exhaust: Event<Exhaust>,
}
);

// Field-based interface with compile-time initialization safety.
// All fields must be explicitly initialized via the Type State pattern before offering.
// The Type State pattern ensures that you cannot call offer() until all fields have been updated.
// Just for demonstration of APIs usage we are creating a separate interface for field,
// we have plan to update the interface macro to support mixed event and field interface in future.
// https://github.com/eclipse-score/communication/issues/701
interface!(
interface VehicleField {
Id = "VehicleFieldInterface",
left_tire: Field<Tire>,
exhaust: Field<Exhaust>,
}
);

// We can also define mix of event , field and method in one interface.
// TODO : Remove the comment once method design PR is merged.
// interface!(
// interface VehicleMonitor {
// Id = "VehicleMonitorInterface",
// left_tire: Event<Tire>,
// exhaust: Event<Exhaust>,
// left_tire_field: Field<Tire>,
// exhaust_field: Field<Exhaust>,
// update_tire_pressure(Tire) -> (),
// update_front_tires_pressure(Tire, Tire) -> (),
// get_tire_pressure() -> Tire,
// }
// );
119 changes: 119 additions & 0 deletions score/mw/com/example/com-api-example/src/field_consumer.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
/********************************************************************************
* Copyright (c) 2025 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
********************************************************************************/

#![allow(unused)]

use score_com::{
Builder, FieldSubscriber, FieldSubscription, FindServiceSpecifier, InstanceSpecifier,
Interface, Runtime, SampleContainer, ServiceDiscovery, Subscriber, Subscription,
};

use com_api_gen::{Tire, VehicleFieldInterface};

type VehicleFieldConsumer<R> = <VehicleFieldInterface as Interface>::Consumer<R>;

// create the consumer.
fn create_consumer_field<R: Runtime>(
runtime: &R,
service_id: InstanceSpecifier,
) -> VehicleFieldConsumer<R> {
let consumer_discovery =
runtime.find_service::<VehicleFieldInterface>(FindServiceSpecifier::Specific(service_id));
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)
.expect("Failed to get consumer builder at specified handle index");

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

async fn process_get_method_async<S, R>(subscription: S)
where
S: FieldSubscription<Tire, R>,
R: Runtime,
{
// Get field value asynchronously
match subscription.get().await {
Ok(_method_return) => {
println!("Current tire pressure from spawned task");
}
Err(e) => eprintln!("Failed to get tire pressure: {:?}", e),
}

println!("Async subscription processing in spawned task completed");
}

// Function to demonstrate the usage of the consumer to get and set fields,
// Subscribe to the fields event and it provides the set and get method as well.
fn consumer_processing_field<R: Runtime + 'static>(consumer: VehicleFieldConsumer<R>)
where
<<R as Runtime>::FieldSubscriber<Tire> as Subscriber<Tire, R>>::Subscription: Send + 'static,
{
// Field consumer API methods
// But they demonstrate the correct API usage pattern
// TODO: Currently we are not offering the get method async in FieldSubscriber
// because async call will may run in different thread and that will cause the issue in subscription.
let _ = consumer
.left_tire
.get()
.map(|result| println!("Got field value via consumer: {:?}", result));

let _ = consumer
.left_tire
.set(&Tire { pressure: 30.0 })
.map(|result| println!("Set field value via consumer: {:?}", result));

// Subscribe to the field to receive updates
let subscription = consumer
.left_tire
.subscribe(3)
.expect("Failed to subscribe to field");

// Create scope for sample_buf to ensure it's dropped before tokio::spawn
{
let mut sample_buf = SampleContainer::new(3);

// Poll for updates (non-blocking)
match subscription.try_receive(&mut sample_buf, 1) {
Ok(n) if n > 0 => {
while let Some(sample) = sample_buf.pop_front() {
println!("Updated tire pressure: {:?}", *sample);
}
}
_ => {
println!("No new tire pressure updates available");
}
}
// sample_buf is dropped here at end of scope
}

// Set via subscription
let _ = subscription
.set(&Tire { pressure: 35.0 })
.map(|result| println!("Set field value via subscription: {:?}", result));

// Spawn async task with subscription
// The subscription is moved into the task
tokio::spawn(async move {
process_get_method_async(subscription).await;
// subscription is automatically unsubscribed when dropped at end of task
});
}
83 changes: 83 additions & 0 deletions score/mw/com/example/com-api-example/src/field_producer.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
/********************************************************************************
* Copyright (c) 2025 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
********************************************************************************/

#![allow(unused)]

use score_com::{Builder, FieldPublisher, InstanceSpecifier, Interface, Producer, Runtime};

use com_api_gen::{Exhaust, Tire, VehicleFieldInterface};

// VehicleFieldProducer is the producer type for the VehicleField interface (before offering)
type VehicleFieldProducer<R> = <VehicleFieldInterface as Interface>::Producer<R>;
// VehicleFieldOfferedProducer is the offered producer type for the VehicleField interface (fields support update/set-handler)
type VehicleFieldOfferedProducer<R> =
<<VehicleFieldInterface as Interface>::Producer<R> as Producer<R>>::OfferedProducer;

// Below function just demonstrate the field APIs usage
// This build fine but it can not run because we have not implemented the field APIs in Lola runtime yet.

// Producer creation and intialization of fields with initial values and set handlers for the fields
// It will return the offered producer instance which can be used to update the fields.
fn create_producer_field<R: Runtime + 'static>(
runtime: &R,
service_id: InstanceSpecifier,
initial_tire_value: Tire,
initial_exhaust_value: Exhaust,
) -> VehicleFieldOfferedProducer<R>
where
<R as Runtime>::FieldPublisher<Tire>: Send + Sync,
<R as Runtime>::FieldPublisher<Exhaust>: Send,
{
let producer_builder = runtime.producer_builder::<VehicleFieldInterface>(service_id);
let producer = producer_builder
.build()
.expect("Failed to build producer instance");

// Use validator pattern with compile-time type-state validation
// Must register handlers and initialize all fields before offer() is available
let offered = producer
.init()
.register_set_handler_left_tire(move |val: &Tire| {
println!("Received tire pressure update: {:?}", val);
// Additional logic to handle the tire pressure update can be added here
// For example, we can increment value or conver unit and update the field again.
// TODO: in working example add that logic to demonstrate the set handler usage.
// Note: I think producer may be need clone ?
})
.register_set_handler_exhaust(|_val: &Exhaust| {
println!("Received exhaust update");
})
.update_left_tire(&initial_tire_value)
.expect("Failed to update left_tire field")
.update_exhaust(&initial_exhaust_value)
.expect("Failed to update exhaust field")
.offer()
.expect("Failed to offer producer instance");

offered
}

// Function to demonstrate the usage of the offered producer to update fields
fn offered_producer_process<R: Runtime>(offered_producer: VehicleFieldOfferedProducer<R>) {
// Use the offered producer to update fields
let new_tire_value = Tire { pressure: 32.0 };
let new_exhaust_value = Exhaust {};
offered_producer
.left_tire
.update(&new_tire_value)
.expect("Failed to update left_tire field");
offered_producer
.exhaust
.update(&new_exhaust_value)
.expect("Failed to update exhaust field");
}
4 changes: 3 additions & 1 deletion score/mw/com/example/com-api-example/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,14 @@
********************************************************************************/

pub mod consumer;
mod field_consumer;
mod field_producer;
pub mod producer;
pub use consumer::VehicleMonitorConsumer;
pub use producer::VehicleMonitorProducer;

use score_com::{Interface, Producer};
use com_api_gen::VehicleInterface;
use score_com::{Interface, Producer};

// Type aliases for generated consumer and offered producer types for the Vehicle interface
// VehicleConsumer is the consumer type generated for the Vehicle interface, parameterized by the runtime R
Expand Down
7 changes: 1 addition & 6 deletions score/mw/com/impl/rust/com-api/com-api-runtime-lola/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,7 @@ load("@rules_rust//rust:defs.bzl", "rust_doc_test", "rust_library", "rust_test")

rust_library(
name = "com-api-runtime-lola",
srcs = [
"consumer.rs",
"lib.rs",
"producer.rs",
"runtime.rs",
],
srcs = glob(["**/*.rs"]),
edition = "2024",
visibility = ["//score/mw/com:__subpackages__"],
deps = [
Expand Down
Loading
Loading