Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
5c9e3df
PoC implementation of the Generic Skeleton for Events and with initia…
mina-hamdi Dec 30, 2025
3edd8b6
Enhance Generic Skeleton Code
Abhishek2581 Jan 9, 2026
809cecd
Remove Send Api for Generic Skeleton with void *
Abhishek2581 Jan 15, 2026
7e6999e
Merge branch 'main' into generic_skeleton_implementation
Abhishek2581 Jan 19, 2026
2e6c99d
Reslove GenericSkeleton review feedback, fix layering violations
esrour99 Jan 23, 2026
9df7841
Required Edits for generic skeleton Interface
esrour99 Jan 27, 2026
ac37c95
Update the signature of ResetGuards func and Reusing DataTypeMetaInfo…
esrour99 Jan 28, 2026
24c35ea
Remove useless comments and clean up the code
esrour99 Jan 28, 2026
409fd3d
Add docstrings to new functions
esrour99 Jan 28, 2026
69faf2f
Integrate SampleAllocateePtr without void specilization
esrour99 Feb 1, 2026
c77993a
Fix blank dereference crash in SampleAllocateePtr and missing mock de…
esrour99 Feb 2, 2026
f26e10a
Refactor the Generic Skeleton Event implementation to address all rem…
esrour99 Feb 8, 2026
537931a
refine the Generic Skeleton Interface and adding Readme for generic s…
esrour99 Feb 10, 2026
c42fe16
Update GenericSkeleton Readme file & Update data block handling
esrour99 Feb 16, 2026
a9c4bfc
Update Readme for GS and shared memory alignment using std::max_align_t
esrour99 Feb 25, 2026
6150c15
Guard against over-aligned types & Update TestCases
esrour99 Feb 25, 2026
fbdb6cf
Adding Testcase to validate correct allocation
esrour99 Feb 25, 2026
af42aa4
Resolve conflicts, and fix generic skeleton tests
esrour99 Feb 25, 2026
45c117a
fix:shared_memory_storage_application
esrour99 Feb 25, 2026
8a11929
Merge remote-tracking branch 'origin/main' into generic_skeleton_impl…
esrour99 Feb 26, 2026
bdf5d83
Fix Alignment
esrour99 Feb 26, 2026
8fd004f
Merge pull request #1 from Valeo-S-CORE-Organization/generic_skeleton…
esrour99 Feb 26, 2026
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
Empty file modified score/mw/com/design/events_fields/NestedCallbacks.svg
100755 → 100644
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
6 changes: 4 additions & 2 deletions score/mw/com/design/skeleton_proxy/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@

