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,42 @@ class "ServiceElementMapView<ElementType>" {
+empty() : bool
}

abstract class "score::mw::com::impl::SkeletonFieldBase" {
#skeleton_event_dispatch_ : std::unique_ptr<impl::SkeletonEventBase>
+PrepareOffer(): Result<void>
+UpdateSkeletonReference(skeleton_base: SkeletonBase&): 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>>
+UpdateSkeletonReference(skeleton_base: SkeletonBase&): void
.. Method Handlers (Stubs) ..
+RegisterGetHandler(std::function<std::vector<uint8_t>()> handler): Result<void>
+RegisterSetHandler(std::function<std::vector<uint8_t>(span<const uint8_t>)> handler): Result<void>
}

abstract class "mw::com::impl::SkeletonEventBase" #yellow {
+SkeletonEventBase(SkeletonBase&, const std::string_view, std::unique_ptr<SkeletonEventBindingBase>)
+OfferService(): Result<void>
+StopOfferService(): void
+UpdateSkeletonReference(skeleton_base: SkeletonBase&): 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 @@ -166,6 +197,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 @@ -191,5 +225,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 @@ -169,6 +170,7 @@ cc_library(
deps = [
":data_type_meta_info",
":generic_skeleton_event",
":generic_skeleton_field",
":instance_identifier",
":instance_specifier",
":runtime",
Expand Down Expand Up @@ -201,6 +203,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 @@ -1224,6 +1247,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
82 changes: 81 additions & 1 deletion score/mw/com/impl/generic_skeleton.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,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)
{
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 @@ -120,6 +141,59 @@ Result<GenericSkeleton> GenericSkeleton::Create(const InstanceIdentifier& identi
}
}

// 3. Create fields directly in the map
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);
}

auto field_binding_result =
GenericSkeletonEventBindingFactory::Create(skeleton, info.name, info.data_type_meta_info);

if (!field_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(field_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);
}

auto update_result = emplace_result.first->second.Update(info.initial_value);
if (!update_result.has_value())
{
score::mw::log::LogError("GenericSkeleton") << "Failed to set initial value for field: " << info.name;
return score::Unexpected(update_result.error());
}
}

return skeleton;
}

Expand All @@ -128,6 +202,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 @@ -140,7 +219,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
25 changes: 22 additions & 3 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,20 @@ 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};
score::cpp::span<const uint8_t> initial_value{};
};

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,7 +61,8 @@ 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`
Expand All @@ -72,8 +85,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 +105,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(skeleton_base, 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 @@ -28,10 +28,20 @@ class GenericSkeletonEventBinding;
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