diff --git a/MODULE.bazel b/MODULE.bazel index 090786ea..92104d59 100644 --- a/MODULE.bazel +++ b/MODULE.bazel @@ -229,6 +229,8 @@ use_repo(car_window_sim_crate, "car_window_sim_crate") # ============================================================================ # Rules for Foreign/C/C++/CMake integration bazel_dep(name = "rules_foreign_cc", version = "0.15.1") +bazel_dep(name = "nlohmann_json", version = "3.11.3") + single_version_override( module_name = "rules_foreign_cc", patch_strip = 1, diff --git a/src/gatewayd/etc/gatewayd_config.json b/src/gatewayd/etc/gatewayd_config.json index 82f9ff32..9b230fc7 100644 --- a/src/gatewayd/etc/gatewayd_config.json +++ b/src/gatewayd/etc/gatewayd_config.json @@ -1,17 +1,21 @@ { "local_service_instances": [ { + "someip_service_id": 4660, "instance_specifier": "gatewayd/application_window_control", "events": [ { + "someip_method_id": 34680, "event_name": "window_control" } ] }, { + "someip_service_id": 4660, "instance_specifier": "gatewayd/application_echo_request", "events": [ { + "someip_method_id": 34680, "event_name": "echo_request_tiny" } ] diff --git a/src/someipd/BUILD.bazel b/src/someipd/BUILD.bazel index f6828b9f..828fd0eb 100644 --- a/src/someipd/BUILD.bazel +++ b/src/someipd/BUILD.bazel @@ -16,27 +16,99 @@ This daemon contains the SOME/IP communication stack and is QM """ load("@rules_cc//cc:cc_binary.bzl", "cc_binary") +load("@rules_cc//cc:cc_library.bzl", "cc_library") load("@score_communication//bazel/tools:json_schema_validator.bzl", "validate_json_schema_test") -# ============================================================================ -# Main Binary -# ============================================================================ +# Export config files for deployment +exports_files( + [ + "etc/mw_com_config.json", + "etc/someipd_config.json", + ], + visibility = ["//visibility:public"], +) + +# Core Types (stack-independent SOME/IP types and constants) +cc_library( + name = "someipd_types", + hdrs = ["someipd_types.h"], +) + +cc_library( + name = "i_network_stack", + hdrs = ["i_network_stack.h"], + deps = [":someipd_types"], +) + +cc_library( + name = "i_internal_ipc", + hdrs = ["i_internal_ipc.h"], +) + +cc_library( + name = "someipd_config", + srcs = ["someipd_config_reader.cpp"], + hdrs = ["someipd_config.h"], + deps = [ + ":someipd_types", + "@nlohmann_json//:json", + ], +) + +cc_library( + name = "gateway_routing", + srcs = ["gateway_routing.cpp"], + hdrs = ["gateway_routing.h"], + deps = [ + ":i_internal_ipc", + ":i_network_stack", + ":someipd_config", + "@score_baselibs//score/mw/log", + ], +) + +cc_library( + name = "vsomeip_adapter", + srcs = ["vsomeip_adapter.cpp"], + hdrs = ["vsomeip_adapter.h"], + deps = [ + ":i_network_stack", + "@vsomeip", + ], +) + +cc_library( + name = "mwcom_adapter", + srcs = ["mwcom_adapter.cpp"], + hdrs = ["mwcom_adapter.h"], + deps = [ + ":i_internal_ipc", + "//src/network_service:provider", + "@score_baselibs//score/mw/log", + "@score_communication//score/mw/com", + ], +) + cc_binary( name = "someipd", srcs = ["main.cpp"], args = [ "-service_instance_manifest", "$(rootpath etc/mw_com_config.json)", + "-someipd_config", + "$(rootpath etc/someipd_config.json)", ], data = [ "etc/mw_com_config.json", + "etc/someipd_config.json", ], visibility = ["//visibility:public"], deps = [ - "//src/network_service:provider", - "@score_baselibs//score/language/futurecpp", - "@score_communication//score/mw/com", - "@vsomeip", + ":gateway_routing", + ":mwcom_adapter", + ":someipd_config", + ":vsomeip_adapter", + "@score_baselibs//score/mw/log", ], ) diff --git a/src/someipd/etc/someipd_config.json b/src/someipd/etc/someipd_config.json new file mode 100644 index 00000000..c2d25c6a --- /dev/null +++ b/src/someipd/etc/someipd_config.json @@ -0,0 +1,27 @@ +{ + "offered_services": [ + { + "service_id": "0x1234", + "instance_id": "0x5678", + "unreliable_port": 31000, + "events": [ + { + "event_id": "0x8778", + "eventgroup_id": "0x4465" + } + ] + } + ], + "subscribed_services": [ + { + "service_id": "0x4321", + "instance_id": "0x5678", + "events": [ + { + "event_id": "0x8778", + "eventgroup_id": "0x4465" + } + ] + } + ] +} diff --git a/src/someipd/gateway_routing.cpp b/src/someipd/gateway_routing.cpp new file mode 100644 index 00000000..0eae9ba5 --- /dev/null +++ b/src/someipd/gateway_routing.cpp @@ -0,0 +1,140 @@ +/******************************************************************************** + * 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 + ********************************************************************************/ + +#include "src/someipd/gateway_routing.h" + +#include +#include + +#include "score/mw/log/logging.h" + +namespace score::someip_gateway::someipd { + +GatewayRouting::GatewayRouting(std::unique_ptr network_stack, + std::unique_ptr internal_ipc, SomeipDConfig config) + : config_(std::move(config)), + internal_ipc_(std::move(internal_ipc)), + network_stack_(std::move(network_stack)) {} + +void GatewayRouting::SetupSubscriptions() { + for (const auto& svc : config_.subscribed_services) { + for (const auto& ev : svc.events) { + score::mw::log::LogInfo() << "Registering message handler for service " + << score::mw::log::LogHex16{svc.service_id} << ":" + << score::mw::log::LogHex16{svc.instance_id} << " event " + << score::mw::log::LogHex16{ev.event_id}; + + network_stack_->RegisterMessageHandler( + svc.service_id, svc.instance_id, ev.event_id, + [this](ServiceId /*service_id*/, InstanceId /*instance_id*/, EventId /*event_id*/, + const std::byte* payload_data, std::size_t payload_size) { + const std::size_t total_size = kSomeipFullHeaderSize + payload_size; + if (total_size > kMaxMessageSize) { + score::mw::log::LogError() + << "Message too large (" << static_cast(total_size) + << " bytes). Dropping."; + return; + } + std::byte buffer[kMaxMessageSize] = {}; + std::memcpy(buffer + kSomeipFullHeaderSize, payload_data, payload_size); + internal_ipc_->SendToGatewayd(buffer, total_size); + }); + } + + network_stack_->RequestService(svc.service_id, svc.instance_id); + for (const auto& ev : svc.events) { + network_stack_->SubscribeEvent(svc.service_id, svc.instance_id, ev.event_id, + ev.eventgroup_id); + } + } +} + +void GatewayRouting::SetupOfferings() { + // Order matters: offer_event must come before offer_service. + for (const auto& svc : config_.offered_services) { + for (const auto& ev : svc.events) { + score::mw::log::LogInfo() + << "Offering event " << score::mw::log::LogHex16{svc.service_id} << ":" + << score::mw::log::LogHex16{svc.instance_id} << " event " + << score::mw::log::LogHex16{ev.event_id} << " in event group " + << score::mw::log::LogHex16{ev.eventgroup_id}; + network_stack_->OfferEvent(svc.service_id, svc.instance_id, ev.event_id, + ev.eventgroup_id); + } + score::mw::log::LogInfo() << "Offering service " << score::mw::log::LogHex16{svc.service_id} + << ":" << score::mw::log::LogHex16{svc.instance_id}; + network_stack_->OfferService(svc.service_id, svc.instance_id); + } +} + +InstanceId GatewayRouting::LookupInstanceId(ServiceId service_id) const { + for (const auto& svc : config_.offered_services) { + if (svc.service_id == service_id) { + return svc.instance_id; + } + } + return kAnyInstance; +} +// exchange event data +void GatewayRouting::ProcessMessages(std::atomic& shutdown_requested) { + static constexpr std::size_t kMaxSampleCount = 10; + + score::mw::log::LogInfo() << "SOME/IP daemon started, waiting for messages..."; + + while (!shutdown_requested.load()) { + // TODO: Use ReceiveHandler + async runtime instead of polling + internal_ipc_->ReceiveFromGatewayd( + [this](const std::byte* data, std::size_t size) { + if (size < kSomeipFullHeaderSize) { + score::mw::log::LogError() + << "Received too small sample (size: " << static_cast(size) + << ", expected at least: " + << static_cast(kSomeipFullHeaderSize) + << "). Skipping message."; + return; + } + + // Read service_id and event_id from the SOME/IP header (big-endian) + const auto service_id = static_cast( + (std::to_integer(data[0]) << 8) | std::to_integer(data[1])); + const auto event_id = static_cast( + (std::to_integer(data[2]) << 8) | std::to_integer(data[3])); + + const auto instance_id = LookupInstanceId(service_id); + if (instance_id == kAnyInstance) { + score::mw::log::LogError() + << "No offered service configured for service_id " + << score::mw::log::LogHex16{service_id} << ". Dropping message."; + return; + } + + const auto* payload = data + kSomeipFullHeaderSize; + const auto payload_size = size - kSomeipFullHeaderSize; + network_stack_->Notify(service_id, instance_id, event_id, payload, payload_size); + }, + kMaxSampleCount); + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + } + + score::mw::log::LogInfo() << "Shutting down SOME/IP daemon..."; +} + +void GatewayRouting::Run(std::atomic& shutdown_requested) { + network_stack_->StartProcessing(); + SetupSubscriptions(); // once per service, not per message + SetupOfferings(); // once per service, not per message + ProcessMessages(shutdown_requested); + network_stack_->StopProcessing(); +} + +} // namespace score::someip_gateway::someipd diff --git a/src/someipd/gateway_routing.h b/src/someipd/gateway_routing.h new file mode 100644 index 00000000..81ce40fb --- /dev/null +++ b/src/someipd/gateway_routing.h @@ -0,0 +1,53 @@ +/******************************************************************************** + * 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 + ********************************************************************************/ + +#pragma once + +#include +#include + +#include "src/someipd/i_internal_ipc.h" +#include "src/someipd/i_network_stack.h" +#include "src/someipd/someipd_config.h" + +namespace score::someip_gateway::someipd { + +/// Bridge abstraction that decouples the routing logic from both the SOME/IP +/// network stack and the internal IPC framework. +/// +/// Abstraction:GatewayRouting +/// Implementor: IInternalIpc and INetworkStack (Abstract interfaces) +/// Concrete Implementor (A/B): MwcomAdapter and VsomeipAdapter. +/// Delegation: network_stack_->RequestService(...) or internal_ipc_->SendToGatewayd(...), ... etc +/// Client: main.cpp instantiates GatewayRouting() and calls Run() + +class GatewayRouting { + public: + GatewayRouting(std::unique_ptr network_stack, + std::unique_ptr internal_ipc, SomeipDConfig config); + + /// Run the routing loop. Blocks until shutdown_requested becomes true. + void Run(std::atomic& shutdown_requested); + + private: + void SetupSubscriptions(); + void SetupOfferings(); + void ProcessMessages(std::atomic& shutdown_requested); + InstanceId LookupInstanceId(ServiceId service_id) const; + + SomeipDConfig config_; + std::unique_ptr internal_ipc_; + std::unique_ptr network_stack_; +}; + +} // namespace score::someip_gateway::someipd diff --git a/src/someipd/i_internal_ipc.h b/src/someipd/i_internal_ipc.h new file mode 100644 index 00000000..5f8fd04d --- /dev/null +++ b/src/someipd/i_internal_ipc.h @@ -0,0 +1,42 @@ +/******************************************************************************** + * 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 + ********************************************************************************/ + +#pragma once + +#include +#include + +namespace score::someip_gateway::someipd { + +/// Abstract interface for internal IPC between someipd and gatewayd. +/// +/// Adapters implement this interface to isolate the core routing logic from the +/// concrete IPC framework being used (e.g., mw::com, plain sockets, etc.). +class IInternalIpc { + public: + virtual ~IInternalIpc() = default; + + /// Initialize the IPC runtime, discover services, and establish connections. + virtual void Init(int argc, const char* argv[]) = 0; + + /// Send a raw SOME/IP message to gatewayd (inbound path: network → someipd → gatewayd). + virtual bool SendToGatewayd(const std::byte* data, std::size_t size) = 0; + + /// Callback signature for messages received from gatewayd. + using MessageCallback = std::function; + + /// Poll for new messages from gatewayd (outbound path: gatewayd → someipd → network). + virtual void ReceiveFromGatewayd(MessageCallback callback, std::size_t max_count) = 0; +}; + +} // namespace score::someip_gateway::someipd diff --git a/src/someipd/i_network_stack.h b/src/someipd/i_network_stack.h new file mode 100644 index 00000000..1e64589d --- /dev/null +++ b/src/someipd/i_network_stack.h @@ -0,0 +1,70 @@ +/******************************************************************************** + * 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 + ********************************************************************************/ + +#pragma once + +#include +#include + +#include "src/someipd/someipd_types.h" + +namespace score::someip_gateway::someipd { + +/// Abstract interface for a SOME/IP network stack (e.g., vsomeip, Xsomeip, etc.). +/// +/// Adapters implement this interface to isolate the core routing logic from the +/// concrete SOME/IP stack being used. Swapping the network stack only requires +/// providing a different adapter — no changes to GatewayRouting or main. +class INetworkStack { + public: + virtual ~INetworkStack() = default; + + /// Initialize the network stack (create application, configure transport, etc.). + virtual bool Init() = 0; + + /// Start processing network messages (non-blocking). + /// After this call, registered message handlers may be invoked. + virtual void StartProcessing() = 0; + + /// Stop processing network messages and release resources. + virtual void StopProcessing() = 0; + + // -- Offering services to the SOME/IP network (outbound: IPC → network) -- + + virtual void OfferEvent(ServiceId service_id, InstanceId instance_id, EventId event_id, + EventGroupId eventgroup_id) = 0; + + virtual void OfferService(ServiceId service_id, InstanceId instance_id) = 0; + + /// Send an event notification to the SOME/IP network. + virtual void Notify(ServiceId service_id, InstanceId instance_id, EventId event_id, + const std::byte* payload_data, std::size_t payload_size) = 0; + + // -- Subscribing to services from the SOME/IP network (inbound: network → IPC) -- + + virtual void RequestService(ServiceId service_id, InstanceId instance_id) = 0; + + virtual void SubscribeEvent(ServiceId service_id, InstanceId instance_id, EventId event_id, + EventGroupId eventgroup_id) = 0; + + /// Callback signature for received SOME/IP messages. + using MessageHandler = + std::function; + + /// Register a handler that will be called when a matching message arrives. + virtual void RegisterMessageHandler(ServiceId service_id, InstanceId instance_id, + EventId event_id, MessageHandler handler) = 0; +}; + +} // namespace score::someip_gateway::someipd diff --git a/src/someipd/main.cpp b/src/someipd/main.cpp index f62fba20..5c300919 100644 --- a/src/someipd/main.cpp +++ b/src/someipd/main.cpp @@ -14,159 +14,59 @@ #include #include #include -#include -#include -#include -#include -#include +#include -#include "score/mw/com/runtime.h" -#include "score/span.hpp" -#include "src/network_service/interfaces/message_transfer.h" +#include "score/mw/log/logging.h" +#include "src/someipd/gateway_routing.h" +#include "src/someipd/mwcom_adapter.h" +#include "src/someipd/someipd_config.h" +#include "src/someipd/vsomeip_adapter.h" -const char* someipd_name = "someipd"; +using score::someip_gateway::someipd::GatewayRouting; +using score::someip_gateway::someipd::MwcomAdapter; +using score::someip_gateway::someipd::SomeipDConfig; +using score::someip_gateway::someipd::VsomeipAdapter; -static const vsomeip::service_t service_id = 0x1111; -static const vsomeip::instance_t service_instance_id = 0x2222; -static const vsomeip::method_t service_method_id = 0x3333; - -static const std::size_t max_sample_count = 10; - -#define SAMPLE_SERVICE_ID 0x1234 -#define RESPONSE_SAMPLE_SERVICE_ID 0x4321 -#define SAMPLE_INSTANCE_ID 0x5678 -#define SAMPLE_METHOD_ID 0x0421 - -#define SAMPLE_EVENT_ID 0x8778 -#define SAMPLE_GET_METHOD_ID 0x0001 -#define SAMPLE_SET_METHOD_ID 0x0002 - -#define SAMPLE_EVENTGROUP_ID 0x4465 - -#define OTHER_SAMPLE_SERVICE_ID 0x0248 -#define OTHER_SAMPLE_INSTANCE_ID 0x5422 -#define OTHER_SAMPLE_METHOD_ID 0x1421 - -using score::someip_gateway::network_service::interfaces::message_transfer:: - SomeipMessageTransferProxy; -using score::someip_gateway::network_service::interfaces::message_transfer:: - SomeipMessageTransferSkeleton; - -// Global flag to control application shutdown static std::atomic shutdown_requested{false}; -// Signal handler for graceful shutdown -void termination_handler(int /*signal*/) { - std::cout << "Received termination signal. Initiating graceful shutdown..." << std::endl; - shutdown_requested.store(true); +void termination_handler(int /*signal*/) { shutdown_requested.store(true); } + +std::optional ParseConfigPath(int argc, const char* argv[]) { + for (int i = 1; i < argc - 1; ++i) { + if (std::string_view{argv[i]} == "-someipd_config") { + return argv[i + 1]; + } + } + return std::nullopt; } int main(int argc, const char* argv[]) { - // Register signal handlers for graceful shutdown + score::mw::log::LogInfo() << "Starting SOME/IP daemon..."; std::signal(SIGTERM, termination_handler); std::signal(SIGINT, termination_handler); - score::mw::com::runtime::InitializeRuntime(argc, argv); - - auto runtime = vsomeip::runtime::get(); - auto application = runtime->create_application(someipd_name); - if (!application->init()) { - std::cerr << "App init failed"; - return 1; + auto someipd_config_path = ParseConfigPath(argc, argv); + if (!someipd_config_path.has_value()) { + score::mw::log::LogError() << "Mandatory argument '-someipd_config' is missing."; + return EXIT_FAILURE; } - std::thread([application]() { - auto handles = - SomeipMessageTransferProxy::FindService( - score::mw::com::InstanceSpecifier::Create(std::string("someipd/gatewayd_messages")) - .value()) - .value(); + SomeipDConfig config{}; + config = score::someip_gateway::someipd::ReadSomeipDConfig(std::string(someipd_config_path.value())); - { // Proxy for receiving messages from gatewayd to be sent via SOME/IP - auto proxy = SomeipMessageTransferProxy::Create(handles.front()).value(); - proxy.message_.Subscribe(max_sample_count); - - // Skeleton for transmitting messages from the network to gatewayd - auto create_result = SomeipMessageTransferSkeleton::Create( - score::mw::com::InstanceSpecifier::Create(std::string("someipd/someipd_messages")) - .value()); - // TODO: Error handling - auto skeleton = std::move(create_result).value(); - (void)skeleton.OfferService(); - - application->register_message_handler( - RESPONSE_SAMPLE_SERVICE_ID, SAMPLE_INSTANCE_ID, SAMPLE_EVENT_ID, - [&skeleton](const std::shared_ptr& msg) { - auto maybe_message = skeleton.message_.Allocate(); - if (!maybe_message.has_value()) { - std::cerr << "Failed to allocate SOME/IP message:" - << maybe_message.error().Message() << std::endl; - return; - } - auto message_sample = std::move(maybe_message).value(); - memcpy(message_sample->data + VSOMEIP_FULL_HEADER_SIZE, - msg->get_payload()->get_data(), msg->get_payload()->get_length()); - message_sample->size = - msg->get_payload()->get_length() + VSOMEIP_FULL_HEADER_SIZE; - skeleton.message_.Send(std::move(message_sample)); - }); - - application->request_service(RESPONSE_SAMPLE_SERVICE_ID, SAMPLE_INSTANCE_ID); - std::set its_groups; - its_groups.insert(SAMPLE_EVENTGROUP_ID); - application->request_event(RESPONSE_SAMPLE_SERVICE_ID, SAMPLE_INSTANCE_ID, - SAMPLE_EVENT_ID, its_groups, - vsomeip::event_type_e::ET_EVENT); - application->subscribe(RESPONSE_SAMPLE_SERVICE_ID, SAMPLE_INSTANCE_ID, - SAMPLE_EVENTGROUP_ID); - - std::set groups{SAMPLE_EVENTGROUP_ID}; - application->offer_event(SAMPLE_SERVICE_ID, SAMPLE_INSTANCE_ID, SAMPLE_EVENT_ID, - groups); - application->offer_service(SAMPLE_SERVICE_ID, SAMPLE_INSTANCE_ID); - - // application->update_service_configuration( - // SAMPLE_SERVICE_ID, SAMPLE_INSTANCE_ID, 12345u, true, true, true); - auto payload = vsomeip::runtime::get()->create_payload(); - - std::cout << "SOME/IP daemon started, waiting for messages..." << std::endl; - - // Process messages until shutdown is requested - while (!shutdown_requested.load()) { - // TODO: Use ReceiveHandler + async runtime instead of polling - proxy.message_.GetNewSamples( - [&](auto message_sample) { - // TODO: Check if size is larger than capacity of data - score::cpp::span message(message_sample->data, - message_sample->size); - - // Check if sample size is valid and contains at least a SOME/IP header - if (message.size() < VSOMEIP_FULL_HEADER_SIZE) { - std::cerr << "Received too small sample (size: " << message.size() - << ", expected at least: " << VSOMEIP_FULL_HEADER_SIZE - << "). Skipping message." << std::endl; - return; - } - - // TODO: Here we need to find a better way how to pass the message to - // vsomeip. There doesn't seem to be a public way to just wrap the existing - // buffer. - auto payload_data = message.subspan(VSOMEIP_FULL_HEADER_SIZE); - payload->set_data( - reinterpret_cast(payload_data.data()), - payload_data.size()); - application->notify(SAMPLE_SERVICE_ID, SAMPLE_INSTANCE_ID, SAMPLE_EVENT_ID, - payload); - }, - max_sample_count); - std::this_thread::sleep_for(std::chrono::milliseconds(100)); - } + // Create adapters — swap these to use different SOME/IP stacks or IPC frameworks. + auto network_stack = std::make_unique("someipd"); + if (!network_stack->Init()) { + score::mw::log::LogError() << "Network stack initialization failed"; + return EXIT_FAILURE; + } - std::cout << "Shutting down SOME/IP daemon..." << std::endl; - } + auto internal_ipc = + std::make_unique("someipd/gatewayd_messages", "someipd/someipd_messages", 10); + internal_ipc->Init(argc, argv); - application->stop(); - }).detach(); + GatewayRouting routing(std::move(network_stack), std::move(internal_ipc), std::move(config)); + routing.Run(shutdown_requested); - application->start(); + return EXIT_SUCCESS; } diff --git a/src/someipd/mwcom_adapter.cpp b/src/someipd/mwcom_adapter.cpp new file mode 100644 index 00000000..86ce069d --- /dev/null +++ b/src/someipd/mwcom_adapter.cpp @@ -0,0 +1,66 @@ +/******************************************************************************** + * 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 + ********************************************************************************/ + +#include "src/someipd/mwcom_adapter.h" + +#include +#include + +#include "score/mw/log/logging.h" + +namespace score::someip_gateway::someipd { + +using score::someip_gateway::network_service::interfaces::message_transfer::MAX_MESSAGE_SIZE; + +MwcomAdapter::MwcomAdapter(std::string proxy_instance_specifier, + std::string skeleton_instance_specifier, std::size_t max_sample_count) + : proxy_instance_specifier_(std::move(proxy_instance_specifier)), + skeleton_instance_specifier_(std::move(skeleton_instance_specifier)), + max_sample_count_(max_sample_count) {} + +void MwcomAdapter::Init(int argc, const char* argv[]) { + score::mw::com::runtime::InitializeRuntime(argc, argv); + + auto handles = Proxy::FindService( + score::mw::com::InstanceSpecifier::Create(proxy_instance_specifier_).value()) + .value(); + proxy_.emplace(Proxy::Create(handles.front()).value()); + proxy_->message_.Subscribe(max_sample_count_); + + auto create_result = Skeleton::Create( + score::mw::com::InstanceSpecifier::Create(skeleton_instance_specifier_).value()); + skeleton_.emplace(std::move(create_result).value()); + (void)skeleton_->OfferService(); +} + +bool MwcomAdapter::SendToGatewayd(const std::byte* data, std::size_t size) { + auto maybe_message = skeleton_->message_.Allocate(); + if (!maybe_message.has_value()) { + score::mw::log::LogError() + << "Failed to allocate SOME/IP message: " << maybe_message.error().Message(); + return false; + } + auto message_sample = std::move(maybe_message).value(); + const auto copy_size = std::min(size, MAX_MESSAGE_SIZE); + std::memcpy(message_sample->data, data, copy_size); + message_sample->size = size; + skeleton_->message_.Send(std::move(message_sample)); + return true; +} + +void MwcomAdapter::ReceiveFromGatewayd(MessageCallback callback, std::size_t max_count) { + proxy_->message_.GetNewSamples( + [&callback](auto sample) { callback(sample->data, sample->size); }, max_count); +} + +} // namespace score::someip_gateway::someipd diff --git a/src/someipd/mwcom_adapter.h b/src/someipd/mwcom_adapter.h new file mode 100644 index 00000000..d3ae9085 --- /dev/null +++ b/src/someipd/mwcom_adapter.h @@ -0,0 +1,58 @@ +/******************************************************************************** + * 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 + ********************************************************************************/ + +#pragma once + +#include +#include +#include + +#include "score/mw/com/runtime.h" +#include "src/network_service/interfaces/message_transfer.h" +#include "src/someipd/i_internal_ipc.h" + +namespace score::someip_gateway::someipd { + +/// Adapter that implements IInternalIpc using the mw::com framework. +/// +/// All mw::com-specific headers and API calls are isolated here. +/// To switch to a different IPC framework, provide another IInternalIpc adapter. +/// +/// +/// Target (IInternalIpc) +/// Adapter (MwcomAdapter) +/// Adaptee (SomeipMessageTransferProxy & SomeipMessageTransferSkeleton) +/// Client main.cpp +class MwcomAdapter : public IInternalIpc { + public: + MwcomAdapter(std::string proxy_instance_specifier, std::string skeleton_instance_specifier, + std::size_t max_sample_count); + + void Init(int argc, const char* argv[]) override; + bool SendToGatewayd(const std::byte* data, std::size_t size) override; + void ReceiveFromGatewayd(MessageCallback callback, std::size_t max_count) override; + + private: + using Proxy = score::someip_gateway::network_service::interfaces::message_transfer:: + SomeipMessageTransferProxy; + using Skeleton = score::someip_gateway::network_service::interfaces::message_transfer:: + SomeipMessageTransferSkeleton; + + std::string proxy_instance_specifier_; + std::string skeleton_instance_specifier_; + std::size_t max_sample_count_; + std::optional proxy_; + std::optional skeleton_; +}; + +} // namespace score::someip_gateway::someipd diff --git a/src/someipd/someipd_config.h b/src/someipd/someipd_config.h new file mode 100644 index 00000000..a52180d4 --- /dev/null +++ b/src/someipd/someipd_config.h @@ -0,0 +1,48 @@ +/******************************************************************************** + * 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 + ********************************************************************************/ + +#pragma once + +#include +#include +#include + +#include "src/someipd/someipd_types.h" + +namespace score::someip_gateway::someipd { + +/// A single event/eventgroup pair within a SOME/IP service. +struct ServiceEventConfig { + EventId event_id; + EventGroupId eventgroup_id; +}; + +/// Transport-level configuration for one SOME/IP service instance. +struct ServiceConfig { + ServiceId service_id; + InstanceId instance_id; + std::uint16_t unreliable_port{0}; + std::vector events; +}; + +/// Full someipd service configuration. +struct SomeipDConfig { + /// Services that someipd offers to the SOME/IP network (outbound: IPC → network). + std::vector offered_services; + /// Services that someipd subscribes to from the SOME/IP network (inbound: network → IPC). + std::vector subscribed_services; +}; + +SomeipDConfig ReadSomeipDConfig(const std::string& config_path); + +} // namespace score::someip_gateway::someipd diff --git a/src/someipd/someipd_config_reader.cpp b/src/someipd/someipd_config_reader.cpp new file mode 100644 index 00000000..c4070236 --- /dev/null +++ b/src/someipd/someipd_config_reader.cpp @@ -0,0 +1,68 @@ +/******************************************************************************** + * 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 + ********************************************************************************/ + +#include +#include +#include + +#include "src/someipd/someipd_config.h" + +namespace score::someip_gateway::someipd { + +namespace { + +std::uint16_t ParseHex16(const std::string& value) { + return static_cast(std::stoul(value, nullptr, 16)); +} + +ServiceEventConfig ParseEvent(const nlohmann::json& obj) { + return ServiceEventConfig{ + ParseHex16(obj.at("event_id").get()), + ParseHex16(obj.at("eventgroup_id").get()), + }; +} + +ServiceConfig ParseService(const nlohmann::json& obj) { + ServiceConfig svc{}; + svc.service_id = ParseHex16(obj.at("service_id").get()); + svc.instance_id = ParseHex16(obj.at("instance_id").get()); + if (obj.contains("unreliable_port")) { + svc.unreliable_port = obj.at("unreliable_port").get(); + } + for (const auto& ev : obj.at("events")) { + svc.events.push_back(ParseEvent(ev)); + } + return svc; +} + +} // namespace + +SomeipDConfig ReadSomeipDConfig(const std::string& config_path) { + std::ifstream file(config_path); + if (!file.is_open()) { + return {}; + } + + const nlohmann::json root = nlohmann::json::parse(file); + + SomeipDConfig config{}; + for (const auto& svc : root.at("offered_services")) { + config.offered_services.push_back(ParseService(svc)); + } + for (const auto& svc : root.at("subscribed_services")) { + config.subscribed_services.push_back(ParseService(svc)); + } + return config; +} + +} // namespace score::someip_gateway::someipd diff --git a/src/someipd/someipd_types.h b/src/someipd/someipd_types.h new file mode 100644 index 00000000..06a2b155 --- /dev/null +++ b/src/someipd/someipd_types.h @@ -0,0 +1,32 @@ +/******************************************************************************** + * 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 + ********************************************************************************/ + +#pragma once + +#include +#include + +namespace score::someip_gateway::someipd { + +/// Hopefully ;) Stack-independent SOME/IP type aliases. +using ServiceId = std::uint16_t; +using InstanceId = std::uint16_t; +using EventId = std::uint16_t; +using EventGroupId = std::uint16_t; + +/// Hopefully ;) SOME/IP protocol constants (per SOME/IP specification, stack-independent). +constexpr std::size_t kSomeipFullHeaderSize = 16; +constexpr std::size_t kMaxMessageSize = 1500; +constexpr InstanceId kAnyInstance = 0xFFFF; + +} // namespace score::someip_gateway::someipd diff --git a/src/someipd/vsomeip_adapter.cpp b/src/someipd/vsomeip_adapter.cpp new file mode 100644 index 00000000..8b447c22 --- /dev/null +++ b/src/someipd/vsomeip_adapter.cpp @@ -0,0 +1,87 @@ +/******************************************************************************** + * 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 + ********************************************************************************/ + +#include "src/someipd/vsomeip_adapter.h" + +#include +#include + +namespace score::someip_gateway::someipd { + +VsomeipAdapter::VsomeipAdapter(std::string app_name) : app_name_(std::move(app_name)) {} + +VsomeipAdapter::~VsomeipAdapter() { StopProcessing(); } + +bool VsomeipAdapter::Init() { + auto runtime = vsomeip::runtime::get(); + application_ = runtime->create_application(app_name_); + if (!application_->init()) { + return false; + } + payload_ = runtime->create_payload(); + return true; +} + +void VsomeipAdapter::StartProcessing() { + processing_thread_ = std::thread([this]() { application_->start(); }); +} + +void VsomeipAdapter::StopProcessing() { + if (application_) { + application_->stop(); + } + if (processing_thread_.joinable()) { + processing_thread_.join(); + } +} + +void VsomeipAdapter::OfferEvent(ServiceId service_id, InstanceId instance_id, EventId event_id, + EventGroupId eventgroup_id) { + std::set groups{eventgroup_id}; + application_->offer_event(service_id, instance_id, event_id, groups); +} + +void VsomeipAdapter::OfferService(ServiceId service_id, InstanceId instance_id) { + application_->offer_service(service_id, instance_id); +} + +void VsomeipAdapter::Notify(ServiceId service_id, InstanceId instance_id, EventId event_id, + const std::byte* payload_data, std::size_t payload_size) { + payload_->set_data(reinterpret_cast(payload_data), + static_cast(payload_size)); + application_->notify(service_id, instance_id, event_id, payload_); +} + +void VsomeipAdapter::RequestService(ServiceId service_id, InstanceId instance_id) { + application_->request_service(service_id, instance_id); +} + +void VsomeipAdapter::SubscribeEvent(ServiceId service_id, InstanceId instance_id, EventId event_id, + EventGroupId eventgroup_id) { + std::set groups{eventgroup_id}; + application_->request_event(service_id, instance_id, event_id, groups, + vsomeip::event_type_e::ET_EVENT); + application_->subscribe(service_id, instance_id, eventgroup_id); +} + +void VsomeipAdapter::RegisterMessageHandler(ServiceId service_id, InstanceId instance_id, + EventId event_id, MessageHandler handler) { + application_->register_message_handler( + service_id, instance_id, event_id, [handler](const std::shared_ptr& msg) { + handler(msg->get_service(), msg->get_instance(), msg->get_method(), + reinterpret_cast(msg->get_payload()->get_data()), + msg->get_payload()->get_length()); + }); +} + +} // namespace score::someip_gateway::someipd diff --git a/src/someipd/vsomeip_adapter.h b/src/someipd/vsomeip_adapter.h new file mode 100644 index 00000000..a50061e9 --- /dev/null +++ b/src/someipd/vsomeip_adapter.h @@ -0,0 +1,66 @@ +/******************************************************************************** + * 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 + ********************************************************************************/ + +#pragma once + +#include +#include +#include +#include + +#include "src/someipd/i_network_stack.h" + +namespace score::someip_gateway::someipd { + +/// Adapter that implements INetworkStack using the vsomeip library. +/// +/// All vsomeip-specific headers and API calls are isolated here. +/// To switch to a different SOME/IP stack, provide another INetworkStack adapter. +/// +/// Target (INetworkStack) +/// Adapter (VsomeipAdapter) +/// Adaptee (vsomeip::application & vsomeip::payload) +/// Client main.cpp , it instantiate the adapter + +class VsomeipAdapter : public INetworkStack { + public: + explicit VsomeipAdapter(std::string app_name); + ~VsomeipAdapter() override; + + VsomeipAdapter(const VsomeipAdapter&) = delete; + VsomeipAdapter& operator=(const VsomeipAdapter&) = delete; + + bool Init() override; + void StartProcessing() override; + void StopProcessing() override; + + void OfferEvent(ServiceId service_id, InstanceId instance_id, EventId event_id, + EventGroupId eventgroup_id) override; + void OfferService(ServiceId service_id, InstanceId instance_id) override; + void Notify(ServiceId service_id, InstanceId instance_id, EventId event_id, + const std::byte* payload_data, std::size_t payload_size) override; + + void RequestService(ServiceId service_id, InstanceId instance_id) override; + void SubscribeEvent(ServiceId service_id, InstanceId instance_id, EventId event_id, + EventGroupId eventgroup_id) override; + void RegisterMessageHandler(ServiceId service_id, InstanceId instance_id, EventId event_id, + MessageHandler handler) override; + + private: + std::string app_name_; + std::shared_ptr application_; + std::shared_ptr payload_; + std::thread processing_thread_; +}; + +} // namespace score::someip_gateway::someipd