The following structural view shows, how the separation of generic/binding independent part of a proxy/skeleton from
its flexible/variable technical binding implementation is achieved. **Note**: It does **only** reflect the common use
case of strongly typed proxies. The special case of "generic proxies" is described in
[design extension for generic proxies](generic_proxy/README.md#) to not bloat this class diagram even more:
case of strongly typed proxies and skeletons. The special case of "generic proxies" and "generic skeletons" are described in
[design extension for generic proxies](generic_proxy/README.md#) and [design extension for generic skeletons](generic_skeleton/README.md#) to not bloat this class diagram even more:

<a name="classdiagram"></a>

Expand Down Expand Up @@ -225,6 +225,8 @@ aggregates an object of type `lola::ProxyEventCommon`, to which it dispatches al
calls, it has to implement to fulfill its interface `ProxyEventBindingBase`.
The reason for this architectural decision is described in the [design extension for generic proxies](./generic_proxy/README.md)

Similarly, on the skeleton side, `lola::SkeletonEvent` aggregates an object of type `lola::SkeletonEventCommon`. This class encapsulates all `SampleTyp`e agnostic logic (such as interaction with `lola::Skeleton` for offering services, timestamp management, and notification handling). This allows both strongly typed skeletons and generic skeletons to share the same core implementation logic.

### Proxy auto-reconnect functionality

According to our requirements (namely requirement `SCR-29682823`), we need to support a functionality, which is known as
Expand Down
86 changes: 86 additions & 0 deletions score/mw/com/design/skeleton_proxy/generic_skeleton/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
# Generic Skeleton

A `GenericSkeleton` is a provider-side (skeleton) object that can be configured at runtime to offer any service. Unlike standard, code-generated skeletons, it is not tied to a specific service interface at compile-time.

It operates on raw data , making it ideal for applications that need to handle data without understanding its structure, such as:
* **Gateways/Bridges**: Forwarding data between different communication protocols.

## How to Use a Generic Skeleton

Using a `GenericSkeleton` requires two steps: defining the service in the deployment configuration and creating the skeleton instance with its runtime configuration.

### 1. Prerequisite: Deployment Configuration

The service instance must be defined in the `mw_com_config.json` file, just like any other service.

### 2. Creating and Using the Skeleton

A `GenericSkeleton` is created by providing it with metadata about the service elements (e.g., events, fields) it will offer. This metadata, including memory size and alignment for each element, is passed via the `GenericSkeletonCreateParams` struct.

The following example demonstrates how to create a generic skeleton that provides a single event.

**Example: Creating a Generic Skeleton with an Event**

```cpp
#include "score/mw/com/impl/generic_skeleton.h"
#include "score/mw/com/impl/data_type_meta_info.h"
#include <vector>

// The service instance specifier, as defined in mw_com_config.json
// const score::mw::com::InstanceSpecifier instance_specifier = ...;

// 1. Define the metadata for the event.
// The name must match the service definition.
const auto event_name = "map_api_lanes_stamped";
// The middleware needs the size and alignment for memory allocation.
const score::mw::com::impl::DataTypeMetaInfo event_meta_info{
sizeof(MapApiLanesStamped),
alignof(MapApiLanesStamped)
};

// 2. Collect all event definitions.
const std::vector<score::mw::com::impl::EventInfo> events = {
{event_name, event_meta_info}
};

// 3. Populate the creation parameters.
// Similar spans can be provided for fields and methods if they were supported.
score::mw::com::impl::GenericSkeletonCreateParams create_params;
create_params.events = events;

// 4. Create the Generic Skeleton instance.
auto create_result = score::mw::com::impl::GenericSkeleton::Create(
instance_specifier,
create_params
);

if (!create_result.has_value()) {
// Handle creation error
return;
}
auto& skeleton = create_result.value();

// 5. Offer the service.
skeleton.OfferService();

// 6. Retrieve the event by name to send data.
auto event_it = skeleton.GetEvents().find(event_name);
if (event_it != skeleton.GetEvents().cend()) {
// Get a non-const reference to the event to call non-const methods.
auto& generic_event = const_cast<score::mw::com::impl::GenericSkeletonEvent&>(event_it->second);

// Allocate a sample from the middleware. The size is known from the metadata
auto allocate_result = generic_event.Allocate();
if (allocate_result.has_value()) {
auto sample = std::move(allocate_result.value());

// Get the raw pointer from the smart pointer for populating the data.
auto* typed_sample = static_cast<MapApiLanesStamped*>(sample.Get());

// ... populate the typed_sample ...

// Send the sample.
generic_event.Send(std::move(sample));
}
}
```
Original file line number Diff line number Diff line change
@@ -0,0 +1,183 @@
@startuml generic_skeleton_model
title "Generic Skeleton Extension"

class "score::mw::com::impl::HandleType" {
-indentifier_: InstanceIdentifier
+operator==(const HandleType& other): bool
+operator<(const HandleType& other): bool
+GetInstanceId(): InstanceIdentifier&
+GetServiceInstanceDeployment(): ServiceInstanceDeployment&
}

class "SkeletonBindingFactory" {
+{static} Create(InstanceIdentifier identifier): std::unique_ptr<SkeletonBinding>
}

abstract class "score::mw::com::impl::SkeletonBase"
{
-skeleton_binding_ : std::unique_ptr<SkeletonBinding>
-identifier_ : InstanceIdentifier
+GetAssociatedInstanceIdentifier() : const InstanceIdentifier&
+OfferService(): ResultBlank
+StopOfferService(): void
..
<u>Notes:</u>
SkeletonBase is not copyable but moveable
}

abstract class "SkeletonBinding" {
+{abstract} PrepareOffer() = 0: ResultBlank
+{abstract} PrepareStopOffer() = 0: void
}

class "mw::com::impl::GenericSkeleton" #yellow {
using EventMap = ServiceElementMap<GenericSkeletonEvent>
..
+GenericSkeleton(const InstanceIdentifier&, std::unique_ptr<SkeletonBinding>, MethodCallProcessingMode)
+Create(const InstanceIdentifier&, const GenericSkeletonCreateParams&, MethodCallProcessingMode): Result<GenericSkeleton>
+Create(const InstanceSpecifier&, const GenericSkeletonCreateParams&, MethodCallProcessingMode): Result<GenericSkeleton>
+events_ : EventMap
+OfferService(): ResultBlank
+StopOfferService(): void
}

class "lola::Skeleton" {
+Skeleton(const InstanceIdentifier&, MethodCallProcessingMode)
+PrepareOffer(): ResultBlank
+PrepareStopOffer(): void
+Register(ElementFqId event_fqn, const SkeletonEventProperties& event_properties, std::size_t size, std::size_t alignment): std::pair<score::memory::shared::OffsetPtr<void>, EventDataControlComposite>
+RegisterGeneric(ElementFqId event_fqn, const SkeletonEventProperties& event_properties, std::size_t size, std::size_t alignment): std::pair<score::memory::shared::OffsetPtr<void>, EventDataControlComposite>
+DisconnectQmConsumers(): void
+GetInstanceQualityType(): QualityType
+GetSourcePid(): pid_t
}

class "mw::com::impl::SkeletonEvent<SampleType>" {
+Send(const SampleType&): ResultBlank
+Allocate(): Result<SampleAllocateePtr<SampleType>>
}

abstract class "SkeletonEventBindingBase" {
+{abstract} PrepareOffer() = 0: ResultBlank
+{abstract} PrepareStopOffer() = 0: void
+{abstract} GetBindingType() = 0: BindingType
+{abstract} SetSkeletonEventTracingData(impl::tracing::SkeletonEventTracingData tracing_data) = 0: void
}

abstract class "SkeletonEventBinding<SampleType>"
{
+{abstract} Send(const SampleType&) = 0: ResultBlank
+{abstract} Allocate() = 0: Result<SampleAllocateePtr<SampleType>>
}

class "SampleAllocateePtr<SampleType>" {
+Get(): SampleType*
+operator->(): SampleType*
+operator*(): SampleType&
}

class "lola::SkeletonEvent<SampleType>" {
+SkeletonEvent(lola::Skeleton& parent, const SkeletonEventProperties& event_properties, const ElementFqId& event_fqn)
+Send(const SampleType&) : ResultBlank
+Allocate(): Result<SampleAllocateePtr<SampleType>>
-event_shared_impl_ : lola::SkeletonEventCommon
}

class "GenericSkeletonEventBindingFactory" {
+{static} Create(SkeletonBase& parent, std::string_view event_name, const DataTypeMetaInfo& size_info): Result<std::unique_ptr<GenericSkeletonEventBinding>>
}

class "ServiceElementMap" {
using key_type = score::cpp::stringview
using mapped_type = GenericSkeletonEvent
using value_type = std::pair<const key_type, mapped_type>
using const_iterator = LegacyBidirectionalIterator to const value_type
..
+cbegin() : const_iterator
+cend() : const_iterator
+find() : const_iterator
+size() : std::size_t
+empty() : bool
}

abstract class "mw::com::impl::SkeletonEventBase" #yellow {
+SkeletonEventBase(SkeletonBase&, const std::string_view, std::unique_ptr<SkeletonEventBindingBase>)
+OfferService(): ResultBlank
+StopOfferService(): void
}

