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
Original file line number Diff line number Diff line change
Expand Up @@ -32,15 +32,18 @@ abstract class "SkeletonBinding" {

class "mw::com::impl::GenericSkeleton" #yellow {
using EventMapView = ServiceElementMapView<GenericSkeletonEvent>
using FieldMapView = ServiceElementMapView<GenericSkeletonField>
..
+Create(const InstanceIdentifier&, const GenericSkeletonServiceElementInfo&): Result<GenericSkeleton>
+Create(const InstanceSpecifier&, const GenericSkeletonServiceElementInfo&): Result<GenericSkeleton>
+GetEvents() : EventMapView
+GetFields(): FieldMapView
+OfferService(): Result<void>
+StopOfferService(): void
..
-GenericSkeleton(const InstanceIdentifier&, std::unique_ptr<SkeletonBinding>)
-events_ : std::unique_ptr<ServiceElementMapViewFactory<GenericSkeletonEvent>::map_type> events_
-fields_ : std::unique_ptr<ServiceElementMapViewFactory<GenericSkeletonField>::map_type>
}

class "lola::Skeleton" {
Expand Down Expand Up @@ -110,14 +113,38 @@ class "ServiceElementMapView<ElementType>" {
+empty() : bool
}

abstract class "score::mw::com::impl::SkeletonFieldBase" {
#skeleton_event_dispatch_ : std::unique_ptr<impl::SkeletonEventBase>
+PrepareOffer(): Result<void>
-{abstract} IsInitialValueSaved(): bool
-{abstract} DoDeferredUpdate(): Result<void>
-{abstract} IsSetHandlerMissing(): bool
}

class "mw::com::impl::GenericSkeletonField" #LightGreen {
-initial_field_value_ : std::vector<uint8_t>
-has_initial_value_ : bool
-has_getter_ : bool
-has_setter_ : bool
-has_notifier_ : bool
-is_set_handler_registered_ : bool
.. Notifier API ..
+Update(raw_value: span<const uint8_t>): Result<void>
+Update(SampleAllocateePtr<void> sample): Result<void>
+Allocate(): Result<SampleAllocateePtr<void>>
.. Method Handlers (Stubs) ..
+RegisterSetHandler(std::function<void(span<uint8_t>)> handler): Result<void>
}

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

