Skip to content
Merged
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
14 changes: 14 additions & 0 deletions libs/bsw/middleware/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,17 @@ target_include_directories(
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>)

target_link_libraries(middlewareHeaders INTERFACE etl)

add_library(
middleware
src/middleware/core/ClusterConnection.cpp
src/middleware/core/ClusterConnectionBase.cpp
src/middleware/core/DatabaseManipulator.cpp
src/middleware/core/IClusterConnectionConfigurationBase.cpp
src/middleware/core/LoggerApi.cpp
src/middleware/core/ProxyBase.cpp
src/middleware/core/SkeletonBase.cpp)

target_include_directories(middleware PUBLIC include)

target_link_libraries(middleware PUBLIC etl)
8 changes: 4 additions & 4 deletions libs/bsw/middleware/doc/dd/core.rst
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ The core software unit of the middleware provides the key algorithms needed for
Middleware Message
------------------

Middleware is a service-oriented message passing system, and as such the ``MiddlewareMessage`` class is the fundamental unit of communication.
Middleware is a service-oriented message passing system, and as such the ``Message`` class is the fundamental unit of communication.
This class consists of a header and a payload.
The header has the following information:

Expand All @@ -19,11 +19,11 @@ The header has the following information:
* Address ID - identifies the recipient of the message within the destination cluster.

The middleware services' nodes will be scattered across different clusters in the ECU.
This means that messages can travel between clusters, and as such the ``MiddlewareMessage`` needs to have information about its origin and destination.
This means that messages can travel between clusters, and as such the ``Message`` needs to have information about its origin and destination.
Additionally, there may exist several possible recipients of a message, and as such, each recipient needs to have a unique identifier after system initialization.
To this end, the header contains the source cluster ID, target cluster ID, and address ID fields.

Finally, the payload contains the actual data being transmitted. This payload can be up to MAX_PAYLOAD_SIZE (currently 20 bytes long) and is stored directly within the ``MiddlewareMessage`` object.
If the payload exceeds this size, the middleware employs its own memory management system and stores an external handle within the ``MiddlewareMessage`` object.
Finally, the payload contains the actual data being transmitted. This payload can be up to MAX_PAYLOAD_SIZE (currently 20 bytes long) and is stored directly within the ``Message`` object.
If the payload exceeds this size, the middleware employs its own memory management system and stores an external handle within the ``Message`` object.
This handle contains information about the location and size of the payload in the middleware's memory region, as well as a flag indicating whether the payload is shared among multiple messages.
In case of internal errors, the payload can store an error code instead, which is delivered to any recipient waiting for a response.
2 changes: 1 addition & 1 deletion libs/bsw/middleware/doc/dd/queue.rst
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ Queue
=====

The queue folder contains a multi-producer single-consumer queue implementation, which is composed of a non-templated base class and a generic templated class.
This queue will be unique to each cluster in the middleware system, and will be used to store ``MiddlewareMessage`` objects.
This queue will be unique to each cluster in the middleware system, and will be used to store ``Message`` objects.
The queue must be declared with a ``QueueTraits`` type, which is a templated structure that contains the following members:

* T - the type that will be contained in the queue
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
// Copyright 2025 BMW AG

#pragma once

#include <cstdint>

namespace middleware
{
namespace concurrency
{

/**
* Suspend all interrupts.
* Platform-specific function that suspends all interrupts to ensure critical sections
* are protected. This is used in conjunction with lock strategies for thread-safe operations.
* The implementation must be provided for each platform integration.
*/
extern void suspendAllInterrupts();

/**
* Scoped lock for single-core protection.
* RAII-style lock that protects critical sections within a single core by disabling
* interrupts or using other single-core synchronization mechanisms. The lock is acquired in the
* constructor and automatically released in the destructor, ensuring proper cleanup even in the
* presence of exceptions.
* The implementation must be provided for each platform integration.
*/
struct ScopedCoreLock
{
/** Acquires the single-core lock. */
ScopedCoreLock();

/** Releases the single-core lock. */
~ScopedCoreLock();

ScopedCoreLock(ScopedCoreLock const&) = delete;
ScopedCoreLock& operator=(ScopedCoreLock const&) = delete;
};

/**
* Scoped lock for ECU-wide (multi-core) protection.
* RAII-style lock that protects critical sections across multiple cores in an ECU by
* using hardware-supported spinlocks or other multi-core synchronization mechanisms. The lock is
* acquired in the constructor and automatically released in the destructor, ensuring proper
* cleanup even in the presence of exceptions.
* The implementation must be provided for each platform integration.
*/
struct ScopedECULock
{
/** Acquires the ECU-wide lock using \p lock. */
ScopedECULock(uint8_t volatile* lock);

/** Releases the ECU-wide lock. */
~ScopedECULock();

ScopedECULock(ScopedECULock const&) = delete;
ScopedECULock& operator=(ScopedECULock const&) = delete;
};

} // namespace concurrency
} // namespace middleware
284 changes: 284 additions & 0 deletions libs/bsw/middleware/include/middleware/core/ClusterConnection.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,284 @@
// Copyright 2025 BMW AG