class "mw::com::impl::GenericSkeletonEvent" #yellow {
+GenericSkeletonEvent(SkeletonBase&, const std::string_view, std::unique_ptr<GenericSkeletonEventBinding>)
+Send(SampleAllocateePtr<void> sample): ResultBlank
+Allocate(): Result<SampleAllocateePtr<void>>
+GetSizeInfo() const : DataTypeMetaInfo
}

abstract class "GenericSkeletonEventBinding" #yellow {
+{abstract} Send(SampleAllocateePtr<void> sample) = 0: ResultBlank
+{abstract} Allocate() = 0: Result<SampleAllocateePtr<void>>
+{abstract} GetSizeInfo() const = 0: std::pair<size_t, size_t>
}

class "lola::SkeletonEventCommon" #yellow {
-parent_: lola::Skeleton&
-element_fq_id_: ElementFqId
-control_: score::cpp::optional<EventDataControlComposite>&
-current_timestamp_: EventSlotStatus::EventTimeStamp&
+SkeletonEventCommon(lola::Skeleton&, const ElementFqId&, score::cpp::optional<EventDataControlComposite>&, EventSlotStatus::EventTimeStamp&, impl::tracing::SkeletonEventTracingData)
+PrepareOfferCommon(): void
+PrepareStopOfferCommon(): void
+GetParent(): lola::Skeleton&
+GetElementFQId(): const ElementFqId&
+IsQmNotificationsRegistered(): bool
+IsAsilBNotificationsRegistered(): bool
+GetTracingData(): impl::tracing::SkeletonEventTracingData&
}