class "mw::com::impl::GenericSkeletonEvent" #yellow {
+GenericSkeletonEvent(SkeletonBase&, const std::string_view, std::unique_ptr<GenericSkeletonEventBinding>)
+GenericSkeletonEvent(SkeletonBase&, const std::string_view, std::unique_ptr<GenericSkeletonEventBinding>, FieldOnlyConstructorEnabler)
+Send(SampleAllocateePtr<void> sample): Result<void>
+Allocate(): Result<SampleAllocateePtr<void>>
+GetSizeInfo() const : DataTypeMetaInfo
Expand Down Expand Up @@ -175,6 +202,9 @@ class "DummySkeleton" <<generated>> {
"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" *-- "ServiceElementMapView"
"score::mw::com::impl::SkeletonFieldBase" <|-- "mw::com::impl::GenericSkeletonField"
"SkeletonFieldBase" "1" *-- "1" "mw::com::impl::SkeletonEventBase" : skeleton_event_dispatch_
"SkeletonBindingFactory" ..> "SkeletonBinding" : creates
"SkeletonBinding" <|-- "lola::Skeleton"

Expand All @@ -200,5 +230,6 @@ class "DummySkeleton" <<generated>> {
"mw::com::impl::GenericSkeleton" ..> "ServiceElementMapViewFactory<ElementType>" : uses
"ServiceElementMapViewFactory<ElementType>" ..> "ServiceElementMapView<ElementType>" : creates
"ServiceElementMapView<ElementType>" o-- "mw::com::impl::GenericSkeletonEvent" : "0..n"
"mw::com::impl::GenericSkeleton" *--> "mw::com::impl::GenericSkeletonField" : "0..n"

@enduml
41 changes: 41 additions & 0 deletions score/mw/com/impl/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ cc_library(
":generic_proxy",
":generic_proxy_event",
":generic_skeleton",
":generic_skeleton_field",
":proxy_event",
":proxy_field",
":skeleton_event",
Expand Down Expand Up @@ -173,6 +174,7 @@ cc_library(
deps = [
":data_type_meta_info",
":generic_skeleton_event",
":generic_skeleton_field",
":instance_identifier",
":instance_specifier",
":service_element_map_view",
Expand Down Expand Up @@ -209,6 +211,27 @@ cc_library(
],
)

cc_library(
name = "generic_skeleton_field",
srcs = ["generic_skeleton_field.cpp"],
hdrs = ["generic_skeleton_field.h"],
features = COMPILER_WARNING_FEATURES,
tags = ["FFI"],
visibility = [
"//score/mw/com:__subpackages__",
],
deps = [
":error",
":generic_skeleton_event",
":skeleton_base",
":skeleton_field_base",
"//score/mw/com/impl/plumbing:sample_allocatee_ptr",
"@score_baselibs//score/language/futurecpp",
"@score_baselibs//score/mw/log",
"@score_baselibs//score/result",
],
)

cc_library(
name = "generic_skeleton_event_binding",
hdrs = [
Expand Down Expand Up @@ -1401,6 +1424,24 @@ cc_unit_test(
],
)

cc_unit_test(
name = "generic_skeleton_field_test",
srcs = ["generic_skeleton_field_test.cpp"],
deps = [
":generic_skeleton",
":generic_skeleton_field",
":runtime_mock",
":service_discovery_client_mock",
":service_discovery_mock",
"//score/mw/com/impl/bindings/mock_binding:generic_skeleton_event",
"//score/mw/com/impl/plumbing:generic_skeleton_event_binding_factory",
"//score/mw/com/impl/plumbing:generic_skeleton_event_binding_factory_mock",
"//score/mw/com/impl/test:binding_factory_resources",
"//score/mw/com/impl/test:dummy_instance_identifier_builder",
"//score/mw/com/impl/test:runtime_mock_guard",
],
)

cc_unit_test(
name = "traits_test",
srcs = [
Expand Down
84 changes: 83 additions & 1 deletion score/mw/com/impl/generic_skeleton.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,27 @@ std::string_view GetEventName(const InstanceIdentifier& identifier, std::string_

return std::visit(visitor, service_type_deployment.binding_info_);
}

// Helper to fetch the stable field name from the Configuration
std::string_view GetFieldName(const InstanceIdentifier& identifier, std::string_view search_name)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess you need to extend generic_skeleton_test.cpp for testing this?

{
const auto& service_type_deployment = InstanceIdentifierView{identifier}.GetServiceTypeDeployment();

auto visitor = score::cpp::overload(
[&](const LolaServiceTypeDeployment& deployment) -> std::string_view {
const auto it = deployment.fields_.find(std::string{search_name});
if (it != deployment.fields_.end())
{
return it->first; // Return the stable address of the Key from the Config Map
}
return {};
},
[](const score::cpp::blank&) noexcept -> std::string_view {
return {};
});

return std::visit(visitor, service_type_deployment.binding_info_);
}
} // namespace

Result<GenericSkeleton> GenericSkeleton::Create(const InstanceSpecifier& specifier,
Expand Down Expand Up @@ -134,6 +155,61 @@ Result<GenericSkeleton> GenericSkeleton::Create(const InstanceIdentifier& identi
}
}

// 3. Create fields directly in the map

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess you need to extend generic_skeleton_test.cpp for testing this extended Create() functionality?

for (const auto& info : in.fields)
{
// Check for duplicates
if (skeleton.fields_->find(info.name) != skeleton.fields_->cend())
{
score::mw::log::LogError("GenericSkeleton") << "Duplicate field name provided: " << info.name;
return MakeUnexpected(ComErrc::kServiceElementAlreadyExists);
}

std::string_view stable_name = GetFieldName(identifier, info.name);

if (stable_name.empty())
{
score::mw::log::LogError("GenericSkeleton") << "Field name not found in configuration: " << info.name;
return MakeUnexpected(ComErrc::kBindingFailure);
}

// Validate & convert the public DataTypeMetaInfo into the internal DataTypeSizeInfo.
auto data_type_size_info_result = MakeDataTypeSizeInfo(info.data_type_meta_info);
if (!data_type_size_info_result.has_value())
{
score::mw::log::LogError("GenericSkeleton")
<< "Invalid data type meta-info provided for field: " << info.name;
return MakeUnexpected(ComErrc::kInvalidConfiguration);
}

auto event_binding_result =
GenericSkeletonEventBindingFactory::Create(skeleton, info.name, data_type_size_info_result.value());

if (!event_binding_result.has_value())
{
return MakeUnexpected(ComErrc::kBindingFailure);
}

// Use the hidden constructor tag so the event doesn't register itself in the events_ map
auto generic_event =
std::make_unique<GenericSkeletonEvent>(skeleton,
stable_name,
std::move(event_binding_result).value(),
GenericSkeletonEvent::FieldOnlyConstructorEnabler{});

const auto emplace_result = skeleton.fields_->emplace(
std::piecewise_construct,
std::forward_as_tuple(stable_name),
std::forward_as_tuple(
skeleton, stable_name, std::move(generic_event), info.has_getter, info.has_setter, info.has_notifier));

if (!emplace_result.second)
{
score::mw::log::LogError("GenericSkeleton") << "Failed to emplace field in map: " << info.name;
return MakeUnexpected(ComErrc::kBindingFailure);
}
}

return skeleton;
}

Expand All @@ -142,6 +218,11 @@ ServiceElementMapView<GenericSkeletonEvent> GenericSkeleton::GetEvents() const n
return ServiceElementMapViewFactory<GenericSkeletonEvent>::Create(*events_);
}

GenericSkeleton::FieldMapView GenericSkeleton::GetFields() const noexcept
{
return ServiceElementMapViewFactory<GenericSkeletonField>::Create(*fields_);
}

Result<void> GenericSkeleton::OfferService() noexcept
{
return SkeletonBase::OfferService();
Expand All @@ -154,7 +235,8 @@ void GenericSkeleton::StopOfferService() noexcept

GenericSkeleton::GenericSkeleton(const InstanceIdentifier& identifier, std::unique_ptr<SkeletonBinding> binding)
: SkeletonBase(std::move(binding), identifier),
events_(std::make_unique<std::map<std::string_view, GenericSkeletonEvent>>())
events_(std::make_unique<std::map<std::string_view, GenericSkeletonEvent>>()),
fields_(std::make_unique<std::map<std::string_view, GenericSkeletonField>>())
{
}

Expand Down
26 changes: 21 additions & 5 deletions score/mw/com/impl/generic_skeleton.h
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@

#include "score/mw/com/impl/data_type_meta_info.h"
#include "score/mw/com/impl/generic_skeleton_event.h"
#include "score/mw/com/impl/generic_skeleton_field.h"
#include "score/mw/com/impl/instance_identifier.h"
#include "score/mw/com/impl/instance_specifier.h"
#include "score/mw/com/impl/service_element_map_view.h"
Expand All @@ -35,9 +36,19 @@ struct EventInfo
DataTypeMetaInfo data_type_meta_info;
};

struct FieldInfo
{
std::string_view name;
DataTypeMetaInfo data_type_meta_info;
bool has_getter{false};
bool has_setter{false};
bool has_notifier{false};
};

struct GenericSkeletonServiceElementInfo
{
score::cpp::span<const EventInfo> events{};
score::cpp::span<const FieldInfo> fields{};
};

/// @brief Represents a type-erased, runtime-configurable skeleton for a service instance.
Expand All @@ -49,14 +60,13 @@ class GenericSkeleton : public SkeletonBase
{
public:
using EventMapView = ServiceElementMapView<GenericSkeletonEvent>;
/// \brief Creates a GenericSkeleton and all its service elements (events + fields) atomically.
using FieldMapView = ServiceElementMapView<GenericSkeletonField>;
/// @brief Creates a GenericSkeleton and all its service elements (events + fields) atomically.
///
/// \contract
/// - Empty spans are allowed for `in.events` and/or `in.fields`
/// - Each provided name must exist in the binding deployment for this instance (events/fields respectively).
/// - All element names must be unique across all element kinds within this skeleton.
/// - For each field, `initial_value_bytes` must be non-empty and
/// `initial_value_bytes.size()` must be <= `size_info.size`.
/// - On error, no partially-created elements are left behind.
[[nodiscard]] static Result<GenericSkeleton> Create(const InstanceIdentifier& identifier,
const GenericSkeletonServiceElementInfo& in);
Expand All @@ -72,8 +82,12 @@ class GenericSkeleton : public SkeletonBase
/// \note The returned view is valid as long as the GenericSkeleton lives.
[[nodiscard]] EventMapView GetEvents() const noexcept;

/// \brief Offers the service instance.
/// \return A blank result, or an error if offering fails.
/// @brief Returns a read-only view to the name-keyed map of fields.
/// @note The returned view is valid as long as the GenericSkeleton lives.
[[nodiscard]] FieldMapView GetFields() const noexcept;

/// @brief Offers the service instance.
/// @return A blank result, or an error if offering fails.
[[nodiscard]] Result<void> OfferService() noexcept;

/// \brief Stops offering the service instance.
Expand All @@ -88,6 +102,8 @@ class GenericSkeleton : public SkeletonBase
/// GenericSkeleton. This is required as we hand out views to this map (see GetEvents()), which need to be valid
/// even after the GenericSkeleton instance has been moved.
std::unique_ptr<ServiceElementMapViewFactory<GenericSkeletonEvent>::map_type> events_;
/// @brief This map owns all GenericSkeletonField instances.
std::unique_ptr<ServiceElementMapViewFactory<GenericSkeletonField>::map_type> fields_;
};
} // namespace score::mw::com::impl

Expand Down
23 changes: 23 additions & 0 deletions score/mw/com/impl/generic_skeleton_event.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,29 @@ GenericSkeletonEvent::GenericSkeletonEvent(SkeletonBase& skeleton_base,
}
}

GenericSkeletonEvent::GenericSkeletonEvent(SkeletonBase& skeleton_base,
const std::string_view event_name,
std::unique_ptr<GenericSkeletonEventBinding> binding,
FieldOnlyConstructorEnabler /*tag*/)
: SkeletonEventBase(event_name, std::move(binding))
{
// Intentionally omitting SkeletonBaseView{skeleton_base}.RegisterEvent(event_name, *this);

if (binding_ != nullptr)
{
const SkeletonBaseView skeleton_base_view{skeleton_base};
const auto& instance_identifier = skeleton_base_view.GetAssociatedInstanceIdentifier();
auto* const binding_ptr = static_cast<GenericSkeletonEventBinding*>(binding_.get());
if (binding_ptr)
{
const auto binding_type = binding_ptr->GetBindingType();
auto tracing_data =
tracing::GenerateSkeletonTracingStructFromEventConfig(instance_identifier, binding_type, event_name);
binding_ptr->SetSkeletonEventTracingData(tracing_data);
}
}
}

Result<void> GenericSkeletonEvent::Send(SampleAllocateePtr<void> sample) noexcept
{
if (!service_offered_flag_.IsSet())
Expand Down
10 changes: 10 additions & 0 deletions score/mw/com/impl/generic_skeleton_event.h
Original file line number Diff line number Diff line change
Expand Up @@ -31,10 +31,20 @@ class SkeletonBase;
class GenericSkeletonEvent : public SkeletonEventBase
{
public:
struct FieldOnlyConstructorEnabler
{
explicit FieldOnlyConstructorEnabler() = default;
};

GenericSkeletonEvent(SkeletonBase& skeleton_base,
const std::string_view event_name,
std::unique_ptr<GenericSkeletonEventBinding> binding);

GenericSkeletonEvent(SkeletonBase& skeleton_base,
const std::string_view event_name,
std::unique_ptr<GenericSkeletonEventBinding> binding,
FieldOnlyConstructorEnabler tag);

Result<void> Send(SampleAllocateePtr<void> sample) noexcept;

Result<SampleAllocateePtr<void>> Allocate() noexcept;
Expand Down
Loading
Loading