#pragma once

#include <etl/type_traits.h>

#include "middleware/core/ClusterConnectionBase.h"
#include "middleware/core/IClusterConnectionConfigurationBase.h"
#include "middleware/core/Message.h"

namespace middleware
{
namespace core
{
class ProxyBase;
class SkeletonBase;

/**
* Cluster connection for proxy-only communication without timeout support.
* This class provides a cluster connection that only supports proxy subscriptions,
* without timeout management. Skeleton subscriptions are not implemented and will return
* NotImplemented.
*/
class ClusterConnectionNoTimeoutProxyOnly final : public ClusterConnectionBase
{
using Base = ClusterConnectionBase;

public:
/** Constructs from \p configuration. */
explicit ClusterConnectionNoTimeoutProxyOnly(
IClusterConnectionConfigurationProxyOnly& configuration);

/** \see IClusterConnection::subscribe() */
HRESULT subscribe(ProxyBase& proxy, uint16_t const serviceInstanceId) final;

/** Not supported: this is a proxy-only connection. */
HRESULT subscribe(SkeletonBase&, uint16_t const) final { return HRESULT::NotImplemented; }

/** \see IClusterConnection::unsubscribe() */
void unsubscribe(ProxyBase& proxy, uint16_t const serviceId) final;

/** No-op: this is a proxy-only connection. */
void unsubscribe(SkeletonBase&, uint16_t const) final {}
};

/**
* Cluster connection for skeleton-only communication without timeout support.
* This class provides a cluster connection that only supports skeleton subscriptions,
* without timeout management. Proxy subscriptions are not implemented and will return
* NotImplemented.
*/
class ClusterConnectionNoTimeoutSkeletonOnly final : public ClusterConnectionBase
{
using Base = ClusterConnectionBase;

public:
/** Constructs from \p configuration. */
explicit ClusterConnectionNoTimeoutSkeletonOnly(
IClusterConnectionConfigurationSkeletonOnly& configuration);

/** Not supported: this is a skeleton-only connection. */
HRESULT subscribe(ProxyBase&, uint16_t const) final { return HRESULT::NotImplemented; }

/** \see IClusterConnection::subscribe() */
HRESULT subscribe(SkeletonBase& skeleton, uint16_t const serviceInstanceId) final;

/** No-op: this is a skeleton-only connection. */
void unsubscribe(ProxyBase&, uint16_t const) final {}

/** \see IClusterConnection::unsubscribe() */
void unsubscribe(SkeletonBase& skeleton, uint16_t const serviceId) final;
};

/**
* Cluster connection for bidirectional communication without timeout support.
* This class provides a cluster connection that supports both proxy and skeleton
* subscriptions, without timeout management. It enables full bidirectional communication
* between clusters.
*/
class ClusterConnectionNoTimeoutBidirectional final : public ClusterConnectionBase
{
using Base = ClusterConnectionBase;

public:
/** Constructs from \p configuration. */
explicit ClusterConnectionNoTimeoutBidirectional(
IClusterConnectionConfigurationBidirectional& configuration);

/** \see IClusterConnection::subscribe() */
HRESULT subscribe(ProxyBase& proxy, uint16_t const serviceInstanceId) final;

/** \see IClusterConnection::subscribe() */
HRESULT subscribe(SkeletonBase& skeleton, uint16_t const serviceInstanceId) final;

/** \see IClusterConnection::unsubscribe() */
void unsubscribe(ProxyBase& proxy, uint16_t const serviceId) final;

/** \see IClusterConnection::unsubscribe() */
void unsubscribe(SkeletonBase& skeleton, uint16_t const serviceId) final;
};

/**
* Cluster connection for bidirectional communication with timeout support.
* This class provides a cluster connection that supports both proxy and skeleton
* subscriptions, with timeout management capabilities. It enables full bidirectional
* communication between clusters with timeout tracking.
*/
class ClusterConnectionBidirectionalWithTimeout final : public ClusterConnectionTimeoutBase
{
using Base = ClusterConnectionTimeoutBase;

public:
/** Constructs from \p configuration. */
explicit ClusterConnectionBidirectionalWithTimeout(
IClusterConnectionConfigurationBidirectionalWithTimeout& configuration);

/** \see IClusterConnection::subscribe() */
HRESULT subscribe(ProxyBase& proxy, uint16_t const serviceInstanceId) final;

/** \see IClusterConnection::subscribe() */
HRESULT subscribe(SkeletonBase& skeleton, uint16_t const serviceInstanceId) final;

/** \see IClusterConnection::unsubscribe() */
void unsubscribe(ProxyBase& proxy, uint16_t const serviceId) final;

/** \see IClusterConnection::unsubscribe() */
void unsubscribe(SkeletonBase& skeleton, uint16_t const serviceId) final;
};

/**
* Cluster connection for proxy-only communication with timeout support.
* This class provides a cluster connection that only supports proxy subscriptions,
* with timeout management capabilities. Skeleton subscriptions are not implemented.
*/
class ClusterConnectionProxyOnlyWithTimeout final : public ClusterConnectionTimeoutBase
{
using Base = ClusterConnectionTimeoutBase;

public:
/** Constructs from \p configuration. */
explicit ClusterConnectionProxyOnlyWithTimeout(
IClusterConnectionConfigurationProxyOnlyWithTimeout& configuration);

/** \see IClusterConnection::subscribe() */
HRESULT subscribe(ProxyBase& proxy, uint16_t const serviceInstanceId) final;

/** Not supported: this is a proxy-only connection. */
HRESULT subscribe(SkeletonBase&, uint16_t const) final { return HRESULT::NotImplemented; }

/** \see IClusterConnection::unsubscribe() */
void unsubscribe(ProxyBase& proxy, uint16_t const serviceId) final;

/** No-op: this is a proxy-only connection. */
void unsubscribe(SkeletonBase&, uint16_t const) final {}
};

/**
* Cluster connection for skeleton-only communication with timeout support.
* This class provides a cluster connection that only supports skeleton subscriptions,
* with timeout management capabilities. Proxy subscriptions are not implemented.
*/
class ClusterConnectionSkeletonOnlyWithTimeout final : public ClusterConnectionTimeoutBase
{
using Base = ClusterConnectionTimeoutBase;

public:
/** Constructs from \p configuration. */
explicit ClusterConnectionSkeletonOnlyWithTimeout(
IClusterConnectionConfigurationSkeletonOnlyWithTimeout& configuration);

/** Not supported: this is a skeleton-only connection. */
HRESULT subscribe(ProxyBase&, uint16_t const) final { return HRESULT::NotImplemented; }

/** \see IClusterConnection::subscribe() */
HRESULT subscribe(SkeletonBase&, uint16_t const) final;

/** No-op: this is a skeleton-only connection. */
void
unsubscribe([[maybe_unused]] ProxyBase& proxy, [[maybe_unused]] uint16_t const serviceId) final
{}

/** \see IClusterConnection::unsubscribe() */
void unsubscribe(SkeletonBase&, [[maybe_unused]] uint16_t const) final;
};

/**
* Type selector for cluster connection implementations.
* This template struct selects the appropriate cluster connection type based on the
* configuration type provided. It uses SFINAE (Substitution Failure Is Not An Error) with
* enable_if to select the correct specialization. Instantiation without a valid configuration
* type will lead to a compilation error by design.
*
* \tparam T the configuration type
* \tparam Specialization SFINAE enabler parameter
*/
template<typename T, typename Specialization = void>
struct ClusterConnectionTypeSelector;

/**
* Type selector specialization for proxy-only configurations.
* \tparam T the proxy-only configuration type
*/
template<typename T>
struct ClusterConnectionTypeSelector<
T,
typename etl::enable_if<
etl::is_base_of<IClusterConnectionConfigurationProxyOnly, T>::value>::type>
{
/** The selected cluster connection type. */
using type = ClusterConnectionNoTimeoutProxyOnly;
};

/**
* Type selector specialization for skeleton-only configurations.
* \tparam T the skeleton-only configuration type
*/
template<typename T>
struct ClusterConnectionTypeSelector<
T,
typename etl::enable_if<
etl::is_base_of<IClusterConnectionConfigurationSkeletonOnly, T>::value>::type>
{
/** The selected cluster connection type. */
using type = ClusterConnectionNoTimeoutSkeletonOnly;
};

/**
* Type selector specialization for bidirectional configurations.
* \tparam T the bidirectional configuration type
*/
template<typename T>
struct ClusterConnectionTypeSelector<
T,
typename etl::enable_if<
etl::is_base_of<IClusterConnectionConfigurationBidirectional, T>::value>::type>
{
/** The selected cluster connection type. */
using type = ClusterConnectionNoTimeoutBidirectional;
};

/**
* Type selector specialization for proxy-only configurations with timeout.
* \tparam T the proxy-only with timeout configuration type
*/
template<typename T>
struct ClusterConnectionTypeSelector<
T,
typename etl::enable_if<
etl::is_base_of<IClusterConnectionConfigurationProxyOnlyWithTimeout, T>::value>::type>
{
/** The selected cluster connection type. */
using type = ClusterConnectionProxyOnlyWithTimeout;
};

/**
* Type selector specialization for skeleton-only configurations with timeout.
* \tparam T the skeleton-only with timeout configuration type
*/
template<typename T>
struct ClusterConnectionTypeSelector<
T,
typename etl::enable_if<
etl::is_base_of<IClusterConnectionConfigurationSkeletonOnlyWithTimeout, T>::value>::type>
{
/** The selected cluster connection type. */
using type = ClusterConnectionSkeletonOnlyWithTimeout;
};

/**
* Type selector specialization for bidirectional configurations with timeout.
* \tparam T the bidirectional with timeout configuration type
*/
template<typename T>
struct ClusterConnectionTypeSelector<
T,
typename etl::enable_if<
etl::is_base_of<IClusterConnectionConfigurationBidirectionalWithTimeout, T>::value>::type>
{
/** The selected cluster connection type. */
using type = ClusterConnectionBidirectionalWithTimeout;
};

} // namespace core
} // namespace middleware
Loading
Loading