class "lola::GenericSkeletonEvent" #yellow {
+GenericSkeletonEvent(lola::Skeleton& parent, const SkeletonEventProperties& event_properties, const ElementFqId& event_fqn, const DataTypeMetaInfo& size_info, impl::tracing::SkeletonEventTracingData tracing_data)
+Send(score::mw::com::impl::SampleAllocateePtr<void> sample): ResultBlank
+Allocate(): Result<score::mw::com::impl::SampleAllocateePtr<void>>
+GetSizeInfo() const : std::pair<size_t, size_t>
+PrepareOffer(): ResultBlank
+PrepareStopOffer(): void
+GetBindingType(): BindingType
+SetSkeletonEventTracingData(impl::tracing::SkeletonEventTracingData tracing_data): void
+GetMaxSize() const : std::size_t
-event_shared_impl_ : lola::SkeletonEventCommon
}

class "DummySkeleton" <<generated>> {
+DummySkeleton(const InstanceIdentifier&, MethodCallProcessingMode)
+DummyEvent : events::DummyEvent
}

' Relationships
"score::mw::com::impl::SkeletonBase" *--> "SkeletonBinding"
"score::mw::com::impl::SkeletonBase" <|-- "DummySkeleton"
"score::mw::com::impl::SkeletonBase" <|-- "mw::com::impl::GenericSkeleton"
"mw::com::impl::GenericSkeleton" *-- "ServiceElementMap"
"SkeletonBindingFactory" ..> "SkeletonBinding" : creates
"SkeletonBinding" <|-- "lola::Skeleton"

"mw::com::impl::SkeletonEventBase" <|-- "mw::com::impl::SkeletonEvent<SampleType>"
"mw::com::impl::SkeletonEventBase" <|-- "mw::com::impl::GenericSkeletonEvent"

"SkeletonEventBindingBase" <|-- "SkeletonEventBinding<SampleType>"
"SkeletonEventBindingBase" <|-- "GenericSkeletonEventBinding"

"SkeletonEventBinding<SampleType>" <|-- "lola::SkeletonEvent<SampleType>"
"GenericSkeletonEventBinding" <|-- "lola::GenericSkeletonEvent"

"mw::com::impl::GenericSkeletonEvent" ..> "GenericSkeletonEventBindingFactory" : uses
"mw::com::impl::SkeletonEvent<SampleType>" ..> "GenericSkeletonEventBindingFactory" : uses

"lola::SkeletonEvent<SampleType>" *-- "lola::SkeletonEventCommon" : event_shared_impl_
"lola::GenericSkeletonEvent" *-- "lola::SkeletonEventCommon" : event_shared_impl_

"lola::SkeletonEventCommon" o-- "1" "lola::Skeleton"

"DummySkeleton" *--> "mw::com::impl::SkeletonEvent<SampleType>" : "0..n"
"mw::com::impl::GenericSkeleton" *--> "mw::com::impl::GenericSkeletonEvent" : "0..n"

@enduml
6 changes: 5 additions & 1 deletion score/mw/com/example/ipc_bridge/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ Params ParseCommandLineArguments(const int argc, const char** argv)
"Number of cycles that are executed before determining success or failure. 0 indicates no limit.");
options.add_options()("mode,m",
po::value<std::string>(),
"Set to either send/skeleton or recv/proxy to determine the role of the process");
"Set to: send/skeleton (typed skeleton), gen_skeleton (generic skeleton) or recv/proxy to determine the role of the process");
options.add_options()("cycle-time,t", po::value<std::size_t>(), "Cycle time in milliseconds for sending/polling");
options.add_options()(
"service_instance_manifest,s", po::value<std::string>(), "Path to the com configuration file");
Expand Down Expand Up @@ -119,6 +119,10 @@ int main(const int argc, const char** argv)
{
return event_sender_receiver.RunAsSkeleton(instance_specifier, cycle_time, cycles);
}
else if (mode == "gen_skeleton")
{
return event_sender_receiver.RunAsGenericSkeleton(instance_specifier, cycle_time, cycles);
}
else if (mode == "recv" || mode == "proxy")
{
return event_sender_receiver.RunAsProxy(instance_specifier, cycle_time, cycles, false, check_sample_hash);
Expand Down
